Roo/bootstrap/Menu.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');
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');
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.footer.dom.style.display = 'none';
3328             return width;
3329         }
3330         dlg.footerEl.dom.style.display = '';
3331         for(var k in buttons){
3332             if(typeof buttons[k] != "function"){
3333                 if(b[k]){
3334                     buttons[k].show();
3335                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3336                     width += buttons[k].el.getWidth()+15;
3337                 }else{
3338                     buttons[k].hide();
3339                 }
3340             }
3341         }
3342         return width;
3343     };
3344
3345     // private
3346     var handleEsc = function(d, k, e){
3347         if(opt && opt.closable !== false){
3348             dlg.hide();
3349         }
3350         if(e){
3351             e.stopEvent();
3352         }
3353     };
3354
3355     return {
3356         /**
3357          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3358          * @return {Roo.BasicDialog} The BasicDialog element
3359          */
3360         getDialog : function(){
3361            if(!dlg){
3362                 dlg = new Roo.bootstrap.Modal( {
3363                     //draggable: true,
3364                     //resizable:false,
3365                     //constraintoviewport:false,
3366                     //fixedcenter:true,
3367                     //collapsible : false,
3368                     //shim:true,
3369                     //modal: true,
3370                 //    width: 'auto',
3371                   //  height:100,
3372                     //buttonAlign:"center",
3373                     closeClick : function(){
3374                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3375                             handleButton("no");
3376                         }else{
3377                             handleButton("cancel");
3378                         }
3379                     }
3380                 });
3381                 dlg.render();
3382                 dlg.on("hide", handleHide);
3383                 mask = dlg.mask;
3384                 //dlg.addKeyListener(27, handleEsc);
3385                 buttons = {};
3386                 this.buttons = buttons;
3387                 var bt = this.buttonText;
3388                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3389                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3390                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3391                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3392                 //Roo.log(buttons);
3393                 bodyEl = dlg.bodyEl.createChild({
3394
3395                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3396                         '<textarea class="roo-mb-textarea"></textarea>' +
3397                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3398                 });
3399                 msgEl = bodyEl.dom.firstChild;
3400                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3401                 textboxEl.enableDisplayMode();
3402                 textboxEl.addKeyListener([10,13], function(){
3403                     if(dlg.isVisible() && opt && opt.buttons){
3404                         if(opt.buttons.ok){
3405                             handleButton("ok");
3406                         }else if(opt.buttons.yes){
3407                             handleButton("yes");
3408                         }
3409                     }
3410                 });
3411                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3412                 textareaEl.enableDisplayMode();
3413                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3414                 progressEl.enableDisplayMode();
3415                 
3416                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3417                 var pf = progressEl.dom.firstChild;
3418                 if (pf) {
3419                     pp = Roo.get(pf.firstChild);
3420                     pp.setHeight(pf.offsetHeight);
3421                 }
3422                 
3423             }
3424             return dlg;
3425         },
3426
3427         /**
3428          * Updates the message box body text
3429          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3430          * the XHTML-compliant non-breaking space character '&amp;#160;')
3431          * @return {Roo.MessageBox} This message box
3432          */
3433         updateText : function(text)
3434         {
3435             if(!dlg.isVisible() && !opt.width){
3436                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3437                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3438             }
3439             msgEl.innerHTML = text || '&#160;';
3440       
3441             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3442             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3443             var w = Math.max(
3444                     Math.min(opt.width || cw , this.maxWidth), 
3445                     Math.max(opt.minWidth || this.minWidth, bwidth)
3446             );
3447             if(opt.prompt){
3448                 activeTextEl.setWidth(w);
3449             }
3450             if(dlg.isVisible()){
3451                 dlg.fixedcenter = false;
3452             }
3453             // to big, make it scroll. = But as usual stupid IE does not support
3454             // !important..
3455             
3456             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3457                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3458                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3459             } else {
3460                 bodyEl.dom.style.height = '';
3461                 bodyEl.dom.style.overflowY = '';
3462             }
3463             if (cw > w) {
3464                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3465             } else {
3466                 bodyEl.dom.style.overflowX = '';
3467             }
3468             
3469             dlg.setContentSize(w, bodyEl.getHeight());
3470             if(dlg.isVisible()){
3471                 dlg.fixedcenter = true;
3472             }
3473             return this;
3474         },
3475
3476         /**
3477          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3478          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3479          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3480          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3481          * @return {Roo.MessageBox} This message box
3482          */
3483         updateProgress : function(value, text){
3484             if(text){
3485                 this.updateText(text);
3486             }
3487             
3488             if (pp) { // weird bug on my firefox - for some reason this is not defined
3489                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3490                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3491             }
3492             return this;
3493         },        
3494
3495         /**
3496          * Returns true if the message box is currently displayed
3497          * @return {Boolean} True if the message box is visible, else false
3498          */
3499         isVisible : function(){
3500             return dlg && dlg.isVisible();  
3501         },
3502
3503         /**
3504          * Hides the message box if it is displayed
3505          */
3506         hide : function(){
3507             if(this.isVisible()){
3508                 dlg.hide();
3509             }  
3510         },
3511
3512         /**
3513          * Displays a new message box, or reinitializes an existing message box, based on the config options
3514          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3515          * The following config object properties are supported:
3516          * <pre>
3517 Property    Type             Description
3518 ----------  ---------------  ------------------------------------------------------------------------------------
3519 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3520                                    closes (defaults to undefined)
3521 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3522                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3523 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3524                                    progress and wait dialogs will ignore this property and always hide the
3525                                    close button as they can only be closed programmatically.
3526 cls               String           A custom CSS class to apply to the message box element
3527 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3528                                    displayed (defaults to 75)
3529 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3530                                    function will be btn (the name of the button that was clicked, if applicable,
3531                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3532                                    Progress and wait dialogs will ignore this option since they do not respond to
3533                                    user actions and can only be closed programmatically, so any required function
3534                                    should be called by the same code after it closes the dialog.
3535 icon              String           A CSS class that provides a background image to be used as an icon for
3536                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3537 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3538 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3539 modal             Boolean          False to allow user interaction with the page while the message box is
3540                                    displayed (defaults to true)
3541 msg               String           A string that will replace the existing message box body text (defaults
3542                                    to the XHTML-compliant non-breaking space character '&#160;')
3543 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3544 progress          Boolean          True to display a progress bar (defaults to false)
3545 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3546 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3547 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3548 title             String           The title text
3549 value             String           The string value to set into the active textbox element if displayed
3550 wait              Boolean          True to display a progress bar (defaults to false)
3551 width             Number           The width of the dialog in pixels
3552 </pre>
3553          *
3554          * Example usage:
3555          * <pre><code>
3556 Roo.Msg.show({
3557    title: 'Address',
3558    msg: 'Please enter your address:',
3559    width: 300,
3560    buttons: Roo.MessageBox.OKCANCEL,
3561    multiline: true,
3562    fn: saveAddress,
3563    animEl: 'addAddressBtn'
3564 });
3565 </code></pre>
3566          * @param {Object} config Configuration options
3567          * @return {Roo.MessageBox} This message box
3568          */
3569         show : function(options)
3570         {
3571             
3572             // this causes nightmares if you show one dialog after another
3573             // especially on callbacks..
3574              
3575             if(this.isVisible()){
3576                 
3577                 this.hide();
3578                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3579                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3580                 Roo.log("New Dialog Message:" +  options.msg )
3581                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3582                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3583                 
3584             }
3585             var d = this.getDialog();
3586             opt = options;
3587             d.setTitle(opt.title || "&#160;");
3588             d.closeEl.setDisplayed(opt.closable !== false);
3589             activeTextEl = textboxEl;
3590             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3591             if(opt.prompt){
3592                 if(opt.multiline){
3593                     textboxEl.hide();
3594                     textareaEl.show();
3595                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3596                         opt.multiline : this.defaultTextHeight);
3597                     activeTextEl = textareaEl;
3598                 }else{
3599                     textboxEl.show();
3600                     textareaEl.hide();
3601                 }
3602             }else{
3603                 textboxEl.hide();
3604                 textareaEl.hide();
3605             }
3606             progressEl.setDisplayed(opt.progress === true);
3607             this.updateProgress(0);
3608             activeTextEl.dom.value = opt.value || "";
3609             if(opt.prompt){
3610                 dlg.setDefaultButton(activeTextEl);
3611             }else{
3612                 var bs = opt.buttons;
3613                 var db = null;
3614                 if(bs && bs.ok){
3615                     db = buttons["ok"];
3616                 }else if(bs && bs.yes){
3617                     db = buttons["yes"];
3618                 }
3619                 dlg.setDefaultButton(db);
3620             }
3621             bwidth = updateButtons(opt.buttons);
3622             this.updateText(opt.msg);
3623             if(opt.cls){
3624                 d.el.addClass(opt.cls);
3625             }
3626             d.proxyDrag = opt.proxyDrag === true;
3627             d.modal = opt.modal !== false;
3628             d.mask = opt.modal !== false ? mask : false;
3629             if(!d.isVisible()){
3630                 // force it to the end of the z-index stack so it gets a cursor in FF
3631                 document.body.appendChild(dlg.el.dom);
3632                 d.animateTarget = null;
3633                 d.show(options.animEl);
3634             }
3635             return this;
3636         },
3637
3638         /**
3639          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3640          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3641          * and closing the message box when the process is complete.
3642          * @param {String} title The title bar text
3643          * @param {String} msg The message box body text
3644          * @return {Roo.MessageBox} This message box
3645          */
3646         progress : function(title, msg){
3647             this.show({
3648                 title : title,
3649                 msg : msg,
3650                 buttons: false,
3651                 progress:true,
3652                 closable:false,
3653                 minWidth: this.minProgressWidth,
3654                 modal : true
3655             });
3656             return this;
3657         },
3658
3659         /**
3660          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3661          * If a callback function is passed it will be called after the user clicks the button, and the
3662          * id of the button that was clicked will be passed as the only parameter to the callback
3663          * (could also be the top-right close button).
3664          * @param {String} title The title bar text
3665          * @param {String} msg The message box body text
3666          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3667          * @param {Object} scope (optional) The scope of the callback function
3668          * @return {Roo.MessageBox} This message box
3669          */
3670         alert : function(title, msg, fn, scope)
3671         {
3672             this.show({
3673                 title : title,
3674                 msg : msg,
3675                 buttons: this.OK,
3676                 fn: fn,
3677                 closable : false,
3678                 scope : scope,
3679                 modal : true
3680             });
3681             return this;
3682         },
3683
3684         /**
3685          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3686          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3687          * You are responsible for closing the message box when the process is complete.
3688          * @param {String} msg The message box body text
3689          * @param {String} title (optional) The title bar text
3690          * @return {Roo.MessageBox} This message box
3691          */
3692         wait : function(msg, title){
3693             this.show({
3694                 title : title,
3695                 msg : msg,
3696                 buttons: false,
3697                 closable:false,
3698                 progress:true,
3699                 modal:true,
3700                 width:300,
3701                 wait:true
3702             });
3703             waitTimer = Roo.TaskMgr.start({
3704                 run: function(i){
3705                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3706                 },
3707                 interval: 1000
3708             });
3709             return this;
3710         },
3711
3712         /**
3713          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3714          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3715          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3716          * @param {String} title The title bar text
3717          * @param {String} msg The message box body text
3718          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3719          * @param {Object} scope (optional) The scope of the callback function
3720          * @return {Roo.MessageBox} This message box
3721          */
3722         confirm : function(title, msg, fn, scope){
3723             this.show({
3724                 title : title,
3725                 msg : msg,
3726                 buttons: this.YESNO,
3727                 fn: fn,
3728                 scope : scope,
3729                 modal : true
3730             });
3731             return this;
3732         },
3733
3734         /**
3735          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3736          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3737          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3738          * (could also be the top-right close button) and the text that was entered will be passed as the two
3739          * parameters to the callback.
3740          * @param {String} title The title bar text
3741          * @param {String} msg The message box body text
3742          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3743          * @param {Object} scope (optional) The scope of the callback function
3744          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3745          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3746          * @return {Roo.MessageBox} This message box
3747          */
3748         prompt : function(title, msg, fn, scope, multiline){
3749             this.show({
3750                 title : title,
3751                 msg : msg,
3752                 buttons: this.OKCANCEL,
3753                 fn: fn,
3754                 minWidth:250,
3755                 scope : scope,
3756                 prompt:true,
3757                 multiline: multiline,
3758                 modal : true
3759             });
3760             return this;
3761         },
3762
3763         /**
3764          * Button config that displays a single OK button
3765          * @type Object
3766          */
3767         OK : {ok:true},
3768         /**
3769          * Button config that displays Yes and No buttons
3770          * @type Object
3771          */
3772         YESNO : {yes:true, no:true},
3773         /**
3774          * Button config that displays OK and Cancel buttons
3775          * @type Object
3776          */
3777         OKCANCEL : {ok:true, cancel:true},
3778         /**
3779          * Button config that displays Yes, No and Cancel buttons
3780          * @type Object
3781          */
3782         YESNOCANCEL : {yes:true, no:true, cancel:true},
3783
3784         /**
3785          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3786          * @type Number
3787          */
3788         defaultTextHeight : 75,
3789         /**
3790          * The maximum width in pixels of the message box (defaults to 600)
3791          * @type Number
3792          */
3793         maxWidth : 600,
3794         /**
3795          * The minimum width in pixels of the message box (defaults to 100)
3796          * @type Number
3797          */
3798         minWidth : 100,
3799         /**
3800          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3801          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3802          * @type Number
3803          */
3804         minProgressWidth : 250,
3805         /**
3806          * An object containing the default button text strings that can be overriden for localized language support.
3807          * Supported properties are: ok, cancel, yes and no.
3808          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3809          * @type Object
3810          */
3811         buttonText : {
3812             ok : "OK",
3813             cancel : "Cancel",
3814             yes : "Yes",
3815             no : "No"
3816         }
3817     };
3818 }();
3819
3820 /**
3821  * Shorthand for {@link Roo.MessageBox}
3822  */
3823 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3824 Roo.Msg = Roo.Msg || Roo.MessageBox;
3825 /*
3826  * - LGPL
3827  *
3828  * navbar
3829  * 
3830  */
3831
3832 /**
3833  * @class Roo.bootstrap.Navbar
3834  * @extends Roo.bootstrap.Component
3835  * Bootstrap Navbar class
3836
3837  * @constructor
3838  * Create a new Navbar
3839  * @param {Object} config The config object
3840  */
3841
3842
3843 Roo.bootstrap.Navbar = function(config){
3844     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3845     this.addEvents({
3846         // raw events
3847         /**
3848          * @event beforetoggle
3849          * Fire before toggle the menu
3850          * @param {Roo.EventObject} e
3851          */
3852         "beforetoggle" : true
3853     });
3854 };
3855
3856 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3857     
3858     
3859    
3860     // private
3861     navItems : false,
3862     loadMask : false,
3863     
3864     
3865     getAutoCreate : function(){
3866         
3867         
3868         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3869         
3870     },
3871     
3872     initEvents :function ()
3873     {
3874         //Roo.log(this.el.select('.navbar-toggle',true));
3875         this.el.select('.navbar-toggle',true).on('click', function() {
3876             if(this.fireEvent('beforetoggle', this) !== false){
3877                 var ce = this.el.select('.navbar-collapse',true).first();
3878                 ce.toggleClass('in'); // old...
3879                 if (ce.hasClass('collapse')) {
3880                     // show it...
3881                     ce.removeClass('collapse');
3882                     ce.addClass('show');
3883                     var h = ce.getHeight();
3884                     Roo.log(h);
3885                     ce.removeClass('show');
3886                     // at this point we should be able to see it..
3887                     ce.addClass('collapsing');
3888                     
3889                     ce.setHeight(0); // resize it ...
3890                     ce.on('transitionend', function() {
3891                         Roo.log('done transition');
3892                         ce.removeClass('collapsing');
3893                         ce.addClass('show');
3894                         ce.removeClass('collapse');
3895
3896                         ce.dom.style.height = '';
3897                     }, this, { single: true} );
3898                     ce.setHeight(h);
3899                     
3900                 } else {
3901                     ce.setHeight(ce.getHeight());
3902                     ce.removeClass('show');
3903                     ce.addClass('collapsing');
3904                     
3905                     ce.on('transitionend', function() {
3906                         ce.dom.style.height = '';
3907                         ce.removeClass('collapsing');
3908                         ce.addClass('collapse');
3909                     }, this, { single: true} );
3910                     ce.setHeight(0);
3911                 }
3912             }
3913             
3914         }, this);
3915         
3916         var mark = {
3917             tag: "div",
3918             cls:"x-dlg-mask"
3919         };
3920         
3921         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3922         
3923         var size = this.el.getSize();
3924         this.maskEl.setSize(size.width, size.height);
3925         this.maskEl.enableDisplayMode("block");
3926         this.maskEl.hide();
3927         
3928         if(this.loadMask){
3929             this.maskEl.show();
3930         }
3931     },
3932     
3933     
3934     getChildContainer : function()
3935     {
3936         if (this.el.select('.collapse').getCount()) {
3937             return this.el.select('.collapse',true).first();
3938         }
3939         
3940         return this.el;
3941     },
3942     
3943     mask : function()
3944     {
3945         this.maskEl.show();
3946     },
3947     
3948     unmask : function()
3949     {
3950         this.maskEl.hide();
3951     } 
3952     
3953     
3954     
3955     
3956 });
3957
3958
3959
3960  
3961
3962  /*
3963  * - LGPL
3964  *
3965  * navbar
3966  * 
3967  */
3968
3969 /**
3970  * @class Roo.bootstrap.NavSimplebar
3971  * @extends Roo.bootstrap.Navbar
3972  * Bootstrap Sidebar class
3973  *
3974  * @cfg {Boolean} inverse is inverted color
3975  * 
3976  * @cfg {String} type (nav | pills | tabs)
3977  * @cfg {Boolean} arrangement stacked | justified
3978  * @cfg {String} align (left | right) alignment
3979  * 
3980  * @cfg {Boolean} main (true|false) main nav bar? default false
3981  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3982  * 
3983  * @cfg {String} tag (header|footer|nav|div) default is nav 
3984
3985  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3986  * 
3987  * 
3988  * @constructor
3989  * Create a new Sidebar
3990  * @param {Object} config The config object
3991  */
3992
3993
3994 Roo.bootstrap.NavSimplebar = function(config){
3995     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3996 };
3997
3998 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3999     
4000     inverse: false,
4001     
4002     type: false,
4003     arrangement: '',
4004     align : false,
4005     
4006     weight : 'light',
4007     
4008     main : false,
4009     
4010     
4011     tag : false,
4012     
4013     
4014     getAutoCreate : function(){
4015         
4016         
4017         var cfg = {
4018             tag : this.tag || 'div',
4019             cls : 'navbar navbar-expand-lg'
4020         };
4021         if (['light','white'].indexOf(this.weight) > -1) {
4022             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4023         }
4024         cfg.cls += ' bg-' + this.weight;
4025         
4026         if (this.inverse) {
4027             cfg.cls += ' navbar-inverse';
4028             
4029         }
4030         
4031         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4032         
4033         if (Roo.bootstrap.version == 4) {
4034             return cfg;
4035         }
4036         
4037         cfg.cn = [
4038             {
4039                 cls: 'nav',
4040                 tag : 'ul'
4041             }
4042         ];
4043         
4044          
4045         this.type = this.type || 'nav';
4046         if (['tabs','pills'].indexOf(this.type)!==-1) {
4047             cfg.cn[0].cls += ' nav-' + this.type
4048         
4049         
4050         } else {
4051             if (this.type!=='nav') {
4052                 Roo.log('nav type must be nav/tabs/pills')
4053             }
4054             cfg.cn[0].cls += ' navbar-nav'
4055         }
4056         
4057         
4058         
4059         
4060         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
4061             cfg.cn[0].cls += ' nav-' + this.arrangement;
4062         }
4063         
4064         
4065         if (this.align === 'right') {
4066             cfg.cn[0].cls += ' navbar-right';
4067         }
4068         
4069         
4070         
4071         
4072         return cfg;
4073     
4074         
4075     }
4076     
4077     
4078     
4079 });
4080
4081
4082
4083  
4084
4085  
4086        /*
4087  * - LGPL
4088  *
4089  * navbar
4090  * navbar-fixed-top
4091  * navbar-expand-md  fixed-top 
4092  */
4093
4094 /**
4095  * @class Roo.bootstrap.NavHeaderbar
4096  * @extends Roo.bootstrap.NavSimplebar
4097  * Bootstrap Sidebar class
4098  *
4099  * @cfg {String} brand what is brand
4100  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4101  * @cfg {String} brand_href href of the brand
4102  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4103  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4104  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4105  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4106  * 
4107  * @constructor
4108  * Create a new Sidebar
4109  * @param {Object} config The config object
4110  */
4111
4112
4113 Roo.bootstrap.NavHeaderbar = function(config){
4114     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4115       
4116 };
4117
4118 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4119     
4120     position: '',
4121     brand: '',
4122     brand_href: false,
4123     srButton : true,
4124     autohide : false,
4125     desktopCenter : false,
4126    
4127     
4128     getAutoCreate : function(){
4129         
4130         var   cfg = {
4131             tag: this.nav || 'nav',
4132             cls: 'navbar navbar-expand-md',
4133             role: 'navigation',
4134             cn: []
4135         };
4136         
4137         var cn = cfg.cn;
4138         if (this.desktopCenter) {
4139             cn.push({cls : 'container', cn : []});
4140             cn = cn[0].cn;
4141         }
4142         
4143         if(this.srButton){
4144             var btn = {
4145                 tag: 'button',
4146                 type: 'button',
4147                 cls: 'navbar-toggle navbar-toggler',
4148                 'data-toggle': 'collapse',
4149                 cn: [
4150                     {
4151                         tag: 'span',
4152                         cls: 'sr-only',
4153                         html: 'Toggle navigation'
4154                     },
4155                     {
4156                         tag: 'span',
4157                         cls: 'icon-bar navbar-toggler-icon'
4158                     },
4159                     {
4160                         tag: 'span',
4161                         cls: 'icon-bar'
4162                     },
4163                     {
4164                         tag: 'span',
4165                         cls: 'icon-bar'
4166                     }
4167                 ]
4168             };
4169             
4170             cn.push( Roo.bootstrap.version == 4 ? btn : {
4171                 tag: 'div',
4172                 cls: 'navbar-header',
4173                 cn: [
4174                     btn
4175                 ]
4176             });
4177         }
4178         
4179         cn.push({
4180             tag: 'div',
4181             cls: 'collapse navbar-collapse',
4182             cn : []
4183         });
4184         
4185         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4186         
4187         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4188             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4189             
4190             // tag can override this..
4191             
4192             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4193         }
4194         
4195         if (this.brand !== '') {
4196             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4197             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4198                 tag: 'a',
4199                 href: this.brand_href ? this.brand_href : '#',
4200                 cls: 'navbar-brand',
4201                 cn: [
4202                 this.brand
4203                 ]
4204             });
4205         }
4206         
4207         if(this.main){
4208             cfg.cls += ' main-nav';
4209         }
4210         
4211         
4212         return cfg;
4213
4214         
4215     },
4216     getHeaderChildContainer : function()
4217     {
4218         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4219             return this.el.select('.navbar-header',true).first();
4220         }
4221         
4222         return this.getChildContainer();
4223     },
4224     
4225     
4226     initEvents : function()
4227     {
4228         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4229         
4230         if (this.autohide) {
4231             
4232             var prevScroll = 0;
4233             var ft = this.el;
4234             
4235             Roo.get(document).on('scroll',function(e) {
4236                 var ns = Roo.get(document).getScroll().top;
4237                 var os = prevScroll;
4238                 prevScroll = ns;
4239                 
4240                 if(ns > os){
4241                     ft.removeClass('slideDown');
4242                     ft.addClass('slideUp');
4243                     return;
4244                 }
4245                 ft.removeClass('slideUp');
4246                 ft.addClass('slideDown');
4247                  
4248               
4249           },this);
4250         }
4251     }    
4252     
4253 });
4254
4255
4256
4257  
4258
4259  /*
4260  * - LGPL
4261  *
4262  * navbar
4263  * 
4264  */
4265
4266 /**
4267  * @class Roo.bootstrap.NavSidebar
4268  * @extends Roo.bootstrap.Navbar
4269  * Bootstrap Sidebar class
4270  * 
4271  * @constructor
4272  * Create a new Sidebar
4273  * @param {Object} config The config object
4274  */
4275
4276
4277 Roo.bootstrap.NavSidebar = function(config){
4278     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4279 };
4280
4281 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4282     
4283     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4284     
4285     getAutoCreate : function(){
4286         
4287         
4288         return  {
4289             tag: 'div',
4290             cls: 'sidebar sidebar-nav'
4291         };
4292     
4293         
4294     }
4295     
4296     
4297     
4298 });
4299
4300
4301
4302  
4303
4304  /*
4305  * - LGPL
4306  *
4307  * nav group
4308  * 
4309  */
4310
4311 /**
4312  * @class Roo.bootstrap.NavGroup
4313  * @extends Roo.bootstrap.Component
4314  * Bootstrap NavGroup class
4315  * @cfg {String} align (left|right)
4316  * @cfg {Boolean} inverse
4317  * @cfg {String} type (nav|pills|tab) default nav
4318  * @cfg {String} navId - reference Id for navbar.
4319
4320  * 
4321  * @constructor
4322  * Create a new nav group
4323  * @param {Object} config The config object
4324  */
4325
4326 Roo.bootstrap.NavGroup = function(config){
4327     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4328     this.navItems = [];
4329    
4330     Roo.bootstrap.NavGroup.register(this);
4331      this.addEvents({
4332         /**
4333              * @event changed
4334              * Fires when the active item changes
4335              * @param {Roo.bootstrap.NavGroup} this
4336              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4337              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4338          */
4339         'changed': true
4340      });
4341     
4342 };
4343
4344 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4345     
4346     align: '',
4347     inverse: false,
4348     form: false,
4349     type: 'nav',
4350     navId : '',
4351     // private
4352     
4353     navItems : false, 
4354     
4355     getAutoCreate : function()
4356     {
4357         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4358         
4359         cfg = {
4360             tag : 'ul',
4361             cls: 'nav' 
4362         };
4363         if (Roo.bootstrap.version == 4) {
4364             if (this.type == 'pills') {
4365                 cfg.cls = ' nav-pills';
4366             }
4367         } else {
4368             if (['tabs','pills'].indexOf(this.type)!==-1) {
4369                 cfg.cls += ' nav-' + this.type
4370             } else {
4371                 if (this.type !== 'nav') {
4372                     Roo.log('nav type must be nav/tabs/pills')
4373                 }
4374                 cfg.cls += ' navbar-nav'
4375             }
4376         }
4377         
4378         if (this.parent() && this.parent().sidebar) {
4379             cfg = {
4380                 tag: 'ul',
4381                 cls: 'dashboard-menu sidebar-menu'
4382             };
4383             
4384             return cfg;
4385         }
4386         
4387         if (this.form === true) {
4388             cfg = {
4389                 tag: 'form',
4390                 cls: 'navbar-form form-inline'
4391             };
4392             
4393             if (this.align === 'right') {
4394                 cfg.cls += ' navbar-right ml-md-auto';
4395             } else {
4396                 cfg.cls += ' navbar-left';
4397             }
4398         }
4399         
4400         if (this.align === 'right') {
4401             cfg.cls += ' navbar-right ml-md-auto';
4402         } else {
4403             cfg.cls += ' mr-auto';
4404         }
4405         
4406         if (this.inverse) {
4407             cfg.cls += ' navbar-inverse';
4408             
4409         }
4410         
4411         
4412         return cfg;
4413     },
4414     /**
4415     * sets the active Navigation item
4416     * @param {Roo.bootstrap.NavItem} the new current navitem
4417     */
4418     setActiveItem : function(item)
4419     {
4420         var prev = false;
4421         Roo.each(this.navItems, function(v){
4422             if (v == item) {
4423                 return ;
4424             }
4425             if (v.isActive()) {
4426                 v.setActive(false, true);
4427                 prev = v;
4428                 
4429             }
4430             
4431         });
4432
4433         item.setActive(true, true);
4434         this.fireEvent('changed', this, item, prev);
4435         
4436         
4437     },
4438     /**
4439     * gets the active Navigation item
4440     * @return {Roo.bootstrap.NavItem} the current navitem
4441     */
4442     getActive : function()
4443     {
4444         
4445         var prev = false;
4446         Roo.each(this.navItems, function(v){
4447             
4448             if (v.isActive()) {
4449                 prev = v;
4450                 
4451             }
4452             
4453         });
4454         return prev;
4455     },
4456     
4457     indexOfNav : function()
4458     {
4459         
4460         var prev = false;
4461         Roo.each(this.navItems, function(v,i){
4462             
4463             if (v.isActive()) {
4464                 prev = i;
4465                 
4466             }
4467             
4468         });
4469         return prev;
4470     },
4471     /**
4472     * adds a Navigation item
4473     * @param {Roo.bootstrap.NavItem} the navitem to add
4474     */
4475     addItem : function(cfg)
4476     {
4477         if (this.form && Roo.bootstrap.version == 4) {
4478             cfg.tag = 'div';
4479         }
4480         var cn = new Roo.bootstrap.NavItem(cfg);
4481         this.register(cn);
4482         cn.parentId = this.id;
4483         cn.onRender(this.el, null);
4484         return cn;
4485     },
4486     /**
4487     * register a Navigation item
4488     * @param {Roo.bootstrap.NavItem} the navitem to add
4489     */
4490     register : function(item)
4491     {
4492         this.navItems.push( item);
4493         item.navId = this.navId;
4494     
4495     },
4496     
4497     /**
4498     * clear all the Navigation item
4499     */
4500    
4501     clearAll : function()
4502     {
4503         this.navItems = [];
4504         this.el.dom.innerHTML = '';
4505     },
4506     
4507     getNavItem: function(tabId)
4508     {
4509         var ret = false;
4510         Roo.each(this.navItems, function(e) {
4511             if (e.tabId == tabId) {
4512                ret =  e;
4513                return false;
4514             }
4515             return true;
4516             
4517         });
4518         return ret;
4519     },
4520     
4521     setActiveNext : function()
4522     {
4523         var i = this.indexOfNav(this.getActive());
4524         if (i > this.navItems.length) {
4525             return;
4526         }
4527         this.setActiveItem(this.navItems[i+1]);
4528     },
4529     setActivePrev : function()
4530     {
4531         var i = this.indexOfNav(this.getActive());
4532         if (i  < 1) {
4533             return;
4534         }
4535         this.setActiveItem(this.navItems[i-1]);
4536     },
4537     clearWasActive : function(except) {
4538         Roo.each(this.navItems, function(e) {
4539             if (e.tabId != except.tabId && e.was_active) {
4540                e.was_active = false;
4541                return false;
4542             }
4543             return true;
4544             
4545         });
4546     },
4547     getWasActive : function ()
4548     {
4549         var r = false;
4550         Roo.each(this.navItems, function(e) {
4551             if (e.was_active) {
4552                r = e;
4553                return false;
4554             }
4555             return true;
4556             
4557         });
4558         return r;
4559     }
4560     
4561     
4562 });
4563
4564  
4565 Roo.apply(Roo.bootstrap.NavGroup, {
4566     
4567     groups: {},
4568      /**
4569     * register a Navigation Group
4570     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4571     */
4572     register : function(navgrp)
4573     {
4574         this.groups[navgrp.navId] = navgrp;
4575         
4576     },
4577     /**
4578     * fetch a Navigation Group based on the navigation ID
4579     * @param {string} the navgroup to add
4580     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4581     */
4582     get: function(navId) {
4583         if (typeof(this.groups[navId]) == 'undefined') {
4584             return false;
4585             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4586         }
4587         return this.groups[navId] ;
4588     }
4589     
4590     
4591     
4592 });
4593
4594  /*
4595  * - LGPL
4596  *
4597  * row
4598  * 
4599  */
4600
4601 /**
4602  * @class Roo.bootstrap.NavItem
4603  * @extends Roo.bootstrap.Component
4604  * Bootstrap Navbar.NavItem class
4605  * @cfg {String} href  link to
4606  * @cfg {String} html content of button
4607  * @cfg {String} badge text inside badge
4608  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4609  * @cfg {String} glyphicon DEPRICATED - use fa
4610  * @cfg {String} icon DEPRICATED - use fa
4611  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4612  * @cfg {Boolean} active Is item active
4613  * @cfg {Boolean} disabled Is item disabled
4614  
4615  * @cfg {Boolean} preventDefault (true | false) default false
4616  * @cfg {String} tabId the tab that this item activates.
4617  * @cfg {String} tagtype (a|span) render as a href or span?
4618  * @cfg {Boolean} animateRef (true|false) link to element default false  
4619   
4620  * @constructor
4621  * Create a new Navbar Item
4622  * @param {Object} config The config object
4623  */
4624 Roo.bootstrap.NavItem = function(config){
4625     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4626     this.addEvents({
4627         // raw events
4628         /**
4629          * @event click
4630          * The raw click event for the entire grid.
4631          * @param {Roo.EventObject} e
4632          */
4633         "click" : true,
4634          /**
4635             * @event changed
4636             * Fires when the active item active state changes
4637             * @param {Roo.bootstrap.NavItem} this
4638             * @param {boolean} state the new state
4639              
4640          */
4641         'changed': true,
4642         /**
4643             * @event scrollto
4644             * Fires when scroll to element
4645             * @param {Roo.bootstrap.NavItem} this
4646             * @param {Object} options
4647             * @param {Roo.EventObject} e
4648              
4649          */
4650         'scrollto': true
4651     });
4652    
4653 };
4654
4655 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4656     
4657     href: false,
4658     html: '',
4659     badge: '',
4660     icon: false,
4661     fa : false,
4662     glyphicon: false,
4663     active: false,
4664     preventDefault : false,
4665     tabId : false,
4666     tagtype : 'a',
4667     tag: 'li',
4668     disabled : false,
4669     animateRef : false,
4670     was_active : false,
4671     
4672     getAutoCreate : function(){
4673          
4674         var cfg = {
4675             tag: this.tag,
4676             cls: 'nav-item'
4677             
4678         };
4679         
4680         if (this.active) {
4681             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4682         }
4683         if (this.disabled) {
4684             cfg.cls += ' disabled';
4685         }
4686         
4687         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4688             cfg.cn = [
4689                 {
4690                     tag: this.tagtype,
4691                     href : this.href || "#",
4692                     html: this.html || ''
4693                 }
4694             ];
4695             if (this.tagtype == 'a') {
4696                 cfg.cn[0].cls = 'nav-link';
4697             }
4698             if (this.icon) {
4699                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4700             }
4701             if (this.fa) {
4702                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4703             }
4704             if(this.glyphicon) {
4705                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4706             }
4707             
4708             if (this.menu) {
4709                 
4710                 cfg.cn[0].html += " <span class='caret'></span>";
4711              
4712             }
4713             
4714             if (this.badge !== '') {
4715                  
4716                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4717             }
4718         }
4719         
4720         
4721         
4722         return cfg;
4723     },
4724     onRender : function(ct, position)
4725     {
4726        // Roo.log("Call onRender: " + this.xtype);
4727         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
4728             this.tag = 'div';
4729         }
4730         
4731         return Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
4732     },
4733       
4734     
4735     initEvents: function() 
4736     {
4737         if (typeof (this.menu) != 'undefined') {
4738             this.menu.parentType = this.xtype;
4739             this.menu.triggerEl = this.el;
4740             this.menu = this.addxtype(Roo.apply({}, this.menu));
4741         }
4742         
4743         this.el.select('a',true).on('click', this.onClick, this);
4744         
4745         if(this.tagtype == 'span'){
4746             this.el.select('span',true).on('click', this.onClick, this);
4747         }
4748        
4749         // at this point parent should be available..
4750         this.parent().register(this);
4751     },
4752     
4753     onClick : function(e)
4754     {
4755         if (e.getTarget('.dropdown-menu-item')) {
4756             // did you click on a menu itemm.... - then don't trigger onclick..
4757             return;
4758         }
4759         
4760         if(
4761                 this.preventDefault || 
4762                 this.href == '#' 
4763         ){
4764             Roo.log("NavItem - prevent Default?");
4765             e.preventDefault();
4766         }
4767         
4768         if (this.disabled) {
4769             return;
4770         }
4771         
4772         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4773         if (tg && tg.transition) {
4774             Roo.log("waiting for the transitionend");
4775             return;
4776         }
4777         
4778         
4779         
4780         //Roo.log("fire event clicked");
4781         if(this.fireEvent('click', this, e) === false){
4782             return;
4783         };
4784         
4785         if(this.tagtype == 'span'){
4786             return;
4787         }
4788         
4789         //Roo.log(this.href);
4790         var ael = this.el.select('a',true).first();
4791         //Roo.log(ael);
4792         
4793         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4794             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4795             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4796                 return; // ignore... - it's a 'hash' to another page.
4797             }
4798             Roo.log("NavItem - prevent Default?");
4799             e.preventDefault();
4800             this.scrollToElement(e);
4801         }
4802         
4803         
4804         var p =  this.parent();
4805    
4806         if (['tabs','pills'].indexOf(p.type)!==-1) {
4807             if (typeof(p.setActiveItem) !== 'undefined') {
4808                 p.setActiveItem(this);
4809             }
4810         }
4811         
4812         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4813         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4814             // remove the collapsed menu expand...
4815             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4816         }
4817     },
4818     
4819     isActive: function () {
4820         return this.active
4821     },
4822     setActive : function(state, fire, is_was_active)
4823     {
4824         if (this.active && !state && this.navId) {
4825             this.was_active = true;
4826             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4827             if (nv) {
4828                 nv.clearWasActive(this);
4829             }
4830             
4831         }
4832         this.active = state;
4833         
4834         if (!state ) {
4835             this.el.removeClass('active');
4836         } else if (!this.el.hasClass('active')) {
4837             this.el.addClass('active');
4838         }
4839         if (fire) {
4840             this.fireEvent('changed', this, state);
4841         }
4842         
4843         // show a panel if it's registered and related..
4844         
4845         if (!this.navId || !this.tabId || !state || is_was_active) {
4846             return;
4847         }
4848         
4849         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4850         if (!tg) {
4851             return;
4852         }
4853         var pan = tg.getPanelByName(this.tabId);
4854         if (!pan) {
4855             return;
4856         }
4857         // if we can not flip to new panel - go back to old nav highlight..
4858         if (false == tg.showPanel(pan)) {
4859             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4860             if (nv) {
4861                 var onav = nv.getWasActive();
4862                 if (onav) {
4863                     onav.setActive(true, false, true);
4864                 }
4865             }
4866             
4867         }
4868         
4869         
4870         
4871     },
4872      // this should not be here...
4873     setDisabled : function(state)
4874     {
4875         this.disabled = state;
4876         if (!state ) {
4877             this.el.removeClass('disabled');
4878         } else if (!this.el.hasClass('disabled')) {
4879             this.el.addClass('disabled');
4880         }
4881         
4882     },
4883     
4884     /**
4885      * Fetch the element to display the tooltip on.
4886      * @return {Roo.Element} defaults to this.el
4887      */
4888     tooltipEl : function()
4889     {
4890         return this.el.select('' + this.tagtype + '', true).first();
4891     },
4892     
4893     scrollToElement : function(e)
4894     {
4895         var c = document.body;
4896         
4897         /*
4898          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4899          */
4900         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4901             c = document.documentElement;
4902         }
4903         
4904         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4905         
4906         if(!target){
4907             return;
4908         }
4909
4910         var o = target.calcOffsetsTo(c);
4911         
4912         var options = {
4913             target : target,
4914             value : o[1]
4915         };
4916         
4917         this.fireEvent('scrollto', this, options, e);
4918         
4919         Roo.get(c).scrollTo('top', options.value, true);
4920         
4921         return;
4922     }
4923 });
4924  
4925
4926  /*
4927  * - LGPL
4928  *
4929  * sidebar item
4930  *
4931  *  li
4932  *    <span> icon </span>
4933  *    <span> text </span>
4934  *    <span>badge </span>
4935  */
4936
4937 /**
4938  * @class Roo.bootstrap.NavSidebarItem
4939  * @extends Roo.bootstrap.NavItem
4940  * Bootstrap Navbar.NavSidebarItem class
4941  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4942  * {Boolean} open is the menu open
4943  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4944  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4945  * {String} buttonSize (sm|md|lg)the extra classes for the button
4946  * {Boolean} showArrow show arrow next to the text (default true)
4947  * @constructor
4948  * Create a new Navbar Button
4949  * @param {Object} config The config object
4950  */
4951 Roo.bootstrap.NavSidebarItem = function(config){
4952     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4953     this.addEvents({
4954         // raw events
4955         /**
4956          * @event click
4957          * The raw click event for the entire grid.
4958          * @param {Roo.EventObject} e
4959          */
4960         "click" : true,
4961          /**
4962             * @event changed
4963             * Fires when the active item active state changes
4964             * @param {Roo.bootstrap.NavSidebarItem} this
4965             * @param {boolean} state the new state
4966              
4967          */
4968         'changed': true
4969     });
4970    
4971 };
4972
4973 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4974     
4975     badgeWeight : 'default',
4976     
4977     open: false,
4978     
4979     buttonView : false,
4980     
4981     buttonWeight : 'default',
4982     
4983     buttonSize : 'md',
4984     
4985     showArrow : true,
4986     
4987     getAutoCreate : function(){
4988         
4989         
4990         var a = {
4991                 tag: 'a',
4992                 href : this.href || '#',
4993                 cls: '',
4994                 html : '',
4995                 cn : []
4996         };
4997         
4998         if(this.buttonView){
4999             a = {
5000                 tag: 'button',
5001                 href : this.href || '#',
5002                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5003                 html : this.html,
5004                 cn : []
5005             };
5006         }
5007         
5008         var cfg = {
5009             tag: 'li',
5010             cls: '',
5011             cn: [ a ]
5012         };
5013         
5014         if (this.active) {
5015             cfg.cls += ' active';
5016         }
5017         
5018         if (this.disabled) {
5019             cfg.cls += ' disabled';
5020         }
5021         if (this.open) {
5022             cfg.cls += ' open x-open';
5023         }
5024         // left icon..
5025         if (this.glyphicon || this.icon) {
5026             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5027             a.cn.push({ tag : 'i', cls : c }) ;
5028         }
5029         
5030         if(!this.buttonView){
5031             var span = {
5032                 tag: 'span',
5033                 html : this.html || ''
5034             };
5035
5036             a.cn.push(span);
5037             
5038         }
5039         
5040         if (this.badge !== '') {
5041             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5042         }
5043         
5044         if (this.menu) {
5045             
5046             if(this.showArrow){
5047                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5048             }
5049             
5050             a.cls += ' dropdown-toggle treeview' ;
5051         }
5052         
5053         return cfg;
5054     },
5055     
5056     initEvents : function()
5057     { 
5058         if (typeof (this.menu) != 'undefined') {
5059             this.menu.parentType = this.xtype;
5060             this.menu.triggerEl = this.el;
5061             this.menu = this.addxtype(Roo.apply({}, this.menu));
5062         }
5063         
5064         this.el.on('click', this.onClick, this);
5065         
5066         if(this.badge !== ''){
5067             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5068         }
5069         
5070     },
5071     
5072     onClick : function(e)
5073     {
5074         if(this.disabled){
5075             e.preventDefault();
5076             return;
5077         }
5078         
5079         if(this.preventDefault){
5080             e.preventDefault();
5081         }
5082         
5083         this.fireEvent('click', this);
5084     },
5085     
5086     disable : function()
5087     {
5088         this.setDisabled(true);
5089     },
5090     
5091     enable : function()
5092     {
5093         this.setDisabled(false);
5094     },
5095     
5096     setDisabled : function(state)
5097     {
5098         if(this.disabled == state){
5099             return;
5100         }
5101         
5102         this.disabled = state;
5103         
5104         if (state) {
5105             this.el.addClass('disabled');
5106             return;
5107         }
5108         
5109         this.el.removeClass('disabled');
5110         
5111         return;
5112     },
5113     
5114     setActive : function(state)
5115     {
5116         if(this.active == state){
5117             return;
5118         }
5119         
5120         this.active = state;
5121         
5122         if (state) {
5123             this.el.addClass('active');
5124             return;
5125         }
5126         
5127         this.el.removeClass('active');
5128         
5129         return;
5130     },
5131     
5132     isActive: function () 
5133     {
5134         return this.active;
5135     },
5136     
5137     setBadge : function(str)
5138     {
5139         if(!this.badgeEl){
5140             return;
5141         }
5142         
5143         this.badgeEl.dom.innerHTML = str;
5144     }
5145     
5146    
5147      
5148  
5149 });
5150  
5151
5152  /*
5153  * - LGPL
5154  *
5155  * row
5156  * 
5157  */
5158
5159 /**
5160  * @class Roo.bootstrap.Row
5161  * @extends Roo.bootstrap.Component
5162  * Bootstrap Row class (contains columns...)
5163  * 
5164  * @constructor
5165  * Create a new Row
5166  * @param {Object} config The config object
5167  */
5168
5169 Roo.bootstrap.Row = function(config){
5170     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5171 };
5172
5173 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5174     
5175     getAutoCreate : function(){
5176        return {
5177             cls: 'row clearfix'
5178        };
5179     }
5180     
5181     
5182 });
5183
5184  
5185
5186  /*
5187  * - LGPL
5188  *
5189  * element
5190  * 
5191  */
5192
5193 /**
5194  * @class Roo.bootstrap.Element
5195  * @extends Roo.bootstrap.Component
5196  * Bootstrap Element class
5197  * @cfg {String} html contents of the element
5198  * @cfg {String} tag tag of the element
5199  * @cfg {String} cls class of the element
5200  * @cfg {Boolean} preventDefault (true|false) default false
5201  * @cfg {Boolean} clickable (true|false) default false
5202  * 
5203  * @constructor
5204  * Create a new Element
5205  * @param {Object} config The config object
5206  */
5207
5208 Roo.bootstrap.Element = function(config){
5209     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5210     
5211     this.addEvents({
5212         // raw events
5213         /**
5214          * @event click
5215          * When a element is chick
5216          * @param {Roo.bootstrap.Element} this
5217          * @param {Roo.EventObject} e
5218          */
5219         "click" : true
5220     });
5221 };
5222
5223 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5224     
5225     tag: 'div',
5226     cls: '',
5227     html: '',
5228     preventDefault: false, 
5229     clickable: false,
5230     
5231     getAutoCreate : function(){
5232         
5233         var cfg = {
5234             tag: this.tag,
5235             // cls: this.cls, double assign in parent class Component.js :: onRender
5236             html: this.html
5237         };
5238         
5239         return cfg;
5240     },
5241     
5242     initEvents: function() 
5243     {
5244         Roo.bootstrap.Element.superclass.initEvents.call(this);
5245         
5246         if(this.clickable){
5247             this.el.on('click', this.onClick, this);
5248         }
5249         
5250     },
5251     
5252     onClick : function(e)
5253     {
5254         if(this.preventDefault){
5255             e.preventDefault();
5256         }
5257         
5258         this.fireEvent('click', this, e);
5259     },
5260     
5261     getValue : function()
5262     {
5263         return this.el.dom.innerHTML;
5264     },
5265     
5266     setValue : function(value)
5267     {
5268         this.el.dom.innerHTML = value;
5269     }
5270    
5271 });
5272
5273  
5274
5275  /*
5276  * - LGPL
5277  *
5278  * pagination
5279  * 
5280  */
5281
5282 /**
5283  * @class Roo.bootstrap.Pagination
5284  * @extends Roo.bootstrap.Component
5285  * Bootstrap Pagination class
5286  * @cfg {String} size xs | sm | md | lg
5287  * @cfg {Boolean} inverse false | true
5288  * 
5289  * @constructor
5290  * Create a new Pagination
5291  * @param {Object} config The config object
5292  */
5293
5294 Roo.bootstrap.Pagination = function(config){
5295     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5296 };
5297
5298 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5299     
5300     cls: false,
5301     size: false,
5302     inverse: false,
5303     
5304     getAutoCreate : function(){
5305         var cfg = {
5306             tag: 'ul',
5307                 cls: 'pagination'
5308         };
5309         if (this.inverse) {
5310             cfg.cls += ' inverse';
5311         }
5312         if (this.html) {
5313             cfg.html=this.html;
5314         }
5315         if (this.cls) {
5316             cfg.cls += " " + this.cls;
5317         }
5318         return cfg;
5319     }
5320    
5321 });
5322
5323  
5324
5325  /*
5326  * - LGPL
5327  *
5328  * Pagination item
5329  * 
5330  */
5331
5332
5333 /**
5334  * @class Roo.bootstrap.PaginationItem
5335  * @extends Roo.bootstrap.Component
5336  * Bootstrap PaginationItem class
5337  * @cfg {String} html text
5338  * @cfg {String} href the link
5339  * @cfg {Boolean} preventDefault (true | false) default true
5340  * @cfg {Boolean} active (true | false) default false
5341  * @cfg {Boolean} disabled default false
5342  * 
5343  * 
5344  * @constructor
5345  * Create a new PaginationItem
5346  * @param {Object} config The config object
5347  */
5348
5349
5350 Roo.bootstrap.PaginationItem = function(config){
5351     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5352     this.addEvents({
5353         // raw events
5354         /**
5355          * @event click
5356          * The raw click event for the entire grid.
5357          * @param {Roo.EventObject} e
5358          */
5359         "click" : true
5360     });
5361 };
5362
5363 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5364     
5365     href : false,
5366     html : false,
5367     preventDefault: true,
5368     active : false,
5369     cls : false,
5370     disabled: false,
5371     
5372     getAutoCreate : function(){
5373         var cfg= {
5374             tag: 'li',
5375             cn: [
5376                 {
5377                     tag : 'a',
5378                     href : this.href ? this.href : '#',
5379                     html : this.html ? this.html : ''
5380                 }
5381             ]
5382         };
5383         
5384         if(this.cls){
5385             cfg.cls = this.cls;
5386         }
5387         
5388         if(this.disabled){
5389             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5390         }
5391         
5392         if(this.active){
5393             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5394         }
5395         
5396         return cfg;
5397     },
5398     
5399     initEvents: function() {
5400         
5401         this.el.on('click', this.onClick, this);
5402         
5403     },
5404     onClick : function(e)
5405     {
5406         Roo.log('PaginationItem on click ');
5407         if(this.preventDefault){
5408             e.preventDefault();
5409         }
5410         
5411         if(this.disabled){
5412             return;
5413         }
5414         
5415         this.fireEvent('click', this, e);
5416     }
5417    
5418 });
5419
5420  
5421
5422  /*
5423  * - LGPL
5424  *
5425  * slider
5426  * 
5427  */
5428
5429
5430 /**
5431  * @class Roo.bootstrap.Slider
5432  * @extends Roo.bootstrap.Component
5433  * Bootstrap Slider class
5434  *    
5435  * @constructor
5436  * Create a new Slider
5437  * @param {Object} config The config object
5438  */
5439
5440 Roo.bootstrap.Slider = function(config){
5441     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5442 };
5443
5444 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5445     
5446     getAutoCreate : function(){
5447         
5448         var cfg = {
5449             tag: 'div',
5450             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5451             cn: [
5452                 {
5453                     tag: 'a',
5454                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5455                 }
5456             ]
5457         };
5458         
5459         return cfg;
5460     }
5461    
5462 });
5463
5464  /*
5465  * Based on:
5466  * Ext JS Library 1.1.1
5467  * Copyright(c) 2006-2007, Ext JS, LLC.
5468  *
5469  * Originally Released Under LGPL - original licence link has changed is not relivant.
5470  *
5471  * Fork - LGPL
5472  * <script type="text/javascript">
5473  */
5474  
5475
5476 /**
5477  * @class Roo.grid.ColumnModel
5478  * @extends Roo.util.Observable
5479  * This is the default implementation of a ColumnModel used by the Grid. It defines
5480  * the columns in the grid.
5481  * <br>Usage:<br>
5482  <pre><code>
5483  var colModel = new Roo.grid.ColumnModel([
5484         {header: "Ticker", width: 60, sortable: true, locked: true},
5485         {header: "Company Name", width: 150, sortable: true},
5486         {header: "Market Cap.", width: 100, sortable: true},
5487         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5488         {header: "Employees", width: 100, sortable: true, resizable: false}
5489  ]);
5490  </code></pre>
5491  * <p>
5492  
5493  * The config options listed for this class are options which may appear in each
5494  * individual column definition.
5495  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5496  * @constructor
5497  * @param {Object} config An Array of column config objects. See this class's
5498  * config objects for details.
5499 */
5500 Roo.grid.ColumnModel = function(config){
5501         /**
5502      * The config passed into the constructor
5503      */
5504     this.config = config;
5505     this.lookup = {};
5506
5507     // if no id, create one
5508     // if the column does not have a dataIndex mapping,
5509     // map it to the order it is in the config
5510     for(var i = 0, len = config.length; i < len; i++){
5511         var c = config[i];
5512         if(typeof c.dataIndex == "undefined"){
5513             c.dataIndex = i;
5514         }
5515         if(typeof c.renderer == "string"){
5516             c.renderer = Roo.util.Format[c.renderer];
5517         }
5518         if(typeof c.id == "undefined"){
5519             c.id = Roo.id();
5520         }
5521         if(c.editor && c.editor.xtype){
5522             c.editor  = Roo.factory(c.editor, Roo.grid);
5523         }
5524         if(c.editor && c.editor.isFormField){
5525             c.editor = new Roo.grid.GridEditor(c.editor);
5526         }
5527         this.lookup[c.id] = c;
5528     }
5529
5530     /**
5531      * The width of columns which have no width specified (defaults to 100)
5532      * @type Number
5533      */
5534     this.defaultWidth = 100;
5535
5536     /**
5537      * Default sortable of columns which have no sortable specified (defaults to false)
5538      * @type Boolean
5539      */
5540     this.defaultSortable = false;
5541
5542     this.addEvents({
5543         /**
5544              * @event widthchange
5545              * Fires when the width of a column changes.
5546              * @param {ColumnModel} this
5547              * @param {Number} columnIndex The column index
5548              * @param {Number} newWidth The new width
5549              */
5550             "widthchange": true,
5551         /**
5552              * @event headerchange
5553              * Fires when the text of a header changes.
5554              * @param {ColumnModel} this
5555              * @param {Number} columnIndex The column index
5556              * @param {Number} newText The new header text
5557              */
5558             "headerchange": true,
5559         /**
5560              * @event hiddenchange
5561              * Fires when a column is hidden or "unhidden".
5562              * @param {ColumnModel} this
5563              * @param {Number} columnIndex The column index
5564              * @param {Boolean} hidden true if hidden, false otherwise
5565              */
5566             "hiddenchange": true,
5567             /**
5568          * @event columnmoved
5569          * Fires when a column is moved.
5570          * @param {ColumnModel} this
5571          * @param {Number} oldIndex
5572          * @param {Number} newIndex
5573          */
5574         "columnmoved" : true,
5575         /**
5576          * @event columlockchange
5577          * Fires when a column's locked state is changed
5578          * @param {ColumnModel} this
5579          * @param {Number} colIndex
5580          * @param {Boolean} locked true if locked
5581          */
5582         "columnlockchange" : true
5583     });
5584     Roo.grid.ColumnModel.superclass.constructor.call(this);
5585 };
5586 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5587     /**
5588      * @cfg {String} header The header text to display in the Grid view.
5589      */
5590     /**
5591      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5592      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5593      * specified, the column's index is used as an index into the Record's data Array.
5594      */
5595     /**
5596      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5597      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5598      */
5599     /**
5600      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5601      * Defaults to the value of the {@link #defaultSortable} property.
5602      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5603      */
5604     /**
5605      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5606      */
5607     /**
5608      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5609      */
5610     /**
5611      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5612      */
5613     /**
5614      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5615      */
5616     /**
5617      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5618      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5619      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5620      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5621      */
5622        /**
5623      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5624      */
5625     /**
5626      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5627      */
5628     /**
5629      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5630      */
5631     /**
5632      * @cfg {String} cursor (Optional)
5633      */
5634     /**
5635      * @cfg {String} tooltip (Optional)
5636      */
5637     /**
5638      * @cfg {Number} xs (Optional)
5639      */
5640     /**
5641      * @cfg {Number} sm (Optional)
5642      */
5643     /**
5644      * @cfg {Number} md (Optional)
5645      */
5646     /**
5647      * @cfg {Number} lg (Optional)
5648      */
5649     /**
5650      * Returns the id of the column at the specified index.
5651      * @param {Number} index The column index
5652      * @return {String} the id
5653      */
5654     getColumnId : function(index){
5655         return this.config[index].id;
5656     },
5657
5658     /**
5659      * Returns the column for a specified id.
5660      * @param {String} id The column id
5661      * @return {Object} the column
5662      */
5663     getColumnById : function(id){
5664         return this.lookup[id];
5665     },
5666
5667     
5668     /**
5669      * Returns the column for a specified dataIndex.
5670      * @param {String} dataIndex The column dataIndex
5671      * @return {Object|Boolean} the column or false if not found
5672      */
5673     getColumnByDataIndex: function(dataIndex){
5674         var index = this.findColumnIndex(dataIndex);
5675         return index > -1 ? this.config[index] : false;
5676     },
5677     
5678     /**
5679      * Returns the index for a specified column id.
5680      * @param {String} id The column id
5681      * @return {Number} the index, or -1 if not found
5682      */
5683     getIndexById : function(id){
5684         for(var i = 0, len = this.config.length; i < len; i++){
5685             if(this.config[i].id == id){
5686                 return i;
5687             }
5688         }
5689         return -1;
5690     },
5691     
5692     /**
5693      * Returns the index for a specified column dataIndex.
5694      * @param {String} dataIndex The column dataIndex
5695      * @return {Number} the index, or -1 if not found
5696      */
5697     
5698     findColumnIndex : function(dataIndex){
5699         for(var i = 0, len = this.config.length; i < len; i++){
5700             if(this.config[i].dataIndex == dataIndex){
5701                 return i;
5702             }
5703         }
5704         return -1;
5705     },
5706     
5707     
5708     moveColumn : function(oldIndex, newIndex){
5709         var c = this.config[oldIndex];
5710         this.config.splice(oldIndex, 1);
5711         this.config.splice(newIndex, 0, c);
5712         this.dataMap = null;
5713         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5714     },
5715
5716     isLocked : function(colIndex){
5717         return this.config[colIndex].locked === true;
5718     },
5719
5720     setLocked : function(colIndex, value, suppressEvent){
5721         if(this.isLocked(colIndex) == value){
5722             return;
5723         }
5724         this.config[colIndex].locked = value;
5725         if(!suppressEvent){
5726             this.fireEvent("columnlockchange", this, colIndex, value);
5727         }
5728     },
5729
5730     getTotalLockedWidth : function(){
5731         var totalWidth = 0;
5732         for(var i = 0; i < this.config.length; i++){
5733             if(this.isLocked(i) && !this.isHidden(i)){
5734                 this.totalWidth += this.getColumnWidth(i);
5735             }
5736         }
5737         return totalWidth;
5738     },
5739
5740     getLockedCount : function(){
5741         for(var i = 0, len = this.config.length; i < len; i++){
5742             if(!this.isLocked(i)){
5743                 return i;
5744             }
5745         }
5746         
5747         return this.config.length;
5748     },
5749
5750     /**
5751      * Returns the number of columns.
5752      * @return {Number}
5753      */
5754     getColumnCount : function(visibleOnly){
5755         if(visibleOnly === true){
5756             var c = 0;
5757             for(var i = 0, len = this.config.length; i < len; i++){
5758                 if(!this.isHidden(i)){
5759                     c++;
5760                 }
5761             }
5762             return c;
5763         }
5764         return this.config.length;
5765     },
5766
5767     /**
5768      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5769      * @param {Function} fn
5770      * @param {Object} scope (optional)
5771      * @return {Array} result
5772      */
5773     getColumnsBy : function(fn, scope){
5774         var r = [];
5775         for(var i = 0, len = this.config.length; i < len; i++){
5776             var c = this.config[i];
5777             if(fn.call(scope||this, c, i) === true){
5778                 r[r.length] = c;
5779             }
5780         }
5781         return r;
5782     },
5783
5784     /**
5785      * Returns true if the specified column is sortable.
5786      * @param {Number} col The column index
5787      * @return {Boolean}
5788      */
5789     isSortable : function(col){
5790         if(typeof this.config[col].sortable == "undefined"){
5791             return this.defaultSortable;
5792         }
5793         return this.config[col].sortable;
5794     },
5795
5796     /**
5797      * Returns the rendering (formatting) function defined for the column.
5798      * @param {Number} col The column index.
5799      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5800      */
5801     getRenderer : function(col){
5802         if(!this.config[col].renderer){
5803             return Roo.grid.ColumnModel.defaultRenderer;
5804         }
5805         return this.config[col].renderer;
5806     },
5807
5808     /**
5809      * Sets the rendering (formatting) function for a column.
5810      * @param {Number} col The column index
5811      * @param {Function} fn The function to use to process the cell's raw data
5812      * to return HTML markup for the grid view. The render function is called with
5813      * the following parameters:<ul>
5814      * <li>Data value.</li>
5815      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5816      * <li>css A CSS style string to apply to the table cell.</li>
5817      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5818      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5819      * <li>Row index</li>
5820      * <li>Column index</li>
5821      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5822      */
5823     setRenderer : function(col, fn){
5824         this.config[col].renderer = fn;
5825     },
5826
5827     /**
5828      * Returns the width for the specified column.
5829      * @param {Number} col The column index
5830      * @return {Number}
5831      */
5832     getColumnWidth : function(col){
5833         return this.config[col].width * 1 || this.defaultWidth;
5834     },
5835
5836     /**
5837      * Sets the width for a column.
5838      * @param {Number} col The column index
5839      * @param {Number} width The new width
5840      */
5841     setColumnWidth : function(col, width, suppressEvent){
5842         this.config[col].width = width;
5843         this.totalWidth = null;
5844         if(!suppressEvent){
5845              this.fireEvent("widthchange", this, col, width);
5846         }
5847     },
5848
5849     /**
5850      * Returns the total width of all columns.
5851      * @param {Boolean} includeHidden True to include hidden column widths
5852      * @return {Number}
5853      */
5854     getTotalWidth : function(includeHidden){
5855         if(!this.totalWidth){
5856             this.totalWidth = 0;
5857             for(var i = 0, len = this.config.length; i < len; i++){
5858                 if(includeHidden || !this.isHidden(i)){
5859                     this.totalWidth += this.getColumnWidth(i);
5860                 }
5861             }
5862         }
5863         return this.totalWidth;
5864     },
5865
5866     /**
5867      * Returns the header for the specified column.
5868      * @param {Number} col The column index
5869      * @return {String}
5870      */
5871     getColumnHeader : function(col){
5872         return this.config[col].header;
5873     },
5874
5875     /**
5876      * Sets the header for a column.
5877      * @param {Number} col The column index
5878      * @param {String} header The new header
5879      */
5880     setColumnHeader : function(col, header){
5881         this.config[col].header = header;
5882         this.fireEvent("headerchange", this, col, header);
5883     },
5884
5885     /**
5886      * Returns the tooltip for the specified column.
5887      * @param {Number} col The column index
5888      * @return {String}
5889      */
5890     getColumnTooltip : function(col){
5891             return this.config[col].tooltip;
5892     },
5893     /**
5894      * Sets the tooltip for a column.
5895      * @param {Number} col The column index
5896      * @param {String} tooltip The new tooltip
5897      */
5898     setColumnTooltip : function(col, tooltip){
5899             this.config[col].tooltip = tooltip;
5900     },
5901
5902     /**
5903      * Returns the dataIndex for the specified column.
5904      * @param {Number} col The column index
5905      * @return {Number}
5906      */
5907     getDataIndex : function(col){
5908         return this.config[col].dataIndex;
5909     },
5910
5911     /**
5912      * Sets the dataIndex for a column.
5913      * @param {Number} col The column index
5914      * @param {Number} dataIndex The new dataIndex
5915      */
5916     setDataIndex : function(col, dataIndex){
5917         this.config[col].dataIndex = dataIndex;
5918     },
5919
5920     
5921     
5922     /**
5923      * Returns true if the cell is editable.
5924      * @param {Number} colIndex The column index
5925      * @param {Number} rowIndex The row index - this is nto actually used..?
5926      * @return {Boolean}
5927      */
5928     isCellEditable : function(colIndex, rowIndex){
5929         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5930     },
5931
5932     /**
5933      * Returns the editor defined for the cell/column.
5934      * return false or null to disable editing.
5935      * @param {Number} colIndex The column index
5936      * @param {Number} rowIndex The row index
5937      * @return {Object}
5938      */
5939     getCellEditor : function(colIndex, rowIndex){
5940         return this.config[colIndex].editor;
5941     },
5942
5943     /**
5944      * Sets if a column is editable.
5945      * @param {Number} col The column index
5946      * @param {Boolean} editable True if the column is editable
5947      */
5948     setEditable : function(col, editable){
5949         this.config[col].editable = editable;
5950     },
5951
5952
5953     /**
5954      * Returns true if the column is hidden.
5955      * @param {Number} colIndex The column index
5956      * @return {Boolean}
5957      */
5958     isHidden : function(colIndex){
5959         return this.config[colIndex].hidden;
5960     },
5961
5962
5963     /**
5964      * Returns true if the column width cannot be changed
5965      */
5966     isFixed : function(colIndex){
5967         return this.config[colIndex].fixed;
5968     },
5969
5970     /**
5971      * Returns true if the column can be resized
5972      * @return {Boolean}
5973      */
5974     isResizable : function(colIndex){
5975         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5976     },
5977     /**
5978      * Sets if a column is hidden.
5979      * @param {Number} colIndex The column index
5980      * @param {Boolean} hidden True if the column is hidden
5981      */
5982     setHidden : function(colIndex, hidden){
5983         this.config[colIndex].hidden = hidden;
5984         this.totalWidth = null;
5985         this.fireEvent("hiddenchange", this, colIndex, hidden);
5986     },
5987
5988     /**
5989      * Sets the editor for a column.
5990      * @param {Number} col The column index
5991      * @param {Object} editor The editor object
5992      */
5993     setEditor : function(col, editor){
5994         this.config[col].editor = editor;
5995     }
5996 });
5997
5998 Roo.grid.ColumnModel.defaultRenderer = function(value)
5999 {
6000     if(typeof value == "object") {
6001         return value;
6002     }
6003         if(typeof value == "string" && value.length < 1){
6004             return "&#160;";
6005         }
6006     
6007         return String.format("{0}", value);
6008 };
6009
6010 // Alias for backwards compatibility
6011 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6012 /*
6013  * Based on:
6014  * Ext JS Library 1.1.1
6015  * Copyright(c) 2006-2007, Ext JS, LLC.
6016  *
6017  * Originally Released Under LGPL - original licence link has changed is not relivant.
6018  *
6019  * Fork - LGPL
6020  * <script type="text/javascript">
6021  */
6022  
6023 /**
6024  * @class Roo.LoadMask
6025  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6026  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6027  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6028  * element's UpdateManager load indicator and will be destroyed after the initial load.
6029  * @constructor
6030  * Create a new LoadMask
6031  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6032  * @param {Object} config The config object
6033  */
6034 Roo.LoadMask = function(el, config){
6035     this.el = Roo.get(el);
6036     Roo.apply(this, config);
6037     if(this.store){
6038         this.store.on('beforeload', this.onBeforeLoad, this);
6039         this.store.on('load', this.onLoad, this);
6040         this.store.on('loadexception', this.onLoadException, this);
6041         this.removeMask = false;
6042     }else{
6043         var um = this.el.getUpdateManager();
6044         um.showLoadIndicator = false; // disable the default indicator
6045         um.on('beforeupdate', this.onBeforeLoad, this);
6046         um.on('update', this.onLoad, this);
6047         um.on('failure', this.onLoad, this);
6048         this.removeMask = true;
6049     }
6050 };
6051
6052 Roo.LoadMask.prototype = {
6053     /**
6054      * @cfg {Boolean} removeMask
6055      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6056      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6057      */
6058     /**
6059      * @cfg {String} msg
6060      * The text to display in a centered loading message box (defaults to 'Loading...')
6061      */
6062     msg : 'Loading...',
6063     /**
6064      * @cfg {String} msgCls
6065      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6066      */
6067     msgCls : 'x-mask-loading',
6068
6069     /**
6070      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6071      * @type Boolean
6072      */
6073     disabled: false,
6074
6075     /**
6076      * Disables the mask to prevent it from being displayed
6077      */
6078     disable : function(){
6079        this.disabled = true;
6080     },
6081
6082     /**
6083      * Enables the mask so that it can be displayed
6084      */
6085     enable : function(){
6086         this.disabled = false;
6087     },
6088     
6089     onLoadException : function()
6090     {
6091         Roo.log(arguments);
6092         
6093         if (typeof(arguments[3]) != 'undefined') {
6094             Roo.MessageBox.alert("Error loading",arguments[3]);
6095         } 
6096         /*
6097         try {
6098             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6099                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6100             }   
6101         } catch(e) {
6102             
6103         }
6104         */
6105     
6106         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6107     },
6108     // private
6109     onLoad : function()
6110     {
6111         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6112     },
6113
6114     // private
6115     onBeforeLoad : function(){
6116         if(!this.disabled){
6117             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6118         }
6119     },
6120
6121     // private
6122     destroy : function(){
6123         if(this.store){
6124             this.store.un('beforeload', this.onBeforeLoad, this);
6125             this.store.un('load', this.onLoad, this);
6126             this.store.un('loadexception', this.onLoadException, this);
6127         }else{
6128             var um = this.el.getUpdateManager();
6129             um.un('beforeupdate', this.onBeforeLoad, this);
6130             um.un('update', this.onLoad, this);
6131             um.un('failure', this.onLoad, this);
6132         }
6133     }
6134 };/*
6135  * - LGPL
6136  *
6137  * table
6138  * 
6139  */
6140
6141 /**
6142  * @class Roo.bootstrap.Table
6143  * @extends Roo.bootstrap.Component
6144  * Bootstrap Table class
6145  * @cfg {String} cls table class
6146  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6147  * @cfg {String} bgcolor Specifies the background color for a table
6148  * @cfg {Number} border Specifies whether the table cells should have borders or not
6149  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6150  * @cfg {Number} cellspacing Specifies the space between cells
6151  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6152  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6153  * @cfg {String} sortable Specifies that the table should be sortable
6154  * @cfg {String} summary Specifies a summary of the content of a table
6155  * @cfg {Number} width Specifies the width of a table
6156  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6157  * 
6158  * @cfg {boolean} striped Should the rows be alternative striped
6159  * @cfg {boolean} bordered Add borders to the table
6160  * @cfg {boolean} hover Add hover highlighting
6161  * @cfg {boolean} condensed Format condensed
6162  * @cfg {boolean} responsive Format condensed
6163  * @cfg {Boolean} loadMask (true|false) default false
6164  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6165  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6166  * @cfg {Boolean} rowSelection (true|false) default false
6167  * @cfg {Boolean} cellSelection (true|false) default false
6168  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6169  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6170  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6171  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6172  
6173  * 
6174  * @constructor
6175  * Create a new Table
6176  * @param {Object} config The config object
6177  */
6178
6179 Roo.bootstrap.Table = function(config){
6180     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6181     
6182   
6183     
6184     // BC...
6185     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6186     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6187     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6188     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6189     
6190     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6191     if (this.sm) {
6192         this.sm.grid = this;
6193         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6194         this.sm = this.selModel;
6195         this.sm.xmodule = this.xmodule || false;
6196     }
6197     
6198     if (this.cm && typeof(this.cm.config) == 'undefined') {
6199         this.colModel = new Roo.grid.ColumnModel(this.cm);
6200         this.cm = this.colModel;
6201         this.cm.xmodule = this.xmodule || false;
6202     }
6203     if (this.store) {
6204         this.store= Roo.factory(this.store, Roo.data);
6205         this.ds = this.store;
6206         this.ds.xmodule = this.xmodule || false;
6207          
6208     }
6209     if (this.footer && this.store) {
6210         this.footer.dataSource = this.ds;
6211         this.footer = Roo.factory(this.footer);
6212     }
6213     
6214     /** @private */
6215     this.addEvents({
6216         /**
6217          * @event cellclick
6218          * Fires when a cell is clicked
6219          * @param {Roo.bootstrap.Table} this
6220          * @param {Roo.Element} el
6221          * @param {Number} rowIndex
6222          * @param {Number} columnIndex
6223          * @param {Roo.EventObject} e
6224          */
6225         "cellclick" : true,
6226         /**
6227          * @event celldblclick
6228          * Fires when a cell is double clicked
6229          * @param {Roo.bootstrap.Table} this
6230          * @param {Roo.Element} el
6231          * @param {Number} rowIndex
6232          * @param {Number} columnIndex
6233          * @param {Roo.EventObject} e
6234          */
6235         "celldblclick" : true,
6236         /**
6237          * @event rowclick
6238          * Fires when a row is clicked
6239          * @param {Roo.bootstrap.Table} this
6240          * @param {Roo.Element} el
6241          * @param {Number} rowIndex
6242          * @param {Roo.EventObject} e
6243          */
6244         "rowclick" : true,
6245         /**
6246          * @event rowdblclick
6247          * Fires when a row is double clicked
6248          * @param {Roo.bootstrap.Table} this
6249          * @param {Roo.Element} el
6250          * @param {Number} rowIndex
6251          * @param {Roo.EventObject} e
6252          */
6253         "rowdblclick" : true,
6254         /**
6255          * @event mouseover
6256          * Fires when a mouseover occur
6257          * @param {Roo.bootstrap.Table} this
6258          * @param {Roo.Element} el
6259          * @param {Number} rowIndex
6260          * @param {Number} columnIndex
6261          * @param {Roo.EventObject} e
6262          */
6263         "mouseover" : true,
6264         /**
6265          * @event mouseout
6266          * Fires when a mouseout occur
6267          * @param {Roo.bootstrap.Table} this
6268          * @param {Roo.Element} el
6269          * @param {Number} rowIndex
6270          * @param {Number} columnIndex
6271          * @param {Roo.EventObject} e
6272          */
6273         "mouseout" : true,
6274         /**
6275          * @event rowclass
6276          * Fires when a row is rendered, so you can change add a style to it.
6277          * @param {Roo.bootstrap.Table} this
6278          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6279          */
6280         'rowclass' : true,
6281           /**
6282          * @event rowsrendered
6283          * Fires when all the  rows have been rendered
6284          * @param {Roo.bootstrap.Table} this
6285          */
6286         'rowsrendered' : true,
6287         /**
6288          * @event contextmenu
6289          * The raw contextmenu event for the entire grid.
6290          * @param {Roo.EventObject} e
6291          */
6292         "contextmenu" : true,
6293         /**
6294          * @event rowcontextmenu
6295          * Fires when a row is right clicked
6296          * @param {Roo.bootstrap.Table} this
6297          * @param {Number} rowIndex
6298          * @param {Roo.EventObject} e
6299          */
6300         "rowcontextmenu" : true,
6301         /**
6302          * @event cellcontextmenu
6303          * Fires when a cell is right clicked
6304          * @param {Roo.bootstrap.Table} this
6305          * @param {Number} rowIndex
6306          * @param {Number} cellIndex
6307          * @param {Roo.EventObject} e
6308          */
6309          "cellcontextmenu" : true,
6310          /**
6311          * @event headercontextmenu
6312          * Fires when a header is right clicked
6313          * @param {Roo.bootstrap.Table} this
6314          * @param {Number} columnIndex
6315          * @param {Roo.EventObject} e
6316          */
6317         "headercontextmenu" : true
6318     });
6319 };
6320
6321 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6322     
6323     cls: false,
6324     align: false,
6325     bgcolor: false,
6326     border: false,
6327     cellpadding: false,
6328     cellspacing: false,
6329     frame: false,
6330     rules: false,
6331     sortable: false,
6332     summary: false,
6333     width: false,
6334     striped : false,
6335     scrollBody : false,
6336     bordered: false,
6337     hover:  false,
6338     condensed : false,
6339     responsive : false,
6340     sm : false,
6341     cm : false,
6342     store : false,
6343     loadMask : false,
6344     footerShow : true,
6345     headerShow : true,
6346   
6347     rowSelection : false,
6348     cellSelection : false,
6349     layout : false,
6350     
6351     // Roo.Element - the tbody
6352     mainBody: false,
6353     // Roo.Element - thead element
6354     mainHead: false,
6355     
6356     container: false, // used by gridpanel...
6357     
6358     lazyLoad : false,
6359     
6360     CSS : Roo.util.CSS,
6361     
6362     auto_hide_footer : false,
6363     
6364     getAutoCreate : function()
6365     {
6366         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6367         
6368         cfg = {
6369             tag: 'table',
6370             cls : 'table',
6371             cn : []
6372         };
6373         if (this.scrollBody) {
6374             cfg.cls += ' table-body-fixed';
6375         }    
6376         if (this.striped) {
6377             cfg.cls += ' table-striped';
6378         }
6379         
6380         if (this.hover) {
6381             cfg.cls += ' table-hover';
6382         }
6383         if (this.bordered) {
6384             cfg.cls += ' table-bordered';
6385         }
6386         if (this.condensed) {
6387             cfg.cls += ' table-condensed';
6388         }
6389         if (this.responsive) {
6390             cfg.cls += ' table-responsive';
6391         }
6392         
6393         if (this.cls) {
6394             cfg.cls+=  ' ' +this.cls;
6395         }
6396         
6397         // this lot should be simplifed...
6398         var _t = this;
6399         var cp = [
6400             'align',
6401             'bgcolor',
6402             'border',
6403             'cellpadding',
6404             'cellspacing',
6405             'frame',
6406             'rules',
6407             'sortable',
6408             'summary',
6409             'width'
6410         ].forEach(function(k) {
6411             if (_t[k]) {
6412                 cfg[k] = _t[k];
6413             }
6414         });
6415         
6416         
6417         if (this.layout) {
6418             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6419         }
6420         
6421         if(this.store || this.cm){
6422             if(this.headerShow){
6423                 cfg.cn.push(this.renderHeader());
6424             }
6425             
6426             cfg.cn.push(this.renderBody());
6427             
6428             if(this.footerShow){
6429                 cfg.cn.push(this.renderFooter());
6430             }
6431             // where does this come from?
6432             //cfg.cls+=  ' TableGrid';
6433         }
6434         
6435         return { cn : [ cfg ] };
6436     },
6437     
6438     initEvents : function()
6439     {   
6440         if(!this.store || !this.cm){
6441             return;
6442         }
6443         if (this.selModel) {
6444             this.selModel.initEvents();
6445         }
6446         
6447         
6448         //Roo.log('initEvents with ds!!!!');
6449         
6450         this.mainBody = this.el.select('tbody', true).first();
6451         this.mainHead = this.el.select('thead', true).first();
6452         this.mainFoot = this.el.select('tfoot', true).first();
6453         
6454         
6455         
6456         var _this = this;
6457         
6458         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6459             e.on('click', _this.sort, _this);
6460         });
6461         
6462         this.mainBody.on("click", this.onClick, this);
6463         this.mainBody.on("dblclick", this.onDblClick, this);
6464         
6465         // why is this done????? = it breaks dialogs??
6466         //this.parent().el.setStyle('position', 'relative');
6467         
6468         
6469         if (this.footer) {
6470             this.footer.parentId = this.id;
6471             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6472             
6473             if(this.lazyLoad){
6474                 this.el.select('tfoot tr td').first().addClass('hide');
6475             }
6476         } 
6477         
6478         if(this.loadMask) {
6479             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6480         }
6481         
6482         this.store.on('load', this.onLoad, this);
6483         this.store.on('beforeload', this.onBeforeLoad, this);
6484         this.store.on('update', this.onUpdate, this);
6485         this.store.on('add', this.onAdd, this);
6486         this.store.on("clear", this.clear, this);
6487         
6488         this.el.on("contextmenu", this.onContextMenu, this);
6489         
6490         this.mainBody.on('scroll', this.onBodyScroll, this);
6491         
6492         this.cm.on("headerchange", this.onHeaderChange, this);
6493         
6494         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6495         
6496     },
6497     
6498     onContextMenu : function(e, t)
6499     {
6500         this.processEvent("contextmenu", e);
6501     },
6502     
6503     processEvent : function(name, e)
6504     {
6505         if (name != 'touchstart' ) {
6506             this.fireEvent(name, e);    
6507         }
6508         
6509         var t = e.getTarget();
6510         
6511         var cell = Roo.get(t);
6512         
6513         if(!cell){
6514             return;
6515         }
6516         
6517         if(cell.findParent('tfoot', false, true)){
6518             return;
6519         }
6520         
6521         if(cell.findParent('thead', false, true)){
6522             
6523             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6524                 cell = Roo.get(t).findParent('th', false, true);
6525                 if (!cell) {
6526                     Roo.log("failed to find th in thead?");
6527                     Roo.log(e.getTarget());
6528                     return;
6529                 }
6530             }
6531             
6532             var cellIndex = cell.dom.cellIndex;
6533             
6534             var ename = name == 'touchstart' ? 'click' : name;
6535             this.fireEvent("header" + ename, this, cellIndex, e);
6536             
6537             return;
6538         }
6539         
6540         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6541             cell = Roo.get(t).findParent('td', false, true);
6542             if (!cell) {
6543                 Roo.log("failed to find th in tbody?");
6544                 Roo.log(e.getTarget());
6545                 return;
6546             }
6547         }
6548         
6549         var row = cell.findParent('tr', false, true);
6550         var cellIndex = cell.dom.cellIndex;
6551         var rowIndex = row.dom.rowIndex - 1;
6552         
6553         if(row !== false){
6554             
6555             this.fireEvent("row" + name, this, rowIndex, e);
6556             
6557             if(cell !== false){
6558             
6559                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6560             }
6561         }
6562         
6563     },
6564     
6565     onMouseover : function(e, el)
6566     {
6567         var cell = Roo.get(el);
6568         
6569         if(!cell){
6570             return;
6571         }
6572         
6573         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6574             cell = cell.findParent('td', false, true);
6575         }
6576         
6577         var row = cell.findParent('tr', false, true);
6578         var cellIndex = cell.dom.cellIndex;
6579         var rowIndex = row.dom.rowIndex - 1; // start from 0
6580         
6581         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6582         
6583     },
6584     
6585     onMouseout : function(e, el)
6586     {
6587         var cell = Roo.get(el);
6588         
6589         if(!cell){
6590             return;
6591         }
6592         
6593         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6594             cell = cell.findParent('td', false, true);
6595         }
6596         
6597         var row = cell.findParent('tr', false, true);
6598         var cellIndex = cell.dom.cellIndex;
6599         var rowIndex = row.dom.rowIndex - 1; // start from 0
6600         
6601         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6602         
6603     },
6604     
6605     onClick : function(e, el)
6606     {
6607         var cell = Roo.get(el);
6608         
6609         if(!cell || (!this.cellSelection && !this.rowSelection)){
6610             return;
6611         }
6612         
6613         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6614             cell = cell.findParent('td', false, true);
6615         }
6616         
6617         if(!cell || typeof(cell) == 'undefined'){
6618             return;
6619         }
6620         
6621         var row = cell.findParent('tr', false, true);
6622         
6623         if(!row || typeof(row) == 'undefined'){
6624             return;
6625         }
6626         
6627         var cellIndex = cell.dom.cellIndex;
6628         var rowIndex = this.getRowIndex(row);
6629         
6630         // why??? - should these not be based on SelectionModel?
6631         if(this.cellSelection){
6632             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6633         }
6634         
6635         if(this.rowSelection){
6636             this.fireEvent('rowclick', this, row, rowIndex, e);
6637         }
6638         
6639         
6640     },
6641         
6642     onDblClick : function(e,el)
6643     {
6644         var cell = Roo.get(el);
6645         
6646         if(!cell || (!this.cellSelection && !this.rowSelection)){
6647             return;
6648         }
6649         
6650         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6651             cell = cell.findParent('td', false, true);
6652         }
6653         
6654         if(!cell || typeof(cell) == 'undefined'){
6655             return;
6656         }
6657         
6658         var row = cell.findParent('tr', false, true);
6659         
6660         if(!row || typeof(row) == 'undefined'){
6661             return;
6662         }
6663         
6664         var cellIndex = cell.dom.cellIndex;
6665         var rowIndex = this.getRowIndex(row);
6666         
6667         if(this.cellSelection){
6668             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6669         }
6670         
6671         if(this.rowSelection){
6672             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6673         }
6674     },
6675     
6676     sort : function(e,el)
6677     {
6678         var col = Roo.get(el);
6679         
6680         if(!col.hasClass('sortable')){
6681             return;
6682         }
6683         
6684         var sort = col.attr('sort');
6685         var dir = 'ASC';
6686         
6687         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6688             dir = 'DESC';
6689         }
6690         
6691         this.store.sortInfo = {field : sort, direction : dir};
6692         
6693         if (this.footer) {
6694             Roo.log("calling footer first");
6695             this.footer.onClick('first');
6696         } else {
6697         
6698             this.store.load({ params : { start : 0 } });
6699         }
6700     },
6701     
6702     renderHeader : function()
6703     {
6704         var header = {
6705             tag: 'thead',
6706             cn : []
6707         };
6708         
6709         var cm = this.cm;
6710         this.totalWidth = 0;
6711         
6712         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6713             
6714             var config = cm.config[i];
6715             
6716             var c = {
6717                 tag: 'th',
6718                 cls : 'x-hcol-' + i,
6719                 style : '',
6720                 html: cm.getColumnHeader(i)
6721             };
6722             
6723             var hh = '';
6724             
6725             if(typeof(config.sortable) != 'undefined' && config.sortable){
6726                 c.cls = 'sortable';
6727                 c.html = '<i class="glyphicon"></i>' + c.html;
6728             }
6729             
6730             if(typeof(config.lgHeader) != 'undefined'){
6731                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6732             }
6733             
6734             if(typeof(config.mdHeader) != 'undefined'){
6735                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6736             }
6737             
6738             if(typeof(config.smHeader) != 'undefined'){
6739                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6740             }
6741             
6742             if(typeof(config.xsHeader) != 'undefined'){
6743                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6744             }
6745             
6746             if(hh.length){
6747                 c.html = hh;
6748             }
6749             
6750             if(typeof(config.tooltip) != 'undefined'){
6751                 c.tooltip = config.tooltip;
6752             }
6753             
6754             if(typeof(config.colspan) != 'undefined'){
6755                 c.colspan = config.colspan;
6756             }
6757             
6758             if(typeof(config.hidden) != 'undefined' && config.hidden){
6759                 c.style += ' display:none;';
6760             }
6761             
6762             if(typeof(config.dataIndex) != 'undefined'){
6763                 c.sort = config.dataIndex;
6764             }
6765             
6766            
6767             
6768             if(typeof(config.align) != 'undefined' && config.align.length){
6769                 c.style += ' text-align:' + config.align + ';';
6770             }
6771             
6772             if(typeof(config.width) != 'undefined'){
6773                 c.style += ' width:' + config.width + 'px;';
6774                 this.totalWidth += config.width;
6775             } else {
6776                 this.totalWidth += 100; // assume minimum of 100 per column?
6777             }
6778             
6779             if(typeof(config.cls) != 'undefined'){
6780                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6781             }
6782             
6783             ['xs','sm','md','lg'].map(function(size){
6784                 
6785                 if(typeof(config[size]) == 'undefined'){
6786                     return;
6787                 }
6788                 
6789                 if (!config[size]) { // 0 = hidden
6790                     c.cls += ' hidden-' + size;
6791                     return;
6792                 }
6793                 
6794                 c.cls += ' col-' + size + '-' + config[size];
6795
6796             });
6797             
6798             header.cn.push(c)
6799         }
6800         
6801         return header;
6802     },
6803     
6804     renderBody : function()
6805     {
6806         var body = {
6807             tag: 'tbody',
6808             cn : [
6809                 {
6810                     tag: 'tr',
6811                     cn : [
6812                         {
6813                             tag : 'td',
6814                             colspan :  this.cm.getColumnCount()
6815                         }
6816                     ]
6817                 }
6818             ]
6819         };
6820         
6821         return body;
6822     },
6823     
6824     renderFooter : function()
6825     {
6826         var footer = {
6827             tag: 'tfoot',
6828             cn : [
6829                 {
6830                     tag: 'tr',
6831                     cn : [
6832                         {
6833                             tag : 'td',
6834                             colspan :  this.cm.getColumnCount()
6835                         }
6836                     ]
6837                 }
6838             ]
6839         };
6840         
6841         return footer;
6842     },
6843     
6844     
6845     
6846     onLoad : function()
6847     {
6848 //        Roo.log('ds onload');
6849         this.clear();
6850         
6851         var _this = this;
6852         var cm = this.cm;
6853         var ds = this.store;
6854         
6855         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6856             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6857             if (_this.store.sortInfo) {
6858                     
6859                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6860                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6861                 }
6862                 
6863                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6864                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6865                 }
6866             }
6867         });
6868         
6869         var tbody =  this.mainBody;
6870               
6871         if(ds.getCount() > 0){
6872             ds.data.each(function(d,rowIndex){
6873                 var row =  this.renderRow(cm, ds, rowIndex);
6874                 
6875                 tbody.createChild(row);
6876                 
6877                 var _this = this;
6878                 
6879                 if(row.cellObjects.length){
6880                     Roo.each(row.cellObjects, function(r){
6881                         _this.renderCellObject(r);
6882                     })
6883                 }
6884                 
6885             }, this);
6886         }
6887         
6888         var tfoot = this.el.select('tfoot', true).first();
6889         
6890         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6891             
6892             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6893             
6894             var total = this.ds.getTotalCount();
6895             
6896             if(this.footer.pageSize < total){
6897                 this.mainFoot.show();
6898             }
6899         }
6900         
6901         Roo.each(this.el.select('tbody td', true).elements, function(e){
6902             e.on('mouseover', _this.onMouseover, _this);
6903         });
6904         
6905         Roo.each(this.el.select('tbody td', true).elements, function(e){
6906             e.on('mouseout', _this.onMouseout, _this);
6907         });
6908         this.fireEvent('rowsrendered', this);
6909         
6910         this.autoSize();
6911     },
6912     
6913     
6914     onUpdate : function(ds,record)
6915     {
6916         this.refreshRow(record);
6917         this.autoSize();
6918     },
6919     
6920     onRemove : function(ds, record, index, isUpdate){
6921         if(isUpdate !== true){
6922             this.fireEvent("beforerowremoved", this, index, record);
6923         }
6924         var bt = this.mainBody.dom;
6925         
6926         var rows = this.el.select('tbody > tr', true).elements;
6927         
6928         if(typeof(rows[index]) != 'undefined'){
6929             bt.removeChild(rows[index].dom);
6930         }
6931         
6932 //        if(bt.rows[index]){
6933 //            bt.removeChild(bt.rows[index]);
6934 //        }
6935         
6936         if(isUpdate !== true){
6937             //this.stripeRows(index);
6938             //this.syncRowHeights(index, index);
6939             //this.layout();
6940             this.fireEvent("rowremoved", this, index, record);
6941         }
6942     },
6943     
6944     onAdd : function(ds, records, rowIndex)
6945     {
6946         //Roo.log('on Add called');
6947         // - note this does not handle multiple adding very well..
6948         var bt = this.mainBody.dom;
6949         for (var i =0 ; i < records.length;i++) {
6950             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6951             //Roo.log(records[i]);
6952             //Roo.log(this.store.getAt(rowIndex+i));
6953             this.insertRow(this.store, rowIndex + i, false);
6954             return;
6955         }
6956         
6957     },
6958     
6959     
6960     refreshRow : function(record){
6961         var ds = this.store, index;
6962         if(typeof record == 'number'){
6963             index = record;
6964             record = ds.getAt(index);
6965         }else{
6966             index = ds.indexOf(record);
6967         }
6968         this.insertRow(ds, index, true);
6969         this.autoSize();
6970         this.onRemove(ds, record, index+1, true);
6971         this.autoSize();
6972         //this.syncRowHeights(index, index);
6973         //this.layout();
6974         this.fireEvent("rowupdated", this, index, record);
6975     },
6976     
6977     insertRow : function(dm, rowIndex, isUpdate){
6978         
6979         if(!isUpdate){
6980             this.fireEvent("beforerowsinserted", this, rowIndex);
6981         }
6982             //var s = this.getScrollState();
6983         var row = this.renderRow(this.cm, this.store, rowIndex);
6984         // insert before rowIndex..
6985         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6986         
6987         var _this = this;
6988                 
6989         if(row.cellObjects.length){
6990             Roo.each(row.cellObjects, function(r){
6991                 _this.renderCellObject(r);
6992             })
6993         }
6994             
6995         if(!isUpdate){
6996             this.fireEvent("rowsinserted", this, rowIndex);
6997             //this.syncRowHeights(firstRow, lastRow);
6998             //this.stripeRows(firstRow);
6999             //this.layout();
7000         }
7001         
7002     },
7003     
7004     
7005     getRowDom : function(rowIndex)
7006     {
7007         var rows = this.el.select('tbody > tr', true).elements;
7008         
7009         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7010         
7011     },
7012     // returns the object tree for a tr..
7013   
7014     
7015     renderRow : function(cm, ds, rowIndex) 
7016     {
7017         var d = ds.getAt(rowIndex);
7018         
7019         var row = {
7020             tag : 'tr',
7021             cls : 'x-row-' + rowIndex,
7022             cn : []
7023         };
7024             
7025         var cellObjects = [];
7026         
7027         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7028             var config = cm.config[i];
7029             
7030             var renderer = cm.getRenderer(i);
7031             var value = '';
7032             var id = false;
7033             
7034             if(typeof(renderer) !== 'undefined'){
7035                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7036             }
7037             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7038             // and are rendered into the cells after the row is rendered - using the id for the element.
7039             
7040             if(typeof(value) === 'object'){
7041                 id = Roo.id();
7042                 cellObjects.push({
7043                     container : id,
7044                     cfg : value 
7045                 })
7046             }
7047             
7048             var rowcfg = {
7049                 record: d,
7050                 rowIndex : rowIndex,
7051                 colIndex : i,
7052                 rowClass : ''
7053             };
7054
7055             this.fireEvent('rowclass', this, rowcfg);
7056             
7057             var td = {
7058                 tag: 'td',
7059                 cls : rowcfg.rowClass + ' x-col-' + i,
7060                 style: '',
7061                 html: (typeof(value) === 'object') ? '' : value
7062             };
7063             
7064             if (id) {
7065                 td.id = id;
7066             }
7067             
7068             if(typeof(config.colspan) != 'undefined'){
7069                 td.colspan = config.colspan;
7070             }
7071             
7072             if(typeof(config.hidden) != 'undefined' && config.hidden){
7073                 td.style += ' display:none;';
7074             }
7075             
7076             if(typeof(config.align) != 'undefined' && config.align.length){
7077                 td.style += ' text-align:' + config.align + ';';
7078             }
7079             if(typeof(config.valign) != 'undefined' && config.valign.length){
7080                 td.style += ' vertical-align:' + config.valign + ';';
7081             }
7082             
7083             if(typeof(config.width) != 'undefined'){
7084                 td.style += ' width:' +  config.width + 'px;';
7085             }
7086             
7087             if(typeof(config.cursor) != 'undefined'){
7088                 td.style += ' cursor:' +  config.cursor + ';';
7089             }
7090             
7091             if(typeof(config.cls) != 'undefined'){
7092                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7093             }
7094             
7095             ['xs','sm','md','lg'].map(function(size){
7096                 
7097                 if(typeof(config[size]) == 'undefined'){
7098                     return;
7099                 }
7100                 
7101                 if (!config[size]) { // 0 = hidden
7102                     td.cls += ' hidden-' + size;
7103                     return;
7104                 }
7105                 
7106                 td.cls += ' col-' + size + '-' + config[size];
7107
7108             });
7109             
7110             row.cn.push(td);
7111            
7112         }
7113         
7114         row.cellObjects = cellObjects;
7115         
7116         return row;
7117           
7118     },
7119     
7120     
7121     
7122     onBeforeLoad : function()
7123     {
7124         
7125     },
7126      /**
7127      * Remove all rows
7128      */
7129     clear : function()
7130     {
7131         this.el.select('tbody', true).first().dom.innerHTML = '';
7132     },
7133     /**
7134      * Show or hide a row.
7135      * @param {Number} rowIndex to show or hide
7136      * @param {Boolean} state hide
7137      */
7138     setRowVisibility : function(rowIndex, state)
7139     {
7140         var bt = this.mainBody.dom;
7141         
7142         var rows = this.el.select('tbody > tr', true).elements;
7143         
7144         if(typeof(rows[rowIndex]) == 'undefined'){
7145             return;
7146         }
7147         rows[rowIndex].dom.style.display = state ? '' : 'none';
7148     },
7149     
7150     
7151     getSelectionModel : function(){
7152         if(!this.selModel){
7153             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7154         }
7155         return this.selModel;
7156     },
7157     /*
7158      * Render the Roo.bootstrap object from renderder
7159      */
7160     renderCellObject : function(r)
7161     {
7162         var _this = this;
7163         
7164         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7165         
7166         var t = r.cfg.render(r.container);
7167         
7168         if(r.cfg.cn){
7169             Roo.each(r.cfg.cn, function(c){
7170                 var child = {
7171                     container: t.getChildContainer(),
7172                     cfg: c
7173                 };
7174                 _this.renderCellObject(child);
7175             })
7176         }
7177     },
7178     
7179     getRowIndex : function(row)
7180     {
7181         var rowIndex = -1;
7182         
7183         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7184             if(el != row){
7185                 return;
7186             }
7187             
7188             rowIndex = index;
7189         });
7190         
7191         return rowIndex;
7192     },
7193      /**
7194      * Returns the grid's underlying element = used by panel.Grid
7195      * @return {Element} The element
7196      */
7197     getGridEl : function(){
7198         return this.el;
7199     },
7200      /**
7201      * Forces a resize - used by panel.Grid
7202      * @return {Element} The element
7203      */
7204     autoSize : function()
7205     {
7206         //var ctr = Roo.get(this.container.dom.parentElement);
7207         var ctr = Roo.get(this.el.dom);
7208         
7209         var thd = this.getGridEl().select('thead',true).first();
7210         var tbd = this.getGridEl().select('tbody', true).first();
7211         var tfd = this.getGridEl().select('tfoot', true).first();
7212         
7213         var cw = ctr.getWidth();
7214         
7215         if (tbd) {
7216             
7217             tbd.setSize(ctr.getWidth(),
7218                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7219             );
7220             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7221             cw -= barsize;
7222         }
7223         cw = Math.max(cw, this.totalWidth);
7224         this.getGridEl().select('tr',true).setWidth(cw);
7225         // resize 'expandable coloumn?
7226         
7227         return; // we doe not have a view in this design..
7228         
7229     },
7230     onBodyScroll: function()
7231     {
7232         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7233         if(this.mainHead){
7234             this.mainHead.setStyle({
7235                 'position' : 'relative',
7236                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7237             });
7238         }
7239         
7240         if(this.lazyLoad){
7241             
7242             var scrollHeight = this.mainBody.dom.scrollHeight;
7243             
7244             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7245             
7246             var height = this.mainBody.getHeight();
7247             
7248             if(scrollHeight - height == scrollTop) {
7249                 
7250                 var total = this.ds.getTotalCount();
7251                 
7252                 if(this.footer.cursor + this.footer.pageSize < total){
7253                     
7254                     this.footer.ds.load({
7255                         params : {
7256                             start : this.footer.cursor + this.footer.pageSize,
7257                             limit : this.footer.pageSize
7258                         },
7259                         add : true
7260                     });
7261                 }
7262             }
7263             
7264         }
7265     },
7266     
7267     onHeaderChange : function()
7268     {
7269         var header = this.renderHeader();
7270         var table = this.el.select('table', true).first();
7271         
7272         this.mainHead.remove();
7273         this.mainHead = table.createChild(header, this.mainBody, false);
7274     },
7275     
7276     onHiddenChange : function(colModel, colIndex, hidden)
7277     {
7278         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7279         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7280         
7281         this.CSS.updateRule(thSelector, "display", "");
7282         this.CSS.updateRule(tdSelector, "display", "");
7283         
7284         if(hidden){
7285             this.CSS.updateRule(thSelector, "display", "none");
7286             this.CSS.updateRule(tdSelector, "display", "none");
7287         }
7288         
7289         this.onHeaderChange();
7290         this.onLoad();
7291     },
7292     
7293     setColumnWidth: function(col_index, width)
7294     {
7295         // width = "md-2 xs-2..."
7296         if(!this.colModel.config[col_index]) {
7297             return;
7298         }
7299         
7300         var w = width.split(" ");
7301         
7302         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7303         
7304         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7305         
7306         
7307         for(var j = 0; j < w.length; j++) {
7308             
7309             if(!w[j]) {
7310                 continue;
7311             }
7312             
7313             var size_cls = w[j].split("-");
7314             
7315             if(!Number.isInteger(size_cls[1] * 1)) {
7316                 continue;
7317             }
7318             
7319             if(!this.colModel.config[col_index][size_cls[0]]) {
7320                 continue;
7321             }
7322             
7323             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7324                 continue;
7325             }
7326             
7327             h_row[0].classList.replace(
7328                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7329                 "col-"+size_cls[0]+"-"+size_cls[1]
7330             );
7331             
7332             for(var i = 0; i < rows.length; i++) {
7333                 
7334                 var size_cls = w[j].split("-");
7335                 
7336                 if(!Number.isInteger(size_cls[1] * 1)) {
7337                     continue;
7338                 }
7339                 
7340                 if(!this.colModel.config[col_index][size_cls[0]]) {
7341                     continue;
7342                 }
7343                 
7344                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7345                     continue;
7346                 }
7347                 
7348                 rows[i].classList.replace(
7349                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7350                     "col-"+size_cls[0]+"-"+size_cls[1]
7351                 );
7352             }
7353             
7354             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7355         }
7356     }
7357 });
7358
7359  
7360
7361  /*
7362  * - LGPL
7363  *
7364  * table cell
7365  * 
7366  */
7367
7368 /**
7369  * @class Roo.bootstrap.TableCell
7370  * @extends Roo.bootstrap.Component
7371  * Bootstrap TableCell class
7372  * @cfg {String} html cell contain text
7373  * @cfg {String} cls cell class
7374  * @cfg {String} tag cell tag (td|th) default td
7375  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7376  * @cfg {String} align Aligns the content in a cell
7377  * @cfg {String} axis Categorizes cells
7378  * @cfg {String} bgcolor Specifies the background color of a cell
7379  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7380  * @cfg {Number} colspan Specifies the number of columns a cell should span
7381  * @cfg {String} headers Specifies one or more header cells a cell is related to
7382  * @cfg {Number} height Sets the height of a cell
7383  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7384  * @cfg {Number} rowspan Sets the number of rows a cell should span
7385  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7386  * @cfg {String} valign Vertical aligns the content in a cell
7387  * @cfg {Number} width Specifies the width of a cell
7388  * 
7389  * @constructor
7390  * Create a new TableCell
7391  * @param {Object} config The config object
7392  */
7393
7394 Roo.bootstrap.TableCell = function(config){
7395     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7396 };
7397
7398 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7399     
7400     html: false,
7401     cls: false,
7402     tag: false,
7403     abbr: false,
7404     align: false,
7405     axis: false,
7406     bgcolor: false,
7407     charoff: false,
7408     colspan: false,
7409     headers: false,
7410     height: false,
7411     nowrap: false,
7412     rowspan: false,
7413     scope: false,
7414     valign: false,
7415     width: false,
7416     
7417     
7418     getAutoCreate : function(){
7419         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7420         
7421         cfg = {
7422             tag: 'td'
7423         };
7424         
7425         if(this.tag){
7426             cfg.tag = this.tag;
7427         }
7428         
7429         if (this.html) {
7430             cfg.html=this.html
7431         }
7432         if (this.cls) {
7433             cfg.cls=this.cls
7434         }
7435         if (this.abbr) {
7436             cfg.abbr=this.abbr
7437         }
7438         if (this.align) {
7439             cfg.align=this.align
7440         }
7441         if (this.axis) {
7442             cfg.axis=this.axis
7443         }
7444         if (this.bgcolor) {
7445             cfg.bgcolor=this.bgcolor
7446         }
7447         if (this.charoff) {
7448             cfg.charoff=this.charoff
7449         }
7450         if (this.colspan) {
7451             cfg.colspan=this.colspan
7452         }
7453         if (this.headers) {
7454             cfg.headers=this.headers
7455         }
7456         if (this.height) {
7457             cfg.height=this.height
7458         }
7459         if (this.nowrap) {
7460             cfg.nowrap=this.nowrap
7461         }
7462         if (this.rowspan) {
7463             cfg.rowspan=this.rowspan
7464         }
7465         if (this.scope) {
7466             cfg.scope=this.scope
7467         }
7468         if (this.valign) {
7469             cfg.valign=this.valign
7470         }
7471         if (this.width) {
7472             cfg.width=this.width
7473         }
7474         
7475         
7476         return cfg;
7477     }
7478    
7479 });
7480
7481  
7482
7483  /*
7484  * - LGPL
7485  *
7486  * table row
7487  * 
7488  */
7489
7490 /**
7491  * @class Roo.bootstrap.TableRow
7492  * @extends Roo.bootstrap.Component
7493  * Bootstrap TableRow class
7494  * @cfg {String} cls row class
7495  * @cfg {String} align Aligns the content in a table row
7496  * @cfg {String} bgcolor Specifies a background color for a table row
7497  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7498  * @cfg {String} valign Vertical aligns the content in a table row
7499  * 
7500  * @constructor
7501  * Create a new TableRow
7502  * @param {Object} config The config object
7503  */
7504
7505 Roo.bootstrap.TableRow = function(config){
7506     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7507 };
7508
7509 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7510     
7511     cls: false,
7512     align: false,
7513     bgcolor: false,
7514     charoff: false,
7515     valign: false,
7516     
7517     getAutoCreate : function(){
7518         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7519         
7520         cfg = {
7521             tag: 'tr'
7522         };
7523             
7524         if(this.cls){
7525             cfg.cls = this.cls;
7526         }
7527         if(this.align){
7528             cfg.align = this.align;
7529         }
7530         if(this.bgcolor){
7531             cfg.bgcolor = this.bgcolor;
7532         }
7533         if(this.charoff){
7534             cfg.charoff = this.charoff;
7535         }
7536         if(this.valign){
7537             cfg.valign = this.valign;
7538         }
7539         
7540         return cfg;
7541     }
7542    
7543 });
7544
7545  
7546
7547  /*
7548  * - LGPL
7549  *
7550  * table body
7551  * 
7552  */
7553
7554 /**
7555  * @class Roo.bootstrap.TableBody
7556  * @extends Roo.bootstrap.Component
7557  * Bootstrap TableBody class
7558  * @cfg {String} cls element class
7559  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7560  * @cfg {String} align Aligns the content inside the element
7561  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7562  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7563  * 
7564  * @constructor
7565  * Create a new TableBody
7566  * @param {Object} config The config object
7567  */
7568
7569 Roo.bootstrap.TableBody = function(config){
7570     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7571 };
7572
7573 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7574     
7575     cls: false,
7576     tag: false,
7577     align: false,
7578     charoff: false,
7579     valign: false,
7580     
7581     getAutoCreate : function(){
7582         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7583         
7584         cfg = {
7585             tag: 'tbody'
7586         };
7587             
7588         if (this.cls) {
7589             cfg.cls=this.cls
7590         }
7591         if(this.tag){
7592             cfg.tag = this.tag;
7593         }
7594         
7595         if(this.align){
7596             cfg.align = this.align;
7597         }
7598         if(this.charoff){
7599             cfg.charoff = this.charoff;
7600         }
7601         if(this.valign){
7602             cfg.valign = this.valign;
7603         }
7604         
7605         return cfg;
7606     }
7607     
7608     
7609 //    initEvents : function()
7610 //    {
7611 //        
7612 //        if(!this.store){
7613 //            return;
7614 //        }
7615 //        
7616 //        this.store = Roo.factory(this.store, Roo.data);
7617 //        this.store.on('load', this.onLoad, this);
7618 //        
7619 //        this.store.load();
7620 //        
7621 //    },
7622 //    
7623 //    onLoad: function () 
7624 //    {   
7625 //        this.fireEvent('load', this);
7626 //    }
7627 //    
7628 //   
7629 });
7630
7631  
7632
7633  /*
7634  * Based on:
7635  * Ext JS Library 1.1.1
7636  * Copyright(c) 2006-2007, Ext JS, LLC.
7637  *
7638  * Originally Released Under LGPL - original licence link has changed is not relivant.
7639  *
7640  * Fork - LGPL
7641  * <script type="text/javascript">
7642  */
7643
7644 // as we use this in bootstrap.
7645 Roo.namespace('Roo.form');
7646  /**
7647  * @class Roo.form.Action
7648  * Internal Class used to handle form actions
7649  * @constructor
7650  * @param {Roo.form.BasicForm} el The form element or its id
7651  * @param {Object} config Configuration options
7652  */
7653
7654  
7655  
7656 // define the action interface
7657 Roo.form.Action = function(form, options){
7658     this.form = form;
7659     this.options = options || {};
7660 };
7661 /**
7662  * Client Validation Failed
7663  * @const 
7664  */
7665 Roo.form.Action.CLIENT_INVALID = 'client';
7666 /**
7667  * Server Validation Failed
7668  * @const 
7669  */
7670 Roo.form.Action.SERVER_INVALID = 'server';
7671  /**
7672  * Connect to Server Failed
7673  * @const 
7674  */
7675 Roo.form.Action.CONNECT_FAILURE = 'connect';
7676 /**
7677  * Reading Data from Server Failed
7678  * @const 
7679  */
7680 Roo.form.Action.LOAD_FAILURE = 'load';
7681
7682 Roo.form.Action.prototype = {
7683     type : 'default',
7684     failureType : undefined,
7685     response : undefined,
7686     result : undefined,
7687
7688     // interface method
7689     run : function(options){
7690
7691     },
7692
7693     // interface method
7694     success : function(response){
7695
7696     },
7697
7698     // interface method
7699     handleResponse : function(response){
7700
7701     },
7702
7703     // default connection failure
7704     failure : function(response){
7705         
7706         this.response = response;
7707         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7708         this.form.afterAction(this, false);
7709     },
7710
7711     processResponse : function(response){
7712         this.response = response;
7713         if(!response.responseText){
7714             return true;
7715         }
7716         this.result = this.handleResponse(response);
7717         return this.result;
7718     },
7719
7720     // utility functions used internally
7721     getUrl : function(appendParams){
7722         var url = this.options.url || this.form.url || this.form.el.dom.action;
7723         if(appendParams){
7724             var p = this.getParams();
7725             if(p){
7726                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7727             }
7728         }
7729         return url;
7730     },
7731
7732     getMethod : function(){
7733         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7734     },
7735
7736     getParams : function(){
7737         var bp = this.form.baseParams;
7738         var p = this.options.params;
7739         if(p){
7740             if(typeof p == "object"){
7741                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7742             }else if(typeof p == 'string' && bp){
7743                 p += '&' + Roo.urlEncode(bp);
7744             }
7745         }else if(bp){
7746             p = Roo.urlEncode(bp);
7747         }
7748         return p;
7749     },
7750
7751     createCallback : function(){
7752         return {
7753             success: this.success,
7754             failure: this.failure,
7755             scope: this,
7756             timeout: (this.form.timeout*1000),
7757             upload: this.form.fileUpload ? this.success : undefined
7758         };
7759     }
7760 };
7761
7762 Roo.form.Action.Submit = function(form, options){
7763     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7764 };
7765
7766 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7767     type : 'submit',
7768
7769     haveProgress : false,
7770     uploadComplete : false,
7771     
7772     // uploadProgress indicator.
7773     uploadProgress : function()
7774     {
7775         if (!this.form.progressUrl) {
7776             return;
7777         }
7778         
7779         if (!this.haveProgress) {
7780             Roo.MessageBox.progress("Uploading", "Uploading");
7781         }
7782         if (this.uploadComplete) {
7783            Roo.MessageBox.hide();
7784            return;
7785         }
7786         
7787         this.haveProgress = true;
7788    
7789         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7790         
7791         var c = new Roo.data.Connection();
7792         c.request({
7793             url : this.form.progressUrl,
7794             params: {
7795                 id : uid
7796             },
7797             method: 'GET',
7798             success : function(req){
7799                //console.log(data);
7800                 var rdata = false;
7801                 var edata;
7802                 try  {
7803                    rdata = Roo.decode(req.responseText)
7804                 } catch (e) {
7805                     Roo.log("Invalid data from server..");
7806                     Roo.log(edata);
7807                     return;
7808                 }
7809                 if (!rdata || !rdata.success) {
7810                     Roo.log(rdata);
7811                     Roo.MessageBox.alert(Roo.encode(rdata));
7812                     return;
7813                 }
7814                 var data = rdata.data;
7815                 
7816                 if (this.uploadComplete) {
7817                    Roo.MessageBox.hide();
7818                    return;
7819                 }
7820                    
7821                 if (data){
7822                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7823                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7824                     );
7825                 }
7826                 this.uploadProgress.defer(2000,this);
7827             },
7828        
7829             failure: function(data) {
7830                 Roo.log('progress url failed ');
7831                 Roo.log(data);
7832             },
7833             scope : this
7834         });
7835            
7836     },
7837     
7838     
7839     run : function()
7840     {
7841         // run get Values on the form, so it syncs any secondary forms.
7842         this.form.getValues();
7843         
7844         var o = this.options;
7845         var method = this.getMethod();
7846         var isPost = method == 'POST';
7847         if(o.clientValidation === false || this.form.isValid()){
7848             
7849             if (this.form.progressUrl) {
7850                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7851                     (new Date() * 1) + '' + Math.random());
7852                     
7853             } 
7854             
7855             
7856             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7857                 form:this.form.el.dom,
7858                 url:this.getUrl(!isPost),
7859                 method: method,
7860                 params:isPost ? this.getParams() : null,
7861                 isUpload: this.form.fileUpload
7862             }));
7863             
7864             this.uploadProgress();
7865
7866         }else if (o.clientValidation !== false){ // client validation failed
7867             this.failureType = Roo.form.Action.CLIENT_INVALID;
7868             this.form.afterAction(this, false);
7869         }
7870     },
7871
7872     success : function(response)
7873     {
7874         this.uploadComplete= true;
7875         if (this.haveProgress) {
7876             Roo.MessageBox.hide();
7877         }
7878         
7879         
7880         var result = this.processResponse(response);
7881         if(result === true || result.success){
7882             this.form.afterAction(this, true);
7883             return;
7884         }
7885         if(result.errors){
7886             this.form.markInvalid(result.errors);
7887             this.failureType = Roo.form.Action.SERVER_INVALID;
7888         }
7889         this.form.afterAction(this, false);
7890     },
7891     failure : function(response)
7892     {
7893         this.uploadComplete= true;
7894         if (this.haveProgress) {
7895             Roo.MessageBox.hide();
7896         }
7897         
7898         this.response = response;
7899         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7900         this.form.afterAction(this, false);
7901     },
7902     
7903     handleResponse : function(response){
7904         if(this.form.errorReader){
7905             var rs = this.form.errorReader.read(response);
7906             var errors = [];
7907             if(rs.records){
7908                 for(var i = 0, len = rs.records.length; i < len; i++) {
7909                     var r = rs.records[i];
7910                     errors[i] = r.data;
7911                 }
7912             }
7913             if(errors.length < 1){
7914                 errors = null;
7915             }
7916             return {
7917                 success : rs.success,
7918                 errors : errors
7919             };
7920         }
7921         var ret = false;
7922         try {
7923             ret = Roo.decode(response.responseText);
7924         } catch (e) {
7925             ret = {
7926                 success: false,
7927                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7928                 errors : []
7929             };
7930         }
7931         return ret;
7932         
7933     }
7934 });
7935
7936
7937 Roo.form.Action.Load = function(form, options){
7938     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7939     this.reader = this.form.reader;
7940 };
7941
7942 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7943     type : 'load',
7944
7945     run : function(){
7946         
7947         Roo.Ajax.request(Roo.apply(
7948                 this.createCallback(), {
7949                     method:this.getMethod(),
7950                     url:this.getUrl(false),
7951                     params:this.getParams()
7952         }));
7953     },
7954
7955     success : function(response){
7956         
7957         var result = this.processResponse(response);
7958         if(result === true || !result.success || !result.data){
7959             this.failureType = Roo.form.Action.LOAD_FAILURE;
7960             this.form.afterAction(this, false);
7961             return;
7962         }
7963         this.form.clearInvalid();
7964         this.form.setValues(result.data);
7965         this.form.afterAction(this, true);
7966     },
7967
7968     handleResponse : function(response){
7969         if(this.form.reader){
7970             var rs = this.form.reader.read(response);
7971             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7972             return {
7973                 success : rs.success,
7974                 data : data
7975             };
7976         }
7977         return Roo.decode(response.responseText);
7978     }
7979 });
7980
7981 Roo.form.Action.ACTION_TYPES = {
7982     'load' : Roo.form.Action.Load,
7983     'submit' : Roo.form.Action.Submit
7984 };/*
7985  * - LGPL
7986  *
7987  * form
7988  *
7989  */
7990
7991 /**
7992  * @class Roo.bootstrap.Form
7993  * @extends Roo.bootstrap.Component
7994  * Bootstrap Form class
7995  * @cfg {String} method  GET | POST (default POST)
7996  * @cfg {String} labelAlign top | left (default top)
7997  * @cfg {String} align left  | right - for navbars
7998  * @cfg {Boolean} loadMask load mask when submit (default true)
7999
8000  *
8001  * @constructor
8002  * Create a new Form
8003  * @param {Object} config The config object
8004  */
8005
8006
8007 Roo.bootstrap.Form = function(config){
8008     
8009     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8010     
8011     Roo.bootstrap.Form.popover.apply();
8012     
8013     this.addEvents({
8014         /**
8015          * @event clientvalidation
8016          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8017          * @param {Form} this
8018          * @param {Boolean} valid true if the form has passed client-side validation
8019          */
8020         clientvalidation: true,
8021         /**
8022          * @event beforeaction
8023          * Fires before any action is performed. Return false to cancel the action.
8024          * @param {Form} this
8025          * @param {Action} action The action to be performed
8026          */
8027         beforeaction: true,
8028         /**
8029          * @event actionfailed
8030          * Fires when an action fails.
8031          * @param {Form} this
8032          * @param {Action} action The action that failed
8033          */
8034         actionfailed : true,
8035         /**
8036          * @event actioncomplete
8037          * Fires when an action is completed.
8038          * @param {Form} this
8039          * @param {Action} action The action that completed
8040          */
8041         actioncomplete : true
8042     });
8043 };
8044
8045 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8046
8047      /**
8048      * @cfg {String} method
8049      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8050      */
8051     method : 'POST',
8052     /**
8053      * @cfg {String} url
8054      * The URL to use for form actions if one isn't supplied in the action options.
8055      */
8056     /**
8057      * @cfg {Boolean} fileUpload
8058      * Set to true if this form is a file upload.
8059      */
8060
8061     /**
8062      * @cfg {Object} baseParams
8063      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8064      */
8065
8066     /**
8067      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8068      */
8069     timeout: 30,
8070     /**
8071      * @cfg {Sting} align (left|right) for navbar forms
8072      */
8073     align : 'left',
8074
8075     // private
8076     activeAction : null,
8077
8078     /**
8079      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8080      * element by passing it or its id or mask the form itself by passing in true.
8081      * @type Mixed
8082      */
8083     waitMsgTarget : false,
8084
8085     loadMask : true,
8086     
8087     /**
8088      * @cfg {Boolean} errorMask (true|false) default false
8089      */
8090     errorMask : false,
8091     
8092     /**
8093      * @cfg {Number} maskOffset Default 100
8094      */
8095     maskOffset : 100,
8096     
8097     /**
8098      * @cfg {Boolean} maskBody
8099      */
8100     maskBody : false,
8101
8102     getAutoCreate : function(){
8103
8104         var cfg = {
8105             tag: 'form',
8106             method : this.method || 'POST',
8107             id : this.id || Roo.id(),
8108             cls : ''
8109         };
8110         if (this.parent().xtype.match(/^Nav/)) {
8111             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8112
8113         }
8114
8115         if (this.labelAlign == 'left' ) {
8116             cfg.cls += ' form-horizontal';
8117         }
8118
8119
8120         return cfg;
8121     },
8122     initEvents : function()
8123     {
8124         this.el.on('submit', this.onSubmit, this);
8125         // this was added as random key presses on the form where triggering form submit.
8126         this.el.on('keypress', function(e) {
8127             if (e.getCharCode() != 13) {
8128                 return true;
8129             }
8130             // we might need to allow it for textareas.. and some other items.
8131             // check e.getTarget().
8132
8133             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8134                 return true;
8135             }
8136
8137             Roo.log("keypress blocked");
8138
8139             e.preventDefault();
8140             return false;
8141         });
8142         
8143     },
8144     // private
8145     onSubmit : function(e){
8146         e.stopEvent();
8147     },
8148
8149      /**
8150      * Returns true if client-side validation on the form is successful.
8151      * @return Boolean
8152      */
8153     isValid : function(){
8154         var items = this.getItems();
8155         var valid = true;
8156         var target = false;
8157         
8158         items.each(function(f){
8159             
8160             if(f.validate()){
8161                 return;
8162             }
8163             
8164             Roo.log('invalid field: ' + f.name);
8165             
8166             valid = false;
8167
8168             if(!target && f.el.isVisible(true)){
8169                 target = f;
8170             }
8171            
8172         });
8173         
8174         if(this.errorMask && !valid){
8175             Roo.bootstrap.Form.popover.mask(this, target);
8176         }
8177         
8178         return valid;
8179     },
8180     
8181     /**
8182      * Returns true if any fields in this form have changed since their original load.
8183      * @return Boolean
8184      */
8185     isDirty : function(){
8186         var dirty = false;
8187         var items = this.getItems();
8188         items.each(function(f){
8189            if(f.isDirty()){
8190                dirty = true;
8191                return false;
8192            }
8193            return true;
8194         });
8195         return dirty;
8196     },
8197      /**
8198      * Performs a predefined action (submit or load) or custom actions you define on this form.
8199      * @param {String} actionName The name of the action type
8200      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8201      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8202      * accept other config options):
8203      * <pre>
8204 Property          Type             Description
8205 ----------------  ---------------  ----------------------------------------------------------------------------------
8206 url               String           The url for the action (defaults to the form's url)
8207 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8208 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8209 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8210                                    validate the form on the client (defaults to false)
8211      * </pre>
8212      * @return {BasicForm} this
8213      */
8214     doAction : function(action, options){
8215         if(typeof action == 'string'){
8216             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8217         }
8218         if(this.fireEvent('beforeaction', this, action) !== false){
8219             this.beforeAction(action);
8220             action.run.defer(100, action);
8221         }
8222         return this;
8223     },
8224
8225     // private
8226     beforeAction : function(action){
8227         var o = action.options;
8228         
8229         if(this.loadMask){
8230             
8231             if(this.maskBody){
8232                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8233             } else {
8234                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8235             }
8236         }
8237         // not really supported yet.. ??
8238
8239         //if(this.waitMsgTarget === true){
8240         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8241         //}else if(this.waitMsgTarget){
8242         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8243         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8244         //}else {
8245         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8246        // }
8247
8248     },
8249
8250     // private
8251     afterAction : function(action, success){
8252         this.activeAction = null;
8253         var o = action.options;
8254
8255         if(this.loadMask){
8256             
8257             if(this.maskBody){
8258                 Roo.get(document.body).unmask();
8259             } else {
8260                 this.el.unmask();
8261             }
8262         }
8263         
8264         //if(this.waitMsgTarget === true){
8265 //            this.el.unmask();
8266         //}else if(this.waitMsgTarget){
8267         //    this.waitMsgTarget.unmask();
8268         //}else{
8269         //    Roo.MessageBox.updateProgress(1);
8270         //    Roo.MessageBox.hide();
8271        // }
8272         //
8273         if(success){
8274             if(o.reset){
8275                 this.reset();
8276             }
8277             Roo.callback(o.success, o.scope, [this, action]);
8278             this.fireEvent('actioncomplete', this, action);
8279
8280         }else{
8281
8282             // failure condition..
8283             // we have a scenario where updates need confirming.
8284             // eg. if a locking scenario exists..
8285             // we look for { errors : { needs_confirm : true }} in the response.
8286             if (
8287                 (typeof(action.result) != 'undefined')  &&
8288                 (typeof(action.result.errors) != 'undefined')  &&
8289                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8290            ){
8291                 var _t = this;
8292                 Roo.log("not supported yet");
8293                  /*
8294
8295                 Roo.MessageBox.confirm(
8296                     "Change requires confirmation",
8297                     action.result.errorMsg,
8298                     function(r) {
8299                         if (r != 'yes') {
8300                             return;
8301                         }
8302                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8303                     }
8304
8305                 );
8306                 */
8307
8308
8309                 return;
8310             }
8311
8312             Roo.callback(o.failure, o.scope, [this, action]);
8313             // show an error message if no failed handler is set..
8314             if (!this.hasListener('actionfailed')) {
8315                 Roo.log("need to add dialog support");
8316                 /*
8317                 Roo.MessageBox.alert("Error",
8318                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8319                         action.result.errorMsg :
8320                         "Saving Failed, please check your entries or try again"
8321                 );
8322                 */
8323             }
8324
8325             this.fireEvent('actionfailed', this, action);
8326         }
8327
8328     },
8329     /**
8330      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8331      * @param {String} id The value to search for
8332      * @return Field
8333      */
8334     findField : function(id){
8335         var items = this.getItems();
8336         var field = items.get(id);
8337         if(!field){
8338              items.each(function(f){
8339                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8340                     field = f;
8341                     return false;
8342                 }
8343                 return true;
8344             });
8345         }
8346         return field || null;
8347     },
8348      /**
8349      * Mark fields in this form invalid in bulk.
8350      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8351      * @return {BasicForm} this
8352      */
8353     markInvalid : function(errors){
8354         if(errors instanceof Array){
8355             for(var i = 0, len = errors.length; i < len; i++){
8356                 var fieldError = errors[i];
8357                 var f = this.findField(fieldError.id);
8358                 if(f){
8359                     f.markInvalid(fieldError.msg);
8360                 }
8361             }
8362         }else{
8363             var field, id;
8364             for(id in errors){
8365                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8366                     field.markInvalid(errors[id]);
8367                 }
8368             }
8369         }
8370         //Roo.each(this.childForms || [], function (f) {
8371         //    f.markInvalid(errors);
8372         //});
8373
8374         return this;
8375     },
8376
8377     /**
8378      * Set values for fields in this form in bulk.
8379      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8380      * @return {BasicForm} this
8381      */
8382     setValues : function(values){
8383         if(values instanceof Array){ // array of objects
8384             for(var i = 0, len = values.length; i < len; i++){
8385                 var v = values[i];
8386                 var f = this.findField(v.id);
8387                 if(f){
8388                     f.setValue(v.value);
8389                     if(this.trackResetOnLoad){
8390                         f.originalValue = f.getValue();
8391                     }
8392                 }
8393             }
8394         }else{ // object hash
8395             var field, id;
8396             for(id in values){
8397                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8398
8399                     if (field.setFromData &&
8400                         field.valueField &&
8401                         field.displayField &&
8402                         // combos' with local stores can
8403                         // be queried via setValue()
8404                         // to set their value..
8405                         (field.store && !field.store.isLocal)
8406                         ) {
8407                         // it's a combo
8408                         var sd = { };
8409                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8410                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8411                         field.setFromData(sd);
8412
8413                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8414                         
8415                         field.setFromData(values);
8416                         
8417                     } else {
8418                         field.setValue(values[id]);
8419                     }
8420
8421
8422                     if(this.trackResetOnLoad){
8423                         field.originalValue = field.getValue();
8424                     }
8425                 }
8426             }
8427         }
8428
8429         //Roo.each(this.childForms || [], function (f) {
8430         //    f.setValues(values);
8431         //});
8432
8433         return this;
8434     },
8435
8436     /**
8437      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8438      * they are returned as an array.
8439      * @param {Boolean} asString
8440      * @return {Object}
8441      */
8442     getValues : function(asString){
8443         //if (this.childForms) {
8444             // copy values from the child forms
8445         //    Roo.each(this.childForms, function (f) {
8446         //        this.setValues(f.getValues());
8447         //    }, this);
8448         //}
8449
8450
8451
8452         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8453         if(asString === true){
8454             return fs;
8455         }
8456         return Roo.urlDecode(fs);
8457     },
8458
8459     /**
8460      * Returns the fields in this form as an object with key/value pairs.
8461      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8462      * @return {Object}
8463      */
8464     getFieldValues : function(with_hidden)
8465     {
8466         var items = this.getItems();
8467         var ret = {};
8468         items.each(function(f){
8469             
8470             if (!f.getName()) {
8471                 return;
8472             }
8473             
8474             var v = f.getValue();
8475             
8476             if (f.inputType =='radio') {
8477                 if (typeof(ret[f.getName()]) == 'undefined') {
8478                     ret[f.getName()] = ''; // empty..
8479                 }
8480
8481                 if (!f.el.dom.checked) {
8482                     return;
8483
8484                 }
8485                 v = f.el.dom.value;
8486
8487             }
8488             
8489             if(f.xtype == 'MoneyField'){
8490                 ret[f.currencyName] = f.getCurrency();
8491             }
8492
8493             // not sure if this supported any more..
8494             if ((typeof(v) == 'object') && f.getRawValue) {
8495                 v = f.getRawValue() ; // dates..
8496             }
8497             // combo boxes where name != hiddenName...
8498             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8499                 ret[f.name] = f.getRawValue();
8500             }
8501             ret[f.getName()] = v;
8502         });
8503
8504         return ret;
8505     },
8506
8507     /**
8508      * Clears all invalid messages in this form.
8509      * @return {BasicForm} this
8510      */
8511     clearInvalid : function(){
8512         var items = this.getItems();
8513
8514         items.each(function(f){
8515            f.clearInvalid();
8516         });
8517
8518         return this;
8519     },
8520
8521     /**
8522      * Resets this form.
8523      * @return {BasicForm} this
8524      */
8525     reset : function(){
8526         var items = this.getItems();
8527         items.each(function(f){
8528             f.reset();
8529         });
8530
8531         Roo.each(this.childForms || [], function (f) {
8532             f.reset();
8533         });
8534
8535
8536         return this;
8537     },
8538     
8539     getItems : function()
8540     {
8541         var r=new Roo.util.MixedCollection(false, function(o){
8542             return o.id || (o.id = Roo.id());
8543         });
8544         var iter = function(el) {
8545             if (el.inputEl) {
8546                 r.add(el);
8547             }
8548             if (!el.items) {
8549                 return;
8550             }
8551             Roo.each(el.items,function(e) {
8552                 iter(e);
8553             });
8554         };
8555
8556         iter(this);
8557         return r;
8558     },
8559     
8560     hideFields : function(items)
8561     {
8562         Roo.each(items, function(i){
8563             
8564             var f = this.findField(i);
8565             
8566             if(!f){
8567                 return;
8568             }
8569             
8570             f.hide();
8571             
8572         }, this);
8573     },
8574     
8575     showFields : function(items)
8576     {
8577         Roo.each(items, function(i){
8578             
8579             var f = this.findField(i);
8580             
8581             if(!f){
8582                 return;
8583             }
8584             
8585             f.show();
8586             
8587         }, this);
8588     }
8589
8590 });
8591
8592 Roo.apply(Roo.bootstrap.Form, {
8593     
8594     popover : {
8595         
8596         padding : 5,
8597         
8598         isApplied : false,
8599         
8600         isMasked : false,
8601         
8602         form : false,
8603         
8604         target : false,
8605         
8606         toolTip : false,
8607         
8608         intervalID : false,
8609         
8610         maskEl : false,
8611         
8612         apply : function()
8613         {
8614             if(this.isApplied){
8615                 return;
8616             }
8617             
8618             this.maskEl = {
8619                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8620                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8621                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8622                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8623             };
8624             
8625             this.maskEl.top.enableDisplayMode("block");
8626             this.maskEl.left.enableDisplayMode("block");
8627             this.maskEl.bottom.enableDisplayMode("block");
8628             this.maskEl.right.enableDisplayMode("block");
8629             
8630             this.toolTip = new Roo.bootstrap.Tooltip({
8631                 cls : 'roo-form-error-popover',
8632                 alignment : {
8633                     'left' : ['r-l', [-2,0], 'right'],
8634                     'right' : ['l-r', [2,0], 'left'],
8635                     'bottom' : ['tl-bl', [0,2], 'top'],
8636                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8637                 }
8638             });
8639             
8640             this.toolTip.render(Roo.get(document.body));
8641
8642             this.toolTip.el.enableDisplayMode("block");
8643             
8644             Roo.get(document.body).on('click', function(){
8645                 this.unmask();
8646             }, this);
8647             
8648             Roo.get(document.body).on('touchstart', function(){
8649                 this.unmask();
8650             }, this);
8651             
8652             this.isApplied = true
8653         },
8654         
8655         mask : function(form, target)
8656         {
8657             this.form = form;
8658             
8659             this.target = target;
8660             
8661             if(!this.form.errorMask || !target.el){
8662                 return;
8663             }
8664             
8665             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8666             
8667             Roo.log(scrollable);
8668             
8669             var ot = this.target.el.calcOffsetsTo(scrollable);
8670             
8671             var scrollTo = ot[1] - this.form.maskOffset;
8672             
8673             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8674             
8675             scrollable.scrollTo('top', scrollTo);
8676             
8677             var box = this.target.el.getBox();
8678             Roo.log(box);
8679             var zIndex = Roo.bootstrap.Modal.zIndex++;
8680
8681             
8682             this.maskEl.top.setStyle('position', 'absolute');
8683             this.maskEl.top.setStyle('z-index', zIndex);
8684             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8685             this.maskEl.top.setLeft(0);
8686             this.maskEl.top.setTop(0);
8687             this.maskEl.top.show();
8688             
8689             this.maskEl.left.setStyle('position', 'absolute');
8690             this.maskEl.left.setStyle('z-index', zIndex);
8691             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8692             this.maskEl.left.setLeft(0);
8693             this.maskEl.left.setTop(box.y - this.padding);
8694             this.maskEl.left.show();
8695
8696             this.maskEl.bottom.setStyle('position', 'absolute');
8697             this.maskEl.bottom.setStyle('z-index', zIndex);
8698             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8699             this.maskEl.bottom.setLeft(0);
8700             this.maskEl.bottom.setTop(box.bottom + this.padding);
8701             this.maskEl.bottom.show();
8702
8703             this.maskEl.right.setStyle('position', 'absolute');
8704             this.maskEl.right.setStyle('z-index', zIndex);
8705             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8706             this.maskEl.right.setLeft(box.right + this.padding);
8707             this.maskEl.right.setTop(box.y - this.padding);
8708             this.maskEl.right.show();
8709
8710             this.toolTip.bindEl = this.target.el;
8711
8712             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8713
8714             var tip = this.target.blankText;
8715
8716             if(this.target.getValue() !== '' ) {
8717                 
8718                 if (this.target.invalidText.length) {
8719                     tip = this.target.invalidText;
8720                 } else if (this.target.regexText.length){
8721                     tip = this.target.regexText;
8722                 }
8723             }
8724
8725             this.toolTip.show(tip);
8726
8727             this.intervalID = window.setInterval(function() {
8728                 Roo.bootstrap.Form.popover.unmask();
8729             }, 10000);
8730
8731             window.onwheel = function(){ return false;};
8732             
8733             (function(){ this.isMasked = true; }).defer(500, this);
8734             
8735         },
8736         
8737         unmask : function()
8738         {
8739             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8740                 return;
8741             }
8742             
8743             this.maskEl.top.setStyle('position', 'absolute');
8744             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8745             this.maskEl.top.hide();
8746
8747             this.maskEl.left.setStyle('position', 'absolute');
8748             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8749             this.maskEl.left.hide();
8750
8751             this.maskEl.bottom.setStyle('position', 'absolute');
8752             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8753             this.maskEl.bottom.hide();
8754
8755             this.maskEl.right.setStyle('position', 'absolute');
8756             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8757             this.maskEl.right.hide();
8758             
8759             this.toolTip.hide();
8760             
8761             this.toolTip.el.hide();
8762             
8763             window.onwheel = function(){ return true;};
8764             
8765             if(this.intervalID){
8766                 window.clearInterval(this.intervalID);
8767                 this.intervalID = false;
8768             }
8769             
8770             this.isMasked = false;
8771             
8772         }
8773         
8774     }
8775     
8776 });
8777
8778 /*
8779  * Based on:
8780  * Ext JS Library 1.1.1
8781  * Copyright(c) 2006-2007, Ext JS, LLC.
8782  *
8783  * Originally Released Under LGPL - original licence link has changed is not relivant.
8784  *
8785  * Fork - LGPL
8786  * <script type="text/javascript">
8787  */
8788 /**
8789  * @class Roo.form.VTypes
8790  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8791  * @singleton
8792  */
8793 Roo.form.VTypes = function(){
8794     // closure these in so they are only created once.
8795     var alpha = /^[a-zA-Z_]+$/;
8796     var alphanum = /^[a-zA-Z0-9_]+$/;
8797     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8798     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8799
8800     // All these messages and functions are configurable
8801     return {
8802         /**
8803          * The function used to validate email addresses
8804          * @param {String} value The email address
8805          */
8806         'email' : function(v){
8807             return email.test(v);
8808         },
8809         /**
8810          * The error text to display when the email validation function returns false
8811          * @type String
8812          */
8813         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8814         /**
8815          * The keystroke filter mask to be applied on email input
8816          * @type RegExp
8817          */
8818         'emailMask' : /[a-z0-9_\.\-@]/i,
8819
8820         /**
8821          * The function used to validate URLs
8822          * @param {String} value The URL
8823          */
8824         'url' : function(v){
8825             return url.test(v);
8826         },
8827         /**
8828          * The error text to display when the url validation function returns false
8829          * @type String
8830          */
8831         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8832         
8833         /**
8834          * The function used to validate alpha values
8835          * @param {String} value The value
8836          */
8837         'alpha' : function(v){
8838             return alpha.test(v);
8839         },
8840         /**
8841          * The error text to display when the alpha validation function returns false
8842          * @type String
8843          */
8844         'alphaText' : 'This field should only contain letters and _',
8845         /**
8846          * The keystroke filter mask to be applied on alpha input
8847          * @type RegExp
8848          */
8849         'alphaMask' : /[a-z_]/i,
8850
8851         /**
8852          * The function used to validate alphanumeric values
8853          * @param {String} value The value
8854          */
8855         'alphanum' : function(v){
8856             return alphanum.test(v);
8857         },
8858         /**
8859          * The error text to display when the alphanumeric validation function returns false
8860          * @type String
8861          */
8862         'alphanumText' : 'This field should only contain letters, numbers and _',
8863         /**
8864          * The keystroke filter mask to be applied on alphanumeric input
8865          * @type RegExp
8866          */
8867         'alphanumMask' : /[a-z0-9_]/i
8868     };
8869 }();/*
8870  * - LGPL
8871  *
8872  * Input
8873  * 
8874  */
8875
8876 /**
8877  * @class Roo.bootstrap.Input
8878  * @extends Roo.bootstrap.Component
8879  * Bootstrap Input class
8880  * @cfg {Boolean} disabled is it disabled
8881  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8882  * @cfg {String} name name of the input
8883  * @cfg {string} fieldLabel - the label associated
8884  * @cfg {string} placeholder - placeholder to put in text.
8885  * @cfg {string}  before - input group add on before
8886  * @cfg {string} after - input group add on after
8887  * @cfg {string} size - (lg|sm) or leave empty..
8888  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8889  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8890  * @cfg {Number} md colspan out of 12 for computer-sized screens
8891  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8892  * @cfg {string} value default value of the input
8893  * @cfg {Number} labelWidth set the width of label 
8894  * @cfg {Number} labellg set the width of label (1-12)
8895  * @cfg {Number} labelmd set the width of label (1-12)
8896  * @cfg {Number} labelsm set the width of label (1-12)
8897  * @cfg {Number} labelxs set the width of label (1-12)
8898  * @cfg {String} labelAlign (top|left)
8899  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8900  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8901  * @cfg {String} indicatorpos (left|right) default left
8902  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8903  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8904
8905  * @cfg {String} align (left|center|right) Default left
8906  * @cfg {Boolean} forceFeedback (true|false) Default false
8907  * 
8908  * @constructor
8909  * Create a new Input
8910  * @param {Object} config The config object
8911  */
8912
8913 Roo.bootstrap.Input = function(config){
8914     
8915     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8916     
8917     this.addEvents({
8918         /**
8919          * @event focus
8920          * Fires when this field receives input focus.
8921          * @param {Roo.form.Field} this
8922          */
8923         focus : true,
8924         /**
8925          * @event blur
8926          * Fires when this field loses input focus.
8927          * @param {Roo.form.Field} this
8928          */
8929         blur : true,
8930         /**
8931          * @event specialkey
8932          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8933          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8934          * @param {Roo.form.Field} this
8935          * @param {Roo.EventObject} e The event object
8936          */
8937         specialkey : true,
8938         /**
8939          * @event change
8940          * Fires just before the field blurs if the field value has changed.
8941          * @param {Roo.form.Field} this
8942          * @param {Mixed} newValue The new value
8943          * @param {Mixed} oldValue The original value
8944          */
8945         change : true,
8946         /**
8947          * @event invalid
8948          * Fires after the field has been marked as invalid.
8949          * @param {Roo.form.Field} this
8950          * @param {String} msg The validation message
8951          */
8952         invalid : true,
8953         /**
8954          * @event valid
8955          * Fires after the field has been validated with no errors.
8956          * @param {Roo.form.Field} this
8957          */
8958         valid : true,
8959          /**
8960          * @event keyup
8961          * Fires after the key up
8962          * @param {Roo.form.Field} this
8963          * @param {Roo.EventObject}  e The event Object
8964          */
8965         keyup : true
8966     });
8967 };
8968
8969 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8970      /**
8971      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8972       automatic validation (defaults to "keyup").
8973      */
8974     validationEvent : "keyup",
8975      /**
8976      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8977      */
8978     validateOnBlur : true,
8979     /**
8980      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8981      */
8982     validationDelay : 250,
8983      /**
8984      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8985      */
8986     focusClass : "x-form-focus",  // not needed???
8987     
8988        
8989     /**
8990      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8991      */
8992     invalidClass : "has-warning",
8993     
8994     /**
8995      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8996      */
8997     validClass : "has-success",
8998     
8999     /**
9000      * @cfg {Boolean} hasFeedback (true|false) default true
9001      */
9002     hasFeedback : true,
9003     
9004     /**
9005      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9006      */
9007     invalidFeedbackClass : "glyphicon-warning-sign",
9008     
9009     /**
9010      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9011      */
9012     validFeedbackClass : "glyphicon-ok",
9013     
9014     /**
9015      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9016      */
9017     selectOnFocus : false,
9018     
9019      /**
9020      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9021      */
9022     maskRe : null,
9023        /**
9024      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9025      */
9026     vtype : null,
9027     
9028       /**
9029      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9030      */
9031     disableKeyFilter : false,
9032     
9033        /**
9034      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9035      */
9036     disabled : false,
9037      /**
9038      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9039      */
9040     allowBlank : true,
9041     /**
9042      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9043      */
9044     blankText : "Please complete this mandatory field",
9045     
9046      /**
9047      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9048      */
9049     minLength : 0,
9050     /**
9051      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9052      */
9053     maxLength : Number.MAX_VALUE,
9054     /**
9055      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9056      */
9057     minLengthText : "The minimum length for this field is {0}",
9058     /**
9059      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9060      */
9061     maxLengthText : "The maximum length for this field is {0}",
9062   
9063     
9064     /**
9065      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9066      * If available, this function will be called only after the basic validators all return true, and will be passed the
9067      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9068      */
9069     validator : null,
9070     /**
9071      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9072      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9073      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9074      */
9075     regex : null,
9076     /**
9077      * @cfg {String} regexText -- Depricated - use Invalid Text
9078      */
9079     regexText : "",
9080     
9081     /**
9082      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9083      */
9084     invalidText : "",
9085     
9086     
9087     
9088     autocomplete: false,
9089     
9090     
9091     fieldLabel : '',
9092     inputType : 'text',
9093     
9094     name : false,
9095     placeholder: false,
9096     before : false,
9097     after : false,
9098     size : false,
9099     hasFocus : false,
9100     preventMark: false,
9101     isFormField : true,
9102     value : '',
9103     labelWidth : 2,
9104     labelAlign : false,
9105     readOnly : false,
9106     align : false,
9107     formatedValue : false,
9108     forceFeedback : false,
9109     
9110     indicatorpos : 'left',
9111     
9112     labellg : 0,
9113     labelmd : 0,
9114     labelsm : 0,
9115     labelxs : 0,
9116     
9117     capture : '',
9118     accept : '',
9119     
9120     parentLabelAlign : function()
9121     {
9122         var parent = this;
9123         while (parent.parent()) {
9124             parent = parent.parent();
9125             if (typeof(parent.labelAlign) !='undefined') {
9126                 return parent.labelAlign;
9127             }
9128         }
9129         return 'left';
9130         
9131     },
9132     
9133     getAutoCreate : function()
9134     {
9135         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9136         
9137         var id = Roo.id();
9138         
9139         var cfg = {};
9140         
9141         if(this.inputType != 'hidden'){
9142             cfg.cls = 'form-group' //input-group
9143         }
9144         
9145         var input =  {
9146             tag: 'input',
9147             id : id,
9148             type : this.inputType,
9149             value : this.value,
9150             cls : 'form-control',
9151             placeholder : this.placeholder || '',
9152             autocomplete : this.autocomplete || 'new-password'
9153         };
9154         
9155         if(this.capture.length){
9156             input.capture = this.capture;
9157         }
9158         
9159         if(this.accept.length){
9160             input.accept = this.accept + "/*";
9161         }
9162         
9163         if(this.align){
9164             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9165         }
9166         
9167         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9168             input.maxLength = this.maxLength;
9169         }
9170         
9171         if (this.disabled) {
9172             input.disabled=true;
9173         }
9174         
9175         if (this.readOnly) {
9176             input.readonly=true;
9177         }
9178         
9179         if (this.name) {
9180             input.name = this.name;
9181         }
9182         
9183         if (this.size) {
9184             input.cls += ' input-' + this.size;
9185         }
9186         
9187         var settings=this;
9188         ['xs','sm','md','lg'].map(function(size){
9189             if (settings[size]) {
9190                 cfg.cls += ' col-' + size + '-' + settings[size];
9191             }
9192         });
9193         
9194         var inputblock = input;
9195         
9196         var feedback = {
9197             tag: 'span',
9198             cls: 'glyphicon form-control-feedback'
9199         };
9200             
9201         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9202             
9203             inputblock = {
9204                 cls : 'has-feedback',
9205                 cn :  [
9206                     input,
9207                     feedback
9208                 ] 
9209             };  
9210         }
9211         
9212         if (this.before || this.after) {
9213             
9214             inputblock = {
9215                 cls : 'input-group',
9216                 cn :  [] 
9217             };
9218             
9219             if (this.before && typeof(this.before) == 'string') {
9220                 
9221                 inputblock.cn.push({
9222                     tag :'span',
9223                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9224                     html : this.before
9225                 });
9226             }
9227             if (this.before && typeof(this.before) == 'object') {
9228                 this.before = Roo.factory(this.before);
9229                 
9230                 inputblock.cn.push({
9231                     tag :'span',
9232                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9233                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9234                 });
9235             }
9236             
9237             inputblock.cn.push(input);
9238             
9239             if (this.after && typeof(this.after) == 'string') {
9240                 inputblock.cn.push({
9241                     tag :'span',
9242                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9243                     html : this.after
9244                 });
9245             }
9246             if (this.after && typeof(this.after) == 'object') {
9247                 this.after = Roo.factory(this.after);
9248                 
9249                 inputblock.cn.push({
9250                     tag :'span',
9251                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9252                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9253                 });
9254             }
9255             
9256             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9257                 inputblock.cls += ' has-feedback';
9258                 inputblock.cn.push(feedback);
9259             }
9260         };
9261         var indicator = {
9262             tag : 'i',
9263             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9264             tooltip : 'This field is required'
9265         };
9266         if (Roo.bootstrap.version == 4) {
9267             indicator = {
9268                 tag : 'i',
9269                 style : 'display-none'
9270             };
9271         }
9272         if (align ==='left' && this.fieldLabel.length) {
9273             
9274             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9275             
9276             cfg.cn = [
9277                 indicator,
9278                 {
9279                     tag: 'label',
9280                     'for' :  id,
9281                     cls : 'control-label col-form-label',
9282                     html : this.fieldLabel
9283
9284                 },
9285                 {
9286                     cls : "", 
9287                     cn: [
9288                         inputblock
9289                     ]
9290                 }
9291             ];
9292             
9293             var labelCfg = cfg.cn[1];
9294             var contentCfg = cfg.cn[2];
9295             
9296             if(this.indicatorpos == 'right'){
9297                 cfg.cn = [
9298                     {
9299                         tag: 'label',
9300                         'for' :  id,
9301                         cls : 'control-label col-form-label',
9302                         cn : [
9303                             {
9304                                 tag : 'span',
9305                                 html : this.fieldLabel
9306                             },
9307                             indicator
9308                         ]
9309                     },
9310                     {
9311                         cls : "",
9312                         cn: [
9313                             inputblock
9314                         ]
9315                     }
9316
9317                 ];
9318                 
9319                 labelCfg = cfg.cn[0];
9320                 contentCfg = cfg.cn[1];
9321             
9322             }
9323             
9324             if(this.labelWidth > 12){
9325                 labelCfg.style = "width: " + this.labelWidth + 'px';
9326             }
9327             
9328             if(this.labelWidth < 13 && this.labelmd == 0){
9329                 this.labelmd = this.labelWidth;
9330             }
9331             
9332             if(this.labellg > 0){
9333                 labelCfg.cls += ' col-lg-' + this.labellg;
9334                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9335             }
9336             
9337             if(this.labelmd > 0){
9338                 labelCfg.cls += ' col-md-' + this.labelmd;
9339                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9340             }
9341             
9342             if(this.labelsm > 0){
9343                 labelCfg.cls += ' col-sm-' + this.labelsm;
9344                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9345             }
9346             
9347             if(this.labelxs > 0){
9348                 labelCfg.cls += ' col-xs-' + this.labelxs;
9349                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9350             }
9351             
9352             
9353         } else if ( this.fieldLabel.length) {
9354                 
9355             cfg.cn = [
9356                 {
9357                     tag : 'i',
9358                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9359                     tooltip : 'This field is required'
9360                 },
9361                 {
9362                     tag: 'label',
9363                    //cls : 'input-group-addon',
9364                     html : this.fieldLabel
9365
9366                 },
9367
9368                inputblock
9369
9370            ];
9371            
9372            if(this.indicatorpos == 'right'){
9373                 
9374                 cfg.cn = [
9375                     {
9376                         tag: 'label',
9377                        //cls : 'input-group-addon',
9378                         html : this.fieldLabel
9379
9380                     },
9381                     {
9382                         tag : 'i',
9383                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9384                         tooltip : 'This field is required'
9385                     },
9386
9387                    inputblock
9388
9389                ];
9390
9391             }
9392
9393         } else {
9394             
9395             cfg.cn = [
9396
9397                     inputblock
9398
9399             ];
9400                 
9401                 
9402         };
9403         
9404         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9405            cfg.cls += ' navbar-form';
9406         }
9407         
9408         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9409             // on BS4 we do this only if not form 
9410             cfg.cls += ' navbar-form';
9411             cfg.tag = 'li';
9412         }
9413         
9414         return cfg;
9415         
9416     },
9417     /**
9418      * return the real input element.
9419      */
9420     inputEl: function ()
9421     {
9422         return this.el.select('input.form-control',true).first();
9423     },
9424     
9425     tooltipEl : function()
9426     {
9427         return this.inputEl();
9428     },
9429     
9430     indicatorEl : function()
9431     {
9432         if (Roo.bootstrap.version == 4) {
9433             return false; // not enabled in v4 yet.
9434         }
9435         
9436         var indicator = this.el.select('i.roo-required-indicator',true).first();
9437         
9438         if(!indicator){
9439             return false;
9440         }
9441         
9442         return indicator;
9443         
9444     },
9445     
9446     setDisabled : function(v)
9447     {
9448         var i  = this.inputEl().dom;
9449         if (!v) {
9450             i.removeAttribute('disabled');
9451             return;
9452             
9453         }
9454         i.setAttribute('disabled','true');
9455     },
9456     initEvents : function()
9457     {
9458           
9459         this.inputEl().on("keydown" , this.fireKey,  this);
9460         this.inputEl().on("focus", this.onFocus,  this);
9461         this.inputEl().on("blur", this.onBlur,  this);
9462         
9463         this.inputEl().relayEvent('keyup', this);
9464         
9465         this.indicator = this.indicatorEl();
9466         
9467         if(this.indicator){
9468             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9469         }
9470  
9471         // reference to original value for reset
9472         this.originalValue = this.getValue();
9473         //Roo.form.TextField.superclass.initEvents.call(this);
9474         if(this.validationEvent == 'keyup'){
9475             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9476             this.inputEl().on('keyup', this.filterValidation, this);
9477         }
9478         else if(this.validationEvent !== false){
9479             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9480         }
9481         
9482         if(this.selectOnFocus){
9483             this.on("focus", this.preFocus, this);
9484             
9485         }
9486         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9487             this.inputEl().on("keypress", this.filterKeys, this);
9488         } else {
9489             this.inputEl().relayEvent('keypress', this);
9490         }
9491        /* if(this.grow){
9492             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9493             this.el.on("click", this.autoSize,  this);
9494         }
9495         */
9496         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9497             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9498         }
9499         
9500         if (typeof(this.before) == 'object') {
9501             this.before.render(this.el.select('.roo-input-before',true).first());
9502         }
9503         if (typeof(this.after) == 'object') {
9504             this.after.render(this.el.select('.roo-input-after',true).first());
9505         }
9506         
9507         this.inputEl().on('change', this.onChange, this);
9508         
9509     },
9510     filterValidation : function(e){
9511         if(!e.isNavKeyPress()){
9512             this.validationTask.delay(this.validationDelay);
9513         }
9514     },
9515      /**
9516      * Validates the field value
9517      * @return {Boolean} True if the value is valid, else false
9518      */
9519     validate : function(){
9520         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9521         if(this.disabled || this.validateValue(this.getRawValue())){
9522             this.markValid();
9523             return true;
9524         }
9525         
9526         this.markInvalid();
9527         return false;
9528     },
9529     
9530     
9531     /**
9532      * Validates a value according to the field's validation rules and marks the field as invalid
9533      * if the validation fails
9534      * @param {Mixed} value The value to validate
9535      * @return {Boolean} True if the value is valid, else false
9536      */
9537     validateValue : function(value)
9538     {
9539         if(this.getVisibilityEl().hasClass('hidden')){
9540             return true;
9541         }
9542         
9543         if(value.length < 1)  { // if it's blank
9544             if(this.allowBlank){
9545                 return true;
9546             }
9547             return false;
9548         }
9549         
9550         if(value.length < this.minLength){
9551             return false;
9552         }
9553         if(value.length > this.maxLength){
9554             return false;
9555         }
9556         if(this.vtype){
9557             var vt = Roo.form.VTypes;
9558             if(!vt[this.vtype](value, this)){
9559                 return false;
9560             }
9561         }
9562         if(typeof this.validator == "function"){
9563             var msg = this.validator(value);
9564             if(msg !== true){
9565                 return false;
9566             }
9567             if (typeof(msg) == 'string') {
9568                 this.invalidText = msg;
9569             }
9570         }
9571         
9572         if(this.regex && !this.regex.test(value)){
9573             return false;
9574         }
9575         
9576         return true;
9577     },
9578     
9579      // private
9580     fireKey : function(e){
9581         //Roo.log('field ' + e.getKey());
9582         if(e.isNavKeyPress()){
9583             this.fireEvent("specialkey", this, e);
9584         }
9585     },
9586     focus : function (selectText){
9587         if(this.rendered){
9588             this.inputEl().focus();
9589             if(selectText === true){
9590                 this.inputEl().dom.select();
9591             }
9592         }
9593         return this;
9594     } ,
9595     
9596     onFocus : function(){
9597         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9598            // this.el.addClass(this.focusClass);
9599         }
9600         if(!this.hasFocus){
9601             this.hasFocus = true;
9602             this.startValue = this.getValue();
9603             this.fireEvent("focus", this);
9604         }
9605     },
9606     
9607     beforeBlur : Roo.emptyFn,
9608
9609     
9610     // private
9611     onBlur : function(){
9612         this.beforeBlur();
9613         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9614             //this.el.removeClass(this.focusClass);
9615         }
9616         this.hasFocus = false;
9617         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9618             this.validate();
9619         }
9620         var v = this.getValue();
9621         if(String(v) !== String(this.startValue)){
9622             this.fireEvent('change', this, v, this.startValue);
9623         }
9624         this.fireEvent("blur", this);
9625     },
9626     
9627     onChange : function(e)
9628     {
9629         var v = this.getValue();
9630         if(String(v) !== String(this.startValue)){
9631             this.fireEvent('change', this, v, this.startValue);
9632         }
9633         
9634     },
9635     
9636     /**
9637      * Resets the current field value to the originally loaded value and clears any validation messages
9638      */
9639     reset : function(){
9640         this.setValue(this.originalValue);
9641         this.validate();
9642     },
9643      /**
9644      * Returns the name of the field
9645      * @return {Mixed} name The name field
9646      */
9647     getName: function(){
9648         return this.name;
9649     },
9650      /**
9651      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9652      * @return {Mixed} value The field value
9653      */
9654     getValue : function(){
9655         
9656         var v = this.inputEl().getValue();
9657         
9658         return v;
9659     },
9660     /**
9661      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9662      * @return {Mixed} value The field value
9663      */
9664     getRawValue : function(){
9665         var v = this.inputEl().getValue();
9666         
9667         return v;
9668     },
9669     
9670     /**
9671      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9672      * @param {Mixed} value The value to set
9673      */
9674     setRawValue : function(v){
9675         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9676     },
9677     
9678     selectText : function(start, end){
9679         var v = this.getRawValue();
9680         if(v.length > 0){
9681             start = start === undefined ? 0 : start;
9682             end = end === undefined ? v.length : end;
9683             var d = this.inputEl().dom;
9684             if(d.setSelectionRange){
9685                 d.setSelectionRange(start, end);
9686             }else if(d.createTextRange){
9687                 var range = d.createTextRange();
9688                 range.moveStart("character", start);
9689                 range.moveEnd("character", v.length-end);
9690                 range.select();
9691             }
9692         }
9693     },
9694     
9695     /**
9696      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9697      * @param {Mixed} value The value to set
9698      */
9699     setValue : function(v){
9700         this.value = v;
9701         if(this.rendered){
9702             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9703             this.validate();
9704         }
9705     },
9706     
9707     /*
9708     processValue : function(value){
9709         if(this.stripCharsRe){
9710             var newValue = value.replace(this.stripCharsRe, '');
9711             if(newValue !== value){
9712                 this.setRawValue(newValue);
9713                 return newValue;
9714             }
9715         }
9716         return value;
9717     },
9718   */
9719     preFocus : function(){
9720         
9721         if(this.selectOnFocus){
9722             this.inputEl().dom.select();
9723         }
9724     },
9725     filterKeys : function(e){
9726         var k = e.getKey();
9727         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9728             return;
9729         }
9730         var c = e.getCharCode(), cc = String.fromCharCode(c);
9731         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9732             return;
9733         }
9734         if(!this.maskRe.test(cc)){
9735             e.stopEvent();
9736         }
9737     },
9738      /**
9739      * Clear any invalid styles/messages for this field
9740      */
9741     clearInvalid : function(){
9742         
9743         if(!this.el || this.preventMark){ // not rendered
9744             return;
9745         }
9746         
9747      
9748         this.el.removeClass(this.invalidClass);
9749         
9750         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9751             
9752             var feedback = this.el.select('.form-control-feedback', true).first();
9753             
9754             if(feedback){
9755                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9756             }
9757             
9758         }
9759         
9760         if(this.indicator){
9761             this.indicator.removeClass('visible');
9762             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9763         }
9764         
9765         this.fireEvent('valid', this);
9766     },
9767     
9768      /**
9769      * Mark this field as valid
9770      */
9771     markValid : function()
9772     {
9773         if(!this.el  || this.preventMark){ // not rendered...
9774             return;
9775         }
9776         
9777         this.el.removeClass([this.invalidClass, this.validClass]);
9778         
9779         var feedback = this.el.select('.form-control-feedback', true).first();
9780             
9781         if(feedback){
9782             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9783         }
9784         
9785         if(this.indicator){
9786             this.indicator.removeClass('visible');
9787             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9788         }
9789         
9790         if(this.disabled){
9791             return;
9792         }
9793         
9794         if(this.allowBlank && !this.getRawValue().length){
9795             return;
9796         }
9797         
9798         this.el.addClass(this.validClass);
9799         
9800         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9801             
9802             var feedback = this.el.select('.form-control-feedback', true).first();
9803             
9804             if(feedback){
9805                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9806                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9807             }
9808             
9809         }
9810         
9811         this.fireEvent('valid', this);
9812     },
9813     
9814      /**
9815      * Mark this field as invalid
9816      * @param {String} msg The validation message
9817      */
9818     markInvalid : function(msg)
9819     {
9820         if(!this.el  || this.preventMark){ // not rendered
9821             return;
9822         }
9823         
9824         this.el.removeClass([this.invalidClass, this.validClass]);
9825         
9826         var feedback = this.el.select('.form-control-feedback', true).first();
9827             
9828         if(feedback){
9829             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9830         }
9831
9832         if(this.disabled){
9833             return;
9834         }
9835         
9836         if(this.allowBlank && !this.getRawValue().length){
9837             return;
9838         }
9839         
9840         if(this.indicator){
9841             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9842             this.indicator.addClass('visible');
9843         }
9844         
9845         this.el.addClass(this.invalidClass);
9846         
9847         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9848             
9849             var feedback = this.el.select('.form-control-feedback', true).first();
9850             
9851             if(feedback){
9852                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9853                 
9854                 if(this.getValue().length || this.forceFeedback){
9855                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9856                 }
9857                 
9858             }
9859             
9860         }
9861         
9862         this.fireEvent('invalid', this, msg);
9863     },
9864     // private
9865     SafariOnKeyDown : function(event)
9866     {
9867         // this is a workaround for a password hang bug on chrome/ webkit.
9868         if (this.inputEl().dom.type != 'password') {
9869             return;
9870         }
9871         
9872         var isSelectAll = false;
9873         
9874         if(this.inputEl().dom.selectionEnd > 0){
9875             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9876         }
9877         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9878             event.preventDefault();
9879             this.setValue('');
9880             return;
9881         }
9882         
9883         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9884             
9885             event.preventDefault();
9886             // this is very hacky as keydown always get's upper case.
9887             //
9888             var cc = String.fromCharCode(event.getCharCode());
9889             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9890             
9891         }
9892     },
9893     adjustWidth : function(tag, w){
9894         tag = tag.toLowerCase();
9895         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9896             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9897                 if(tag == 'input'){
9898                     return w + 2;
9899                 }
9900                 if(tag == 'textarea'){
9901                     return w-2;
9902                 }
9903             }else if(Roo.isOpera){
9904                 if(tag == 'input'){
9905                     return w + 2;
9906                 }
9907                 if(tag == 'textarea'){
9908                     return w-2;
9909                 }
9910             }
9911         }
9912         return w;
9913     },
9914     
9915     setFieldLabel : function(v)
9916     {
9917         if(!this.rendered){
9918             return;
9919         }
9920         
9921         if(this.indicatorEl()){
9922             var ar = this.el.select('label > span',true);
9923             
9924             if (ar.elements.length) {
9925                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9926                 this.fieldLabel = v;
9927                 return;
9928             }
9929             
9930             var br = this.el.select('label',true);
9931             
9932             if(br.elements.length) {
9933                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9934                 this.fieldLabel = v;
9935                 return;
9936             }
9937             
9938             Roo.log('Cannot Found any of label > span || label in input');
9939             return;
9940         }
9941         
9942         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9943         this.fieldLabel = v;
9944         
9945         
9946     }
9947 });
9948
9949  
9950 /*
9951  * - LGPL
9952  *
9953  * Input
9954  * 
9955  */
9956
9957 /**
9958  * @class Roo.bootstrap.TextArea
9959  * @extends Roo.bootstrap.Input
9960  * Bootstrap TextArea class
9961  * @cfg {Number} cols Specifies the visible width of a text area
9962  * @cfg {Number} rows Specifies the visible number of lines in a text area
9963  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9964  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9965  * @cfg {string} html text
9966  * 
9967  * @constructor
9968  * Create a new TextArea
9969  * @param {Object} config The config object
9970  */
9971
9972 Roo.bootstrap.TextArea = function(config){
9973     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9974    
9975 };
9976
9977 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9978      
9979     cols : false,
9980     rows : 5,
9981     readOnly : false,
9982     warp : 'soft',
9983     resize : false,
9984     value: false,
9985     html: false,
9986     
9987     getAutoCreate : function(){
9988         
9989         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9990         
9991         var id = Roo.id();
9992         
9993         var cfg = {};
9994         
9995         if(this.inputType != 'hidden'){
9996             cfg.cls = 'form-group' //input-group
9997         }
9998         
9999         var input =  {
10000             tag: 'textarea',
10001             id : id,
10002             warp : this.warp,
10003             rows : this.rows,
10004             value : this.value || '',
10005             html: this.html || '',
10006             cls : 'form-control',
10007             placeholder : this.placeholder || '' 
10008             
10009         };
10010         
10011         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10012             input.maxLength = this.maxLength;
10013         }
10014         
10015         if(this.resize){
10016             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10017         }
10018         
10019         if(this.cols){
10020             input.cols = this.cols;
10021         }
10022         
10023         if (this.readOnly) {
10024             input.readonly = true;
10025         }
10026         
10027         if (this.name) {
10028             input.name = this.name;
10029         }
10030         
10031         if (this.size) {
10032             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10033         }
10034         
10035         var settings=this;
10036         ['xs','sm','md','lg'].map(function(size){
10037             if (settings[size]) {
10038                 cfg.cls += ' col-' + size + '-' + settings[size];
10039             }
10040         });
10041         
10042         var inputblock = input;
10043         
10044         if(this.hasFeedback && !this.allowBlank){
10045             
10046             var feedback = {
10047                 tag: 'span',
10048                 cls: 'glyphicon form-control-feedback'
10049             };
10050
10051             inputblock = {
10052                 cls : 'has-feedback',
10053                 cn :  [
10054                     input,
10055                     feedback
10056                 ] 
10057             };  
10058         }
10059         
10060         
10061         if (this.before || this.after) {
10062             
10063             inputblock = {
10064                 cls : 'input-group',
10065                 cn :  [] 
10066             };
10067             if (this.before) {
10068                 inputblock.cn.push({
10069                     tag :'span',
10070                     cls : 'input-group-addon',
10071                     html : this.before
10072                 });
10073             }
10074             
10075             inputblock.cn.push(input);
10076             
10077             if(this.hasFeedback && !this.allowBlank){
10078                 inputblock.cls += ' has-feedback';
10079                 inputblock.cn.push(feedback);
10080             }
10081             
10082             if (this.after) {
10083                 inputblock.cn.push({
10084                     tag :'span',
10085                     cls : 'input-group-addon',
10086                     html : this.after
10087                 });
10088             }
10089             
10090         }
10091         
10092         if (align ==='left' && this.fieldLabel.length) {
10093             cfg.cn = [
10094                 {
10095                     tag: 'label',
10096                     'for' :  id,
10097                     cls : 'control-label',
10098                     html : this.fieldLabel
10099                 },
10100                 {
10101                     cls : "",
10102                     cn: [
10103                         inputblock
10104                     ]
10105                 }
10106
10107             ];
10108             
10109             if(this.labelWidth > 12){
10110                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10111             }
10112
10113             if(this.labelWidth < 13 && this.labelmd == 0){
10114                 this.labelmd = this.labelWidth;
10115             }
10116
10117             if(this.labellg > 0){
10118                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10119                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10120             }
10121
10122             if(this.labelmd > 0){
10123                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10124                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10125             }
10126
10127             if(this.labelsm > 0){
10128                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10129                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10130             }
10131
10132             if(this.labelxs > 0){
10133                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10134                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10135             }
10136             
10137         } else if ( this.fieldLabel.length) {
10138             cfg.cn = [
10139
10140                {
10141                    tag: 'label',
10142                    //cls : 'input-group-addon',
10143                    html : this.fieldLabel
10144
10145                },
10146
10147                inputblock
10148
10149            ];
10150
10151         } else {
10152
10153             cfg.cn = [
10154
10155                 inputblock
10156
10157             ];
10158                 
10159         }
10160         
10161         if (this.disabled) {
10162             input.disabled=true;
10163         }
10164         
10165         return cfg;
10166         
10167     },
10168     /**
10169      * return the real textarea element.
10170      */
10171     inputEl: function ()
10172     {
10173         return this.el.select('textarea.form-control',true).first();
10174     },
10175     
10176     /**
10177      * Clear any invalid styles/messages for this field
10178      */
10179     clearInvalid : function()
10180     {
10181         
10182         if(!this.el || this.preventMark){ // not rendered
10183             return;
10184         }
10185         
10186         var label = this.el.select('label', true).first();
10187         var icon = this.el.select('i.fa-star', true).first();
10188         
10189         if(label && icon){
10190             icon.remove();
10191         }
10192         
10193         this.el.removeClass(this.invalidClass);
10194         
10195         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10196             
10197             var feedback = this.el.select('.form-control-feedback', true).first();
10198             
10199             if(feedback){
10200                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10201             }
10202             
10203         }
10204         
10205         this.fireEvent('valid', this);
10206     },
10207     
10208      /**
10209      * Mark this field as valid
10210      */
10211     markValid : function()
10212     {
10213         if(!this.el  || this.preventMark){ // not rendered
10214             return;
10215         }
10216         
10217         this.el.removeClass([this.invalidClass, this.validClass]);
10218         
10219         var feedback = this.el.select('.form-control-feedback', true).first();
10220             
10221         if(feedback){
10222             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10223         }
10224
10225         if(this.disabled || this.allowBlank){
10226             return;
10227         }
10228         
10229         var label = this.el.select('label', true).first();
10230         var icon = this.el.select('i.fa-star', true).first();
10231         
10232         if(label && icon){
10233             icon.remove();
10234         }
10235         
10236         this.el.addClass(this.validClass);
10237         
10238         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10239             
10240             var feedback = this.el.select('.form-control-feedback', true).first();
10241             
10242             if(feedback){
10243                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10244                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10245             }
10246             
10247         }
10248         
10249         this.fireEvent('valid', this);
10250     },
10251     
10252      /**
10253      * Mark this field as invalid
10254      * @param {String} msg The validation message
10255      */
10256     markInvalid : function(msg)
10257     {
10258         if(!this.el  || this.preventMark){ // not rendered
10259             return;
10260         }
10261         
10262         this.el.removeClass([this.invalidClass, this.validClass]);
10263         
10264         var feedback = this.el.select('.form-control-feedback', true).first();
10265             
10266         if(feedback){
10267             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10268         }
10269
10270         if(this.disabled || this.allowBlank){
10271             return;
10272         }
10273         
10274         var label = this.el.select('label', true).first();
10275         var icon = this.el.select('i.fa-star', true).first();
10276         
10277         if(!this.getValue().length && label && !icon){
10278             this.el.createChild({
10279                 tag : 'i',
10280                 cls : 'text-danger fa fa-lg fa-star',
10281                 tooltip : 'This field is required',
10282                 style : 'margin-right:5px;'
10283             }, label, true);
10284         }
10285
10286         this.el.addClass(this.invalidClass);
10287         
10288         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10289             
10290             var feedback = this.el.select('.form-control-feedback', true).first();
10291             
10292             if(feedback){
10293                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10294                 
10295                 if(this.getValue().length || this.forceFeedback){
10296                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10297                 }
10298                 
10299             }
10300             
10301         }
10302         
10303         this.fireEvent('invalid', this, msg);
10304     }
10305 });
10306
10307  
10308 /*
10309  * - LGPL
10310  *
10311  * trigger field - base class for combo..
10312  * 
10313  */
10314  
10315 /**
10316  * @class Roo.bootstrap.TriggerField
10317  * @extends Roo.bootstrap.Input
10318  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10319  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10320  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10321  * for which you can provide a custom implementation.  For example:
10322  * <pre><code>
10323 var trigger = new Roo.bootstrap.TriggerField();
10324 trigger.onTriggerClick = myTriggerFn;
10325 trigger.applyTo('my-field');
10326 </code></pre>
10327  *
10328  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10329  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10330  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10331  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10332  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10333
10334  * @constructor
10335  * Create a new TriggerField.
10336  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10337  * to the base TextField)
10338  */
10339 Roo.bootstrap.TriggerField = function(config){
10340     this.mimicing = false;
10341     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10342 };
10343
10344 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10345     /**
10346      * @cfg {String} triggerClass A CSS class to apply to the trigger
10347      */
10348      /**
10349      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10350      */
10351     hideTrigger:false,
10352
10353     /**
10354      * @cfg {Boolean} removable (true|false) special filter default false
10355      */
10356     removable : false,
10357     
10358     /** @cfg {Boolean} grow @hide */
10359     /** @cfg {Number} growMin @hide */
10360     /** @cfg {Number} growMax @hide */
10361
10362     /**
10363      * @hide 
10364      * @method
10365      */
10366     autoSize: Roo.emptyFn,
10367     // private
10368     monitorTab : true,
10369     // private
10370     deferHeight : true,
10371
10372     
10373     actionMode : 'wrap',
10374     
10375     caret : false,
10376     
10377     
10378     getAutoCreate : function(){
10379        
10380         var align = this.labelAlign || this.parentLabelAlign();
10381         
10382         var id = Roo.id();
10383         
10384         var cfg = {
10385             cls: 'form-group' //input-group
10386         };
10387         
10388         
10389         var input =  {
10390             tag: 'input',
10391             id : id,
10392             type : this.inputType,
10393             cls : 'form-control',
10394             autocomplete: 'new-password',
10395             placeholder : this.placeholder || '' 
10396             
10397         };
10398         if (this.name) {
10399             input.name = this.name;
10400         }
10401         if (this.size) {
10402             input.cls += ' input-' + this.size;
10403         }
10404         
10405         if (this.disabled) {
10406             input.disabled=true;
10407         }
10408         
10409         var inputblock = input;
10410         
10411         if(this.hasFeedback && !this.allowBlank){
10412             
10413             var feedback = {
10414                 tag: 'span',
10415                 cls: 'glyphicon form-control-feedback'
10416             };
10417             
10418             if(this.removable && !this.editable && !this.tickable){
10419                 inputblock = {
10420                     cls : 'has-feedback',
10421                     cn :  [
10422                         inputblock,
10423                         {
10424                             tag: 'button',
10425                             html : 'x',
10426                             cls : 'roo-combo-removable-btn close'
10427                         },
10428                         feedback
10429                     ] 
10430                 };
10431             } else {
10432                 inputblock = {
10433                     cls : 'has-feedback',
10434                     cn :  [
10435                         inputblock,
10436                         feedback
10437                     ] 
10438                 };
10439             }
10440
10441         } else {
10442             if(this.removable && !this.editable && !this.tickable){
10443                 inputblock = {
10444                     cls : 'roo-removable',
10445                     cn :  [
10446                         inputblock,
10447                         {
10448                             tag: 'button',
10449                             html : 'x',
10450                             cls : 'roo-combo-removable-btn close'
10451                         }
10452                     ] 
10453                 };
10454             }
10455         }
10456         
10457         if (this.before || this.after) {
10458             
10459             inputblock = {
10460                 cls : 'input-group',
10461                 cn :  [] 
10462             };
10463             if (this.before) {
10464                 inputblock.cn.push({
10465                     tag :'span',
10466                     cls : 'input-group-addon input-group-prepend input-group-text',
10467                     html : this.before
10468                 });
10469             }
10470             
10471             inputblock.cn.push(input);
10472             
10473             if(this.hasFeedback && !this.allowBlank){
10474                 inputblock.cls += ' has-feedback';
10475                 inputblock.cn.push(feedback);
10476             }
10477             
10478             if (this.after) {
10479                 inputblock.cn.push({
10480                     tag :'span',
10481                     cls : 'input-group-addon input-group-append input-group-text',
10482                     html : this.after
10483                 });
10484             }
10485             
10486         };
10487         
10488       
10489         
10490         var ibwrap = inputblock;
10491         
10492         if(this.multiple){
10493             ibwrap = {
10494                 tag: 'ul',
10495                 cls: 'roo-select2-choices',
10496                 cn:[
10497                     {
10498                         tag: 'li',
10499                         cls: 'roo-select2-search-field',
10500                         cn: [
10501
10502                             inputblock
10503                         ]
10504                     }
10505                 ]
10506             };
10507                 
10508         }
10509         
10510         var combobox = {
10511             cls: 'roo-select2-container input-group',
10512             cn: [
10513                  {
10514                     tag: 'input',
10515                     type : 'hidden',
10516                     cls: 'form-hidden-field'
10517                 },
10518                 ibwrap
10519             ]
10520         };
10521         
10522         if(!this.multiple && this.showToggleBtn){
10523             
10524             var caret = {
10525                         tag: 'span',
10526                         cls: 'caret'
10527              };
10528             if (this.caret != false) {
10529                 caret = {
10530                      tag: 'i',
10531                      cls: 'fa fa-' + this.caret
10532                 };
10533                 
10534             }
10535             
10536             combobox.cn.push({
10537                 tag :'span',
10538                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10539                 cn : [
10540                     caret,
10541                     {
10542                         tag: 'span',
10543                         cls: 'combobox-clear',
10544                         cn  : [
10545                             {
10546                                 tag : 'i',
10547                                 cls: 'icon-remove'
10548                             }
10549                         ]
10550                     }
10551                 ]
10552
10553             })
10554         }
10555         
10556         if(this.multiple){
10557             combobox.cls += ' roo-select2-container-multi';
10558         }
10559          var indicator = {
10560             tag : 'i',
10561             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10562             tooltip : 'This field is required'
10563         };
10564         if (Roo.bootstrap.version == 4) {
10565             indicator = {
10566                 tag : 'i',
10567                 style : 'display:none'
10568             };
10569         }
10570         
10571         
10572         if (align ==='left' && this.fieldLabel.length) {
10573             
10574             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10575
10576             cfg.cn = [
10577                 indicator,
10578                 {
10579                     tag: 'label',
10580                     'for' :  id,
10581                     cls : 'control-label',
10582                     html : this.fieldLabel
10583
10584                 },
10585                 {
10586                     cls : "", 
10587                     cn: [
10588                         combobox
10589                     ]
10590                 }
10591
10592             ];
10593             
10594             var labelCfg = cfg.cn[1];
10595             var contentCfg = cfg.cn[2];
10596             
10597             if(this.indicatorpos == 'right'){
10598                 cfg.cn = [
10599                     {
10600                         tag: 'label',
10601                         'for' :  id,
10602                         cls : 'control-label',
10603                         cn : [
10604                             {
10605                                 tag : 'span',
10606                                 html : this.fieldLabel
10607                             },
10608                             indicator
10609                         ]
10610                     },
10611                     {
10612                         cls : "", 
10613                         cn: [
10614                             combobox
10615                         ]
10616                     }
10617
10618                 ];
10619                 
10620                 labelCfg = cfg.cn[0];
10621                 contentCfg = cfg.cn[1];
10622             }
10623             
10624             if(this.labelWidth > 12){
10625                 labelCfg.style = "width: " + this.labelWidth + 'px';
10626             }
10627             
10628             if(this.labelWidth < 13 && this.labelmd == 0){
10629                 this.labelmd = this.labelWidth;
10630             }
10631             
10632             if(this.labellg > 0){
10633                 labelCfg.cls += ' col-lg-' + this.labellg;
10634                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10635             }
10636             
10637             if(this.labelmd > 0){
10638                 labelCfg.cls += ' col-md-' + this.labelmd;
10639                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10640             }
10641             
10642             if(this.labelsm > 0){
10643                 labelCfg.cls += ' col-sm-' + this.labelsm;
10644                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10645             }
10646             
10647             if(this.labelxs > 0){
10648                 labelCfg.cls += ' col-xs-' + this.labelxs;
10649                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10650             }
10651             
10652         } else if ( this.fieldLabel.length) {
10653 //                Roo.log(" label");
10654             cfg.cn = [
10655                 indicator,
10656                {
10657                    tag: 'label',
10658                    //cls : 'input-group-addon',
10659                    html : this.fieldLabel
10660
10661                },
10662
10663                combobox
10664
10665             ];
10666             
10667             if(this.indicatorpos == 'right'){
10668                 
10669                 cfg.cn = [
10670                     {
10671                        tag: 'label',
10672                        cn : [
10673                            {
10674                                tag : 'span',
10675                                html : this.fieldLabel
10676                            },
10677                            indicator
10678                        ]
10679
10680                     },
10681                     combobox
10682
10683                 ];
10684
10685             }
10686
10687         } else {
10688             
10689 //                Roo.log(" no label && no align");
10690                 cfg = combobox
10691                      
10692                 
10693         }
10694         
10695         var settings=this;
10696         ['xs','sm','md','lg'].map(function(size){
10697             if (settings[size]) {
10698                 cfg.cls += ' col-' + size + '-' + settings[size];
10699             }
10700         });
10701         
10702         return cfg;
10703         
10704     },
10705     
10706     
10707     
10708     // private
10709     onResize : function(w, h){
10710 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10711 //        if(typeof w == 'number'){
10712 //            var x = w - this.trigger.getWidth();
10713 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10714 //            this.trigger.setStyle('left', x+'px');
10715 //        }
10716     },
10717
10718     // private
10719     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10720
10721     // private
10722     getResizeEl : function(){
10723         return this.inputEl();
10724     },
10725
10726     // private
10727     getPositionEl : function(){
10728         return this.inputEl();
10729     },
10730
10731     // private
10732     alignErrorIcon : function(){
10733         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10734     },
10735
10736     // private
10737     initEvents : function(){
10738         
10739         this.createList();
10740         
10741         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10742         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10743         if(!this.multiple && this.showToggleBtn){
10744             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10745             if(this.hideTrigger){
10746                 this.trigger.setDisplayed(false);
10747             }
10748             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10749         }
10750         
10751         if(this.multiple){
10752             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10753         }
10754         
10755         if(this.removable && !this.editable && !this.tickable){
10756             var close = this.closeTriggerEl();
10757             
10758             if(close){
10759                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10760                 close.on('click', this.removeBtnClick, this, close);
10761             }
10762         }
10763         
10764         //this.trigger.addClassOnOver('x-form-trigger-over');
10765         //this.trigger.addClassOnClick('x-form-trigger-click');
10766         
10767         //if(!this.width){
10768         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10769         //}
10770     },
10771     
10772     closeTriggerEl : function()
10773     {
10774         var close = this.el.select('.roo-combo-removable-btn', true).first();
10775         return close ? close : false;
10776     },
10777     
10778     removeBtnClick : function(e, h, el)
10779     {
10780         e.preventDefault();
10781         
10782         if(this.fireEvent("remove", this) !== false){
10783             this.reset();
10784             this.fireEvent("afterremove", this)
10785         }
10786     },
10787     
10788     createList : function()
10789     {
10790         this.list = Roo.get(document.body).createChild({
10791             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10792             cls: 'typeahead typeahead-long dropdown-menu',
10793             style: 'display:none'
10794         });
10795         
10796         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10797         
10798     },
10799
10800     // private
10801     initTrigger : function(){
10802        
10803     },
10804
10805     // private
10806     onDestroy : function(){
10807         if(this.trigger){
10808             this.trigger.removeAllListeners();
10809           //  this.trigger.remove();
10810         }
10811         //if(this.wrap){
10812         //    this.wrap.remove();
10813         //}
10814         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10815     },
10816
10817     // private
10818     onFocus : function(){
10819         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10820         /*
10821         if(!this.mimicing){
10822             this.wrap.addClass('x-trigger-wrap-focus');
10823             this.mimicing = true;
10824             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10825             if(this.monitorTab){
10826                 this.el.on("keydown", this.checkTab, this);
10827             }
10828         }
10829         */
10830     },
10831
10832     // private
10833     checkTab : function(e){
10834         if(e.getKey() == e.TAB){
10835             this.triggerBlur();
10836         }
10837     },
10838
10839     // private
10840     onBlur : function(){
10841         // do nothing
10842     },
10843
10844     // private
10845     mimicBlur : function(e, t){
10846         /*
10847         if(!this.wrap.contains(t) && this.validateBlur()){
10848             this.triggerBlur();
10849         }
10850         */
10851     },
10852
10853     // private
10854     triggerBlur : function(){
10855         this.mimicing = false;
10856         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10857         if(this.monitorTab){
10858             this.el.un("keydown", this.checkTab, this);
10859         }
10860         //this.wrap.removeClass('x-trigger-wrap-focus');
10861         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10862     },
10863
10864     // private
10865     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10866     validateBlur : function(e, t){
10867         return true;
10868     },
10869
10870     // private
10871     onDisable : function(){
10872         this.inputEl().dom.disabled = true;
10873         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10874         //if(this.wrap){
10875         //    this.wrap.addClass('x-item-disabled');
10876         //}
10877     },
10878
10879     // private
10880     onEnable : function(){
10881         this.inputEl().dom.disabled = false;
10882         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10883         //if(this.wrap){
10884         //    this.el.removeClass('x-item-disabled');
10885         //}
10886     },
10887
10888     // private
10889     onShow : function(){
10890         var ae = this.getActionEl();
10891         
10892         if(ae){
10893             ae.dom.style.display = '';
10894             ae.dom.style.visibility = 'visible';
10895         }
10896     },
10897
10898     // private
10899     
10900     onHide : function(){
10901         var ae = this.getActionEl();
10902         ae.dom.style.display = 'none';
10903     },
10904
10905     /**
10906      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10907      * by an implementing function.
10908      * @method
10909      * @param {EventObject} e
10910      */
10911     onTriggerClick : Roo.emptyFn
10912 });
10913  /*
10914  * Based on:
10915  * Ext JS Library 1.1.1
10916  * Copyright(c) 2006-2007, Ext JS, LLC.
10917  *
10918  * Originally Released Under LGPL - original licence link has changed is not relivant.
10919  *
10920  * Fork - LGPL
10921  * <script type="text/javascript">
10922  */
10923
10924
10925 /**
10926  * @class Roo.data.SortTypes
10927  * @singleton
10928  * Defines the default sorting (casting?) comparison functions used when sorting data.
10929  */
10930 Roo.data.SortTypes = {
10931     /**
10932      * Default sort that does nothing
10933      * @param {Mixed} s The value being converted
10934      * @return {Mixed} The comparison value
10935      */
10936     none : function(s){
10937         return s;
10938     },
10939     
10940     /**
10941      * The regular expression used to strip tags
10942      * @type {RegExp}
10943      * @property
10944      */
10945     stripTagsRE : /<\/?[^>]+>/gi,
10946     
10947     /**
10948      * Strips all HTML tags to sort on text only
10949      * @param {Mixed} s The value being converted
10950      * @return {String} The comparison value
10951      */
10952     asText : function(s){
10953         return String(s).replace(this.stripTagsRE, "");
10954     },
10955     
10956     /**
10957      * Strips all HTML tags to sort on text only - Case insensitive
10958      * @param {Mixed} s The value being converted
10959      * @return {String} The comparison value
10960      */
10961     asUCText : function(s){
10962         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10963     },
10964     
10965     /**
10966      * Case insensitive string
10967      * @param {Mixed} s The value being converted
10968      * @return {String} The comparison value
10969      */
10970     asUCString : function(s) {
10971         return String(s).toUpperCase();
10972     },
10973     
10974     /**
10975      * Date sorting
10976      * @param {Mixed} s The value being converted
10977      * @return {Number} The comparison value
10978      */
10979     asDate : function(s) {
10980         if(!s){
10981             return 0;
10982         }
10983         if(s instanceof Date){
10984             return s.getTime();
10985         }
10986         return Date.parse(String(s));
10987     },
10988     
10989     /**
10990      * Float sorting
10991      * @param {Mixed} s The value being converted
10992      * @return {Float} The comparison value
10993      */
10994     asFloat : function(s) {
10995         var val = parseFloat(String(s).replace(/,/g, ""));
10996         if(isNaN(val)) {
10997             val = 0;
10998         }
10999         return val;
11000     },
11001     
11002     /**
11003      * Integer sorting
11004      * @param {Mixed} s The value being converted
11005      * @return {Number} The comparison value
11006      */
11007     asInt : function(s) {
11008         var val = parseInt(String(s).replace(/,/g, ""));
11009         if(isNaN(val)) {
11010             val = 0;
11011         }
11012         return val;
11013     }
11014 };/*
11015  * Based on:
11016  * Ext JS Library 1.1.1
11017  * Copyright(c) 2006-2007, Ext JS, LLC.
11018  *
11019  * Originally Released Under LGPL - original licence link has changed is not relivant.
11020  *
11021  * Fork - LGPL
11022  * <script type="text/javascript">
11023  */
11024
11025 /**
11026 * @class Roo.data.Record
11027  * Instances of this class encapsulate both record <em>definition</em> information, and record
11028  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11029  * to access Records cached in an {@link Roo.data.Store} object.<br>
11030  * <p>
11031  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11032  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11033  * objects.<br>
11034  * <p>
11035  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11036  * @constructor
11037  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11038  * {@link #create}. The parameters are the same.
11039  * @param {Array} data An associative Array of data values keyed by the field name.
11040  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11041  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11042  * not specified an integer id is generated.
11043  */
11044 Roo.data.Record = function(data, id){
11045     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11046     this.data = data;
11047 };
11048
11049 /**
11050  * Generate a constructor for a specific record layout.
11051  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11052  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11053  * Each field definition object may contain the following properties: <ul>
11054  * <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,
11055  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11056  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11057  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11058  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11059  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11060  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11061  * this may be omitted.</p></li>
11062  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11063  * <ul><li>auto (Default, implies no conversion)</li>
11064  * <li>string</li>
11065  * <li>int</li>
11066  * <li>float</li>
11067  * <li>boolean</li>
11068  * <li>date</li></ul></p></li>
11069  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11070  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11071  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11072  * by the Reader into an object that will be stored in the Record. It is passed the
11073  * following parameters:<ul>
11074  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11075  * </ul></p></li>
11076  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11077  * </ul>
11078  * <br>usage:<br><pre><code>
11079 var TopicRecord = Roo.data.Record.create(
11080     {name: 'title', mapping: 'topic_title'},
11081     {name: 'author', mapping: 'username'},
11082     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11083     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11084     {name: 'lastPoster', mapping: 'user2'},
11085     {name: 'excerpt', mapping: 'post_text'}
11086 );
11087
11088 var myNewRecord = new TopicRecord({
11089     title: 'Do my job please',
11090     author: 'noobie',
11091     totalPosts: 1,
11092     lastPost: new Date(),
11093     lastPoster: 'Animal',
11094     excerpt: 'No way dude!'
11095 });
11096 myStore.add(myNewRecord);
11097 </code></pre>
11098  * @method create
11099  * @static
11100  */
11101 Roo.data.Record.create = function(o){
11102     var f = function(){
11103         f.superclass.constructor.apply(this, arguments);
11104     };
11105     Roo.extend(f, Roo.data.Record);
11106     var p = f.prototype;
11107     p.fields = new Roo.util.MixedCollection(false, function(field){
11108         return field.name;
11109     });
11110     for(var i = 0, len = o.length; i < len; i++){
11111         p.fields.add(new Roo.data.Field(o[i]));
11112     }
11113     f.getField = function(name){
11114         return p.fields.get(name);  
11115     };
11116     return f;
11117 };
11118
11119 Roo.data.Record.AUTO_ID = 1000;
11120 Roo.data.Record.EDIT = 'edit';
11121 Roo.data.Record.REJECT = 'reject';
11122 Roo.data.Record.COMMIT = 'commit';
11123
11124 Roo.data.Record.prototype = {
11125     /**
11126      * Readonly flag - true if this record has been modified.
11127      * @type Boolean
11128      */
11129     dirty : false,
11130     editing : false,
11131     error: null,
11132     modified: null,
11133
11134     // private
11135     join : function(store){
11136         this.store = store;
11137     },
11138
11139     /**
11140      * Set the named field to the specified value.
11141      * @param {String} name The name of the field to set.
11142      * @param {Object} value The value to set the field to.
11143      */
11144     set : function(name, value){
11145         if(this.data[name] == value){
11146             return;
11147         }
11148         this.dirty = true;
11149         if(!this.modified){
11150             this.modified = {};
11151         }
11152         if(typeof this.modified[name] == 'undefined'){
11153             this.modified[name] = this.data[name];
11154         }
11155         this.data[name] = value;
11156         if(!this.editing && this.store){
11157             this.store.afterEdit(this);
11158         }       
11159     },
11160
11161     /**
11162      * Get the value of the named field.
11163      * @param {String} name The name of the field to get the value of.
11164      * @return {Object} The value of the field.
11165      */
11166     get : function(name){
11167         return this.data[name]; 
11168     },
11169
11170     // private
11171     beginEdit : function(){
11172         this.editing = true;
11173         this.modified = {}; 
11174     },
11175
11176     // private
11177     cancelEdit : function(){
11178         this.editing = false;
11179         delete this.modified;
11180     },
11181
11182     // private
11183     endEdit : function(){
11184         this.editing = false;
11185         if(this.dirty && this.store){
11186             this.store.afterEdit(this);
11187         }
11188     },
11189
11190     /**
11191      * Usually called by the {@link Roo.data.Store} which owns the Record.
11192      * Rejects all changes made to the Record since either creation, or the last commit operation.
11193      * Modified fields are reverted to their original values.
11194      * <p>
11195      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11196      * of reject operations.
11197      */
11198     reject : function(){
11199         var m = this.modified;
11200         for(var n in m){
11201             if(typeof m[n] != "function"){
11202                 this.data[n] = m[n];
11203             }
11204         }
11205         this.dirty = false;
11206         delete this.modified;
11207         this.editing = false;
11208         if(this.store){
11209             this.store.afterReject(this);
11210         }
11211     },
11212
11213     /**
11214      * Usually called by the {@link Roo.data.Store} which owns the Record.
11215      * Commits all changes made to the Record since either creation, or the last commit operation.
11216      * <p>
11217      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11218      * of commit operations.
11219      */
11220     commit : function(){
11221         this.dirty = false;
11222         delete this.modified;
11223         this.editing = false;
11224         if(this.store){
11225             this.store.afterCommit(this);
11226         }
11227     },
11228
11229     // private
11230     hasError : function(){
11231         return this.error != null;
11232     },
11233
11234     // private
11235     clearError : function(){
11236         this.error = null;
11237     },
11238
11239     /**
11240      * Creates a copy of this record.
11241      * @param {String} id (optional) A new record id if you don't want to use this record's id
11242      * @return {Record}
11243      */
11244     copy : function(newId) {
11245         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11246     }
11247 };/*
11248  * Based on:
11249  * Ext JS Library 1.1.1
11250  * Copyright(c) 2006-2007, Ext JS, LLC.
11251  *
11252  * Originally Released Under LGPL - original licence link has changed is not relivant.
11253  *
11254  * Fork - LGPL
11255  * <script type="text/javascript">
11256  */
11257
11258
11259
11260 /**
11261  * @class Roo.data.Store
11262  * @extends Roo.util.Observable
11263  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11264  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11265  * <p>
11266  * 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
11267  * has no knowledge of the format of the data returned by the Proxy.<br>
11268  * <p>
11269  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11270  * instances from the data object. These records are cached and made available through accessor functions.
11271  * @constructor
11272  * Creates a new Store.
11273  * @param {Object} config A config object containing the objects needed for the Store to access data,
11274  * and read the data into Records.
11275  */
11276 Roo.data.Store = function(config){
11277     this.data = new Roo.util.MixedCollection(false);
11278     this.data.getKey = function(o){
11279         return o.id;
11280     };
11281     this.baseParams = {};
11282     // private
11283     this.paramNames = {
11284         "start" : "start",
11285         "limit" : "limit",
11286         "sort" : "sort",
11287         "dir" : "dir",
11288         "multisort" : "_multisort"
11289     };
11290
11291     if(config && config.data){
11292         this.inlineData = config.data;
11293         delete config.data;
11294     }
11295
11296     Roo.apply(this, config);
11297     
11298     if(this.reader){ // reader passed
11299         this.reader = Roo.factory(this.reader, Roo.data);
11300         this.reader.xmodule = this.xmodule || false;
11301         if(!this.recordType){
11302             this.recordType = this.reader.recordType;
11303         }
11304         if(this.reader.onMetaChange){
11305             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11306         }
11307     }
11308
11309     if(this.recordType){
11310         this.fields = this.recordType.prototype.fields;
11311     }
11312     this.modified = [];
11313
11314     this.addEvents({
11315         /**
11316          * @event datachanged
11317          * Fires when the data cache has changed, and a widget which is using this Store
11318          * as a Record cache should refresh its view.
11319          * @param {Store} this
11320          */
11321         datachanged : true,
11322         /**
11323          * @event metachange
11324          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11325          * @param {Store} this
11326          * @param {Object} meta The JSON metadata
11327          */
11328         metachange : true,
11329         /**
11330          * @event add
11331          * Fires when Records have been added to the Store
11332          * @param {Store} this
11333          * @param {Roo.data.Record[]} records The array of Records added
11334          * @param {Number} index The index at which the record(s) were added
11335          */
11336         add : true,
11337         /**
11338          * @event remove
11339          * Fires when a Record has been removed from the Store
11340          * @param {Store} this
11341          * @param {Roo.data.Record} record The Record that was removed
11342          * @param {Number} index The index at which the record was removed
11343          */
11344         remove : true,
11345         /**
11346          * @event update
11347          * Fires when a Record has been updated
11348          * @param {Store} this
11349          * @param {Roo.data.Record} record The Record that was updated
11350          * @param {String} operation The update operation being performed.  Value may be one of:
11351          * <pre><code>
11352  Roo.data.Record.EDIT
11353  Roo.data.Record.REJECT
11354  Roo.data.Record.COMMIT
11355          * </code></pre>
11356          */
11357         update : true,
11358         /**
11359          * @event clear
11360          * Fires when the data cache has been cleared.
11361          * @param {Store} this
11362          */
11363         clear : true,
11364         /**
11365          * @event beforeload
11366          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11367          * the load action will be canceled.
11368          * @param {Store} this
11369          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11370          */
11371         beforeload : true,
11372         /**
11373          * @event beforeloadadd
11374          * Fires after a new set of Records has been loaded.
11375          * @param {Store} this
11376          * @param {Roo.data.Record[]} records The Records that were loaded
11377          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11378          */
11379         beforeloadadd : true,
11380         /**
11381          * @event load
11382          * Fires after a new set of Records has been loaded, before they are added to the store.
11383          * @param {Store} this
11384          * @param {Roo.data.Record[]} records The Records that were loaded
11385          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11386          * @params {Object} return from reader
11387          */
11388         load : true,
11389         /**
11390          * @event loadexception
11391          * Fires if an exception occurs in the Proxy during loading.
11392          * Called with the signature of the Proxy's "loadexception" event.
11393          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11394          * 
11395          * @param {Proxy} 
11396          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11397          * @param {Object} load options 
11398          * @param {Object} jsonData from your request (normally this contains the Exception)
11399          */
11400         loadexception : true
11401     });
11402     
11403     if(this.proxy){
11404         this.proxy = Roo.factory(this.proxy, Roo.data);
11405         this.proxy.xmodule = this.xmodule || false;
11406         this.relayEvents(this.proxy,  ["loadexception"]);
11407     }
11408     this.sortToggle = {};
11409     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11410
11411     Roo.data.Store.superclass.constructor.call(this);
11412
11413     if(this.inlineData){
11414         this.loadData(this.inlineData);
11415         delete this.inlineData;
11416     }
11417 };
11418
11419 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11420      /**
11421     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11422     * without a remote query - used by combo/forms at present.
11423     */
11424     
11425     /**
11426     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11427     */
11428     /**
11429     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11430     */
11431     /**
11432     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11433     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11434     */
11435     /**
11436     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11437     * on any HTTP request
11438     */
11439     /**
11440     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11441     */
11442     /**
11443     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11444     */
11445     multiSort: false,
11446     /**
11447     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11448     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11449     */
11450     remoteSort : false,
11451
11452     /**
11453     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11454      * loaded or when a record is removed. (defaults to false).
11455     */
11456     pruneModifiedRecords : false,
11457
11458     // private
11459     lastOptions : null,
11460
11461     /**
11462      * Add Records to the Store and fires the add event.
11463      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11464      */
11465     add : function(records){
11466         records = [].concat(records);
11467         for(var i = 0, len = records.length; i < len; i++){
11468             records[i].join(this);
11469         }
11470         var index = this.data.length;
11471         this.data.addAll(records);
11472         this.fireEvent("add", this, records, index);
11473     },
11474
11475     /**
11476      * Remove a Record from the Store and fires the remove event.
11477      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11478      */
11479     remove : function(record){
11480         var index = this.data.indexOf(record);
11481         this.data.removeAt(index);
11482  
11483         if(this.pruneModifiedRecords){
11484             this.modified.remove(record);
11485         }
11486         this.fireEvent("remove", this, record, index);
11487     },
11488
11489     /**
11490      * Remove all Records from the Store and fires the clear event.
11491      */
11492     removeAll : function(){
11493         this.data.clear();
11494         if(this.pruneModifiedRecords){
11495             this.modified = [];
11496         }
11497         this.fireEvent("clear", this);
11498     },
11499
11500     /**
11501      * Inserts Records to the Store at the given index and fires the add event.
11502      * @param {Number} index The start index at which to insert the passed Records.
11503      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11504      */
11505     insert : function(index, records){
11506         records = [].concat(records);
11507         for(var i = 0, len = records.length; i < len; i++){
11508             this.data.insert(index, records[i]);
11509             records[i].join(this);
11510         }
11511         this.fireEvent("add", this, records, index);
11512     },
11513
11514     /**
11515      * Get the index within the cache of the passed Record.
11516      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11517      * @return {Number} The index of the passed Record. Returns -1 if not found.
11518      */
11519     indexOf : function(record){
11520         return this.data.indexOf(record);
11521     },
11522
11523     /**
11524      * Get the index within the cache of the Record with the passed id.
11525      * @param {String} id The id of the Record to find.
11526      * @return {Number} The index of the Record. Returns -1 if not found.
11527      */
11528     indexOfId : function(id){
11529         return this.data.indexOfKey(id);
11530     },
11531
11532     /**
11533      * Get the Record with the specified id.
11534      * @param {String} id The id of the Record to find.
11535      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11536      */
11537     getById : function(id){
11538         return this.data.key(id);
11539     },
11540
11541     /**
11542      * Get the Record at the specified index.
11543      * @param {Number} index The index of the Record to find.
11544      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11545      */
11546     getAt : function(index){
11547         return this.data.itemAt(index);
11548     },
11549
11550     /**
11551      * Returns a range of Records between specified indices.
11552      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11553      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11554      * @return {Roo.data.Record[]} An array of Records
11555      */
11556     getRange : function(start, end){
11557         return this.data.getRange(start, end);
11558     },
11559
11560     // private
11561     storeOptions : function(o){
11562         o = Roo.apply({}, o);
11563         delete o.callback;
11564         delete o.scope;
11565         this.lastOptions = o;
11566     },
11567
11568     /**
11569      * Loads the Record cache from the configured Proxy using the configured Reader.
11570      * <p>
11571      * If using remote paging, then the first load call must specify the <em>start</em>
11572      * and <em>limit</em> properties in the options.params property to establish the initial
11573      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11574      * <p>
11575      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11576      * and this call will return before the new data has been loaded. Perform any post-processing
11577      * in a callback function, or in a "load" event handler.</strong>
11578      * <p>
11579      * @param {Object} options An object containing properties which control loading options:<ul>
11580      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11581      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11582      * passed the following arguments:<ul>
11583      * <li>r : Roo.data.Record[]</li>
11584      * <li>options: Options object from the load call</li>
11585      * <li>success: Boolean success indicator</li></ul></li>
11586      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11587      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11588      * </ul>
11589      */
11590     load : function(options){
11591         options = options || {};
11592         if(this.fireEvent("beforeload", this, options) !== false){
11593             this.storeOptions(options);
11594             var p = Roo.apply(options.params || {}, this.baseParams);
11595             // if meta was not loaded from remote source.. try requesting it.
11596             if (!this.reader.metaFromRemote) {
11597                 p._requestMeta = 1;
11598             }
11599             if(this.sortInfo && this.remoteSort){
11600                 var pn = this.paramNames;
11601                 p[pn["sort"]] = this.sortInfo.field;
11602                 p[pn["dir"]] = this.sortInfo.direction;
11603             }
11604             if (this.multiSort) {
11605                 var pn = this.paramNames;
11606                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11607             }
11608             
11609             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11610         }
11611     },
11612
11613     /**
11614      * Reloads the Record cache from the configured Proxy using the configured Reader and
11615      * the options from the last load operation performed.
11616      * @param {Object} options (optional) An object containing properties which may override the options
11617      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11618      * the most recently used options are reused).
11619      */
11620     reload : function(options){
11621         this.load(Roo.applyIf(options||{}, this.lastOptions));
11622     },
11623
11624     // private
11625     // Called as a callback by the Reader during a load operation.
11626     loadRecords : function(o, options, success){
11627         if(!o || success === false){
11628             if(success !== false){
11629                 this.fireEvent("load", this, [], options, o);
11630             }
11631             if(options.callback){
11632                 options.callback.call(options.scope || this, [], options, false);
11633             }
11634             return;
11635         }
11636         // if data returned failure - throw an exception.
11637         if (o.success === false) {
11638             // show a message if no listener is registered.
11639             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11640                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11641             }
11642             // loadmask wil be hooked into this..
11643             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11644             return;
11645         }
11646         var r = o.records, t = o.totalRecords || r.length;
11647         
11648         this.fireEvent("beforeloadadd", this, r, options, o);
11649         
11650         if(!options || options.add !== true){
11651             if(this.pruneModifiedRecords){
11652                 this.modified = [];
11653             }
11654             for(var i = 0, len = r.length; i < len; i++){
11655                 r[i].join(this);
11656             }
11657             if(this.snapshot){
11658                 this.data = this.snapshot;
11659                 delete this.snapshot;
11660             }
11661             this.data.clear();
11662             this.data.addAll(r);
11663             this.totalLength = t;
11664             this.applySort();
11665             this.fireEvent("datachanged", this);
11666         }else{
11667             this.totalLength = Math.max(t, this.data.length+r.length);
11668             this.add(r);
11669         }
11670         
11671         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11672                 
11673             var e = new Roo.data.Record({});
11674
11675             e.set(this.parent.displayField, this.parent.emptyTitle);
11676             e.set(this.parent.valueField, '');
11677
11678             this.insert(0, e);
11679         }
11680             
11681         this.fireEvent("load", this, r, options, o);
11682         if(options.callback){
11683             options.callback.call(options.scope || this, r, options, true);
11684         }
11685     },
11686
11687
11688     /**
11689      * Loads data from a passed data block. A Reader which understands the format of the data
11690      * must have been configured in the constructor.
11691      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11692      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11693      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11694      */
11695     loadData : function(o, append){
11696         var r = this.reader.readRecords(o);
11697         this.loadRecords(r, {add: append}, true);
11698     },
11699
11700     /**
11701      * Gets the number of cached records.
11702      * <p>
11703      * <em>If using paging, this may not be the total size of the dataset. If the data object
11704      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11705      * the data set size</em>
11706      */
11707     getCount : function(){
11708         return this.data.length || 0;
11709     },
11710
11711     /**
11712      * Gets the total number of records in the dataset as returned by the server.
11713      * <p>
11714      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11715      * the dataset size</em>
11716      */
11717     getTotalCount : function(){
11718         return this.totalLength || 0;
11719     },
11720
11721     /**
11722      * Returns the sort state of the Store as an object with two properties:
11723      * <pre><code>
11724  field {String} The name of the field by which the Records are sorted
11725  direction {String} The sort order, "ASC" or "DESC"
11726      * </code></pre>
11727      */
11728     getSortState : function(){
11729         return this.sortInfo;
11730     },
11731
11732     // private
11733     applySort : function(){
11734         if(this.sortInfo && !this.remoteSort){
11735             var s = this.sortInfo, f = s.field;
11736             var st = this.fields.get(f).sortType;
11737             var fn = function(r1, r2){
11738                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11739                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11740             };
11741             this.data.sort(s.direction, fn);
11742             if(this.snapshot && this.snapshot != this.data){
11743                 this.snapshot.sort(s.direction, fn);
11744             }
11745         }
11746     },
11747
11748     /**
11749      * Sets the default sort column and order to be used by the next load operation.
11750      * @param {String} fieldName The name of the field to sort by.
11751      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11752      */
11753     setDefaultSort : function(field, dir){
11754         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11755     },
11756
11757     /**
11758      * Sort the Records.
11759      * If remote sorting is used, the sort is performed on the server, and the cache is
11760      * reloaded. If local sorting is used, the cache is sorted internally.
11761      * @param {String} fieldName The name of the field to sort by.
11762      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11763      */
11764     sort : function(fieldName, dir){
11765         var f = this.fields.get(fieldName);
11766         if(!dir){
11767             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11768             
11769             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11770                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11771             }else{
11772                 dir = f.sortDir;
11773             }
11774         }
11775         this.sortToggle[f.name] = dir;
11776         this.sortInfo = {field: f.name, direction: dir};
11777         if(!this.remoteSort){
11778             this.applySort();
11779             this.fireEvent("datachanged", this);
11780         }else{
11781             this.load(this.lastOptions);
11782         }
11783     },
11784
11785     /**
11786      * Calls the specified function for each of the Records in the cache.
11787      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11788      * Returning <em>false</em> aborts and exits the iteration.
11789      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11790      */
11791     each : function(fn, scope){
11792         this.data.each(fn, scope);
11793     },
11794
11795     /**
11796      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11797      * (e.g., during paging).
11798      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11799      */
11800     getModifiedRecords : function(){
11801         return this.modified;
11802     },
11803
11804     // private
11805     createFilterFn : function(property, value, anyMatch){
11806         if(!value.exec){ // not a regex
11807             value = String(value);
11808             if(value.length == 0){
11809                 return false;
11810             }
11811             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11812         }
11813         return function(r){
11814             return value.test(r.data[property]);
11815         };
11816     },
11817
11818     /**
11819      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11820      * @param {String} property A field on your records
11821      * @param {Number} start The record index to start at (defaults to 0)
11822      * @param {Number} end The last record index to include (defaults to length - 1)
11823      * @return {Number} The sum
11824      */
11825     sum : function(property, start, end){
11826         var rs = this.data.items, v = 0;
11827         start = start || 0;
11828         end = (end || end === 0) ? end : rs.length-1;
11829
11830         for(var i = start; i <= end; i++){
11831             v += (rs[i].data[property] || 0);
11832         }
11833         return v;
11834     },
11835
11836     /**
11837      * Filter the records by a specified property.
11838      * @param {String} field A field on your records
11839      * @param {String/RegExp} value Either a string that the field
11840      * should start with or a RegExp to test against the field
11841      * @param {Boolean} anyMatch True to match any part not just the beginning
11842      */
11843     filter : function(property, value, anyMatch){
11844         var fn = this.createFilterFn(property, value, anyMatch);
11845         return fn ? this.filterBy(fn) : this.clearFilter();
11846     },
11847
11848     /**
11849      * Filter by a function. The specified function will be called with each
11850      * record in this data source. If the function returns true the record is included,
11851      * otherwise it is filtered.
11852      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11853      * @param {Object} scope (optional) The scope of the function (defaults to this)
11854      */
11855     filterBy : function(fn, scope){
11856         this.snapshot = this.snapshot || this.data;
11857         this.data = this.queryBy(fn, scope||this);
11858         this.fireEvent("datachanged", this);
11859     },
11860
11861     /**
11862      * Query the records by a specified property.
11863      * @param {String} field A field on your records
11864      * @param {String/RegExp} value Either a string that the field
11865      * should start with or a RegExp to test against the field
11866      * @param {Boolean} anyMatch True to match any part not just the beginning
11867      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11868      */
11869     query : function(property, value, anyMatch){
11870         var fn = this.createFilterFn(property, value, anyMatch);
11871         return fn ? this.queryBy(fn) : this.data.clone();
11872     },
11873
11874     /**
11875      * Query by a function. The specified function will be called with each
11876      * record in this data source. If the function returns true the record is included
11877      * in the results.
11878      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11879      * @param {Object} scope (optional) The scope of the function (defaults to this)
11880       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11881      **/
11882     queryBy : function(fn, scope){
11883         var data = this.snapshot || this.data;
11884         return data.filterBy(fn, scope||this);
11885     },
11886
11887     /**
11888      * Collects unique values for a particular dataIndex from this store.
11889      * @param {String} dataIndex The property to collect
11890      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11891      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11892      * @return {Array} An array of the unique values
11893      **/
11894     collect : function(dataIndex, allowNull, bypassFilter){
11895         var d = (bypassFilter === true && this.snapshot) ?
11896                 this.snapshot.items : this.data.items;
11897         var v, sv, r = [], l = {};
11898         for(var i = 0, len = d.length; i < len; i++){
11899             v = d[i].data[dataIndex];
11900             sv = String(v);
11901             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11902                 l[sv] = true;
11903                 r[r.length] = v;
11904             }
11905         }
11906         return r;
11907     },
11908
11909     /**
11910      * Revert to a view of the Record cache with no filtering applied.
11911      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11912      */
11913     clearFilter : function(suppressEvent){
11914         if(this.snapshot && this.snapshot != this.data){
11915             this.data = this.snapshot;
11916             delete this.snapshot;
11917             if(suppressEvent !== true){
11918                 this.fireEvent("datachanged", this);
11919             }
11920         }
11921     },
11922
11923     // private
11924     afterEdit : function(record){
11925         if(this.modified.indexOf(record) == -1){
11926             this.modified.push(record);
11927         }
11928         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11929     },
11930     
11931     // private
11932     afterReject : function(record){
11933         this.modified.remove(record);
11934         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11935     },
11936
11937     // private
11938     afterCommit : function(record){
11939         this.modified.remove(record);
11940         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11941     },
11942
11943     /**
11944      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11945      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11946      */
11947     commitChanges : function(){
11948         var m = this.modified.slice(0);
11949         this.modified = [];
11950         for(var i = 0, len = m.length; i < len; i++){
11951             m[i].commit();
11952         }
11953     },
11954
11955     /**
11956      * Cancel outstanding changes on all changed records.
11957      */
11958     rejectChanges : function(){
11959         var m = this.modified.slice(0);
11960         this.modified = [];
11961         for(var i = 0, len = m.length; i < len; i++){
11962             m[i].reject();
11963         }
11964     },
11965
11966     onMetaChange : function(meta, rtype, o){
11967         this.recordType = rtype;
11968         this.fields = rtype.prototype.fields;
11969         delete this.snapshot;
11970         this.sortInfo = meta.sortInfo || this.sortInfo;
11971         this.modified = [];
11972         this.fireEvent('metachange', this, this.reader.meta);
11973     },
11974     
11975     moveIndex : function(data, type)
11976     {
11977         var index = this.indexOf(data);
11978         
11979         var newIndex = index + type;
11980         
11981         this.remove(data);
11982         
11983         this.insert(newIndex, data);
11984         
11985     }
11986 });/*
11987  * Based on:
11988  * Ext JS Library 1.1.1
11989  * Copyright(c) 2006-2007, Ext JS, LLC.
11990  *
11991  * Originally Released Under LGPL - original licence link has changed is not relivant.
11992  *
11993  * Fork - LGPL
11994  * <script type="text/javascript">
11995  */
11996
11997 /**
11998  * @class Roo.data.SimpleStore
11999  * @extends Roo.data.Store
12000  * Small helper class to make creating Stores from Array data easier.
12001  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12002  * @cfg {Array} fields An array of field definition objects, or field name strings.
12003  * @cfg {Array} data The multi-dimensional array of data
12004  * @constructor
12005  * @param {Object} config
12006  */
12007 Roo.data.SimpleStore = function(config){
12008     Roo.data.SimpleStore.superclass.constructor.call(this, {
12009         isLocal : true,
12010         reader: new Roo.data.ArrayReader({
12011                 id: config.id
12012             },
12013             Roo.data.Record.create(config.fields)
12014         ),
12015         proxy : new Roo.data.MemoryProxy(config.data)
12016     });
12017     this.load();
12018 };
12019 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12020  * Based on:
12021  * Ext JS Library 1.1.1
12022  * Copyright(c) 2006-2007, Ext JS, LLC.
12023  *
12024  * Originally Released Under LGPL - original licence link has changed is not relivant.
12025  *
12026  * Fork - LGPL
12027  * <script type="text/javascript">
12028  */
12029
12030 /**
12031 /**
12032  * @extends Roo.data.Store
12033  * @class Roo.data.JsonStore
12034  * Small helper class to make creating Stores for JSON data easier. <br/>
12035 <pre><code>
12036 var store = new Roo.data.JsonStore({
12037     url: 'get-images.php',
12038     root: 'images',
12039     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12040 });
12041 </code></pre>
12042  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12043  * JsonReader and HttpProxy (unless inline data is provided).</b>
12044  * @cfg {Array} fields An array of field definition objects, or field name strings.
12045  * @constructor
12046  * @param {Object} config
12047  */
12048 Roo.data.JsonStore = function(c){
12049     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12050         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12051         reader: new Roo.data.JsonReader(c, c.fields)
12052     }));
12053 };
12054 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12055  * Based on:
12056  * Ext JS Library 1.1.1
12057  * Copyright(c) 2006-2007, Ext JS, LLC.
12058  *
12059  * Originally Released Under LGPL - original licence link has changed is not relivant.
12060  *
12061  * Fork - LGPL
12062  * <script type="text/javascript">
12063  */
12064
12065  
12066 Roo.data.Field = function(config){
12067     if(typeof config == "string"){
12068         config = {name: config};
12069     }
12070     Roo.apply(this, config);
12071     
12072     if(!this.type){
12073         this.type = "auto";
12074     }
12075     
12076     var st = Roo.data.SortTypes;
12077     // named sortTypes are supported, here we look them up
12078     if(typeof this.sortType == "string"){
12079         this.sortType = st[this.sortType];
12080     }
12081     
12082     // set default sortType for strings and dates
12083     if(!this.sortType){
12084         switch(this.type){
12085             case "string":
12086                 this.sortType = st.asUCString;
12087                 break;
12088             case "date":
12089                 this.sortType = st.asDate;
12090                 break;
12091             default:
12092                 this.sortType = st.none;
12093         }
12094     }
12095
12096     // define once
12097     var stripRe = /[\$,%]/g;
12098
12099     // prebuilt conversion function for this field, instead of
12100     // switching every time we're reading a value
12101     if(!this.convert){
12102         var cv, dateFormat = this.dateFormat;
12103         switch(this.type){
12104             case "":
12105             case "auto":
12106             case undefined:
12107                 cv = function(v){ return v; };
12108                 break;
12109             case "string":
12110                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12111                 break;
12112             case "int":
12113                 cv = function(v){
12114                     return v !== undefined && v !== null && v !== '' ?
12115                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12116                     };
12117                 break;
12118             case "float":
12119                 cv = function(v){
12120                     return v !== undefined && v !== null && v !== '' ?
12121                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12122                     };
12123                 break;
12124             case "bool":
12125             case "boolean":
12126                 cv = function(v){ return v === true || v === "true" || v == 1; };
12127                 break;
12128             case "date":
12129                 cv = function(v){
12130                     if(!v){
12131                         return '';
12132                     }
12133                     if(v instanceof Date){
12134                         return v;
12135                     }
12136                     if(dateFormat){
12137                         if(dateFormat == "timestamp"){
12138                             return new Date(v*1000);
12139                         }
12140                         return Date.parseDate(v, dateFormat);
12141                     }
12142                     var parsed = Date.parse(v);
12143                     return parsed ? new Date(parsed) : null;
12144                 };
12145              break;
12146             
12147         }
12148         this.convert = cv;
12149     }
12150 };
12151
12152 Roo.data.Field.prototype = {
12153     dateFormat: null,
12154     defaultValue: "",
12155     mapping: null,
12156     sortType : null,
12157     sortDir : "ASC"
12158 };/*
12159  * Based on:
12160  * Ext JS Library 1.1.1
12161  * Copyright(c) 2006-2007, Ext JS, LLC.
12162  *
12163  * Originally Released Under LGPL - original licence link has changed is not relivant.
12164  *
12165  * Fork - LGPL
12166  * <script type="text/javascript">
12167  */
12168  
12169 // Base class for reading structured data from a data source.  This class is intended to be
12170 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12171
12172 /**
12173  * @class Roo.data.DataReader
12174  * Base class for reading structured data from a data source.  This class is intended to be
12175  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12176  */
12177
12178 Roo.data.DataReader = function(meta, recordType){
12179     
12180     this.meta = meta;
12181     
12182     this.recordType = recordType instanceof Array ? 
12183         Roo.data.Record.create(recordType) : recordType;
12184 };
12185
12186 Roo.data.DataReader.prototype = {
12187      /**
12188      * Create an empty record
12189      * @param {Object} data (optional) - overlay some values
12190      * @return {Roo.data.Record} record created.
12191      */
12192     newRow :  function(d) {
12193         var da =  {};
12194         this.recordType.prototype.fields.each(function(c) {
12195             switch( c.type) {
12196                 case 'int' : da[c.name] = 0; break;
12197                 case 'date' : da[c.name] = new Date(); break;
12198                 case 'float' : da[c.name] = 0.0; break;
12199                 case 'boolean' : da[c.name] = false; break;
12200                 default : da[c.name] = ""; break;
12201             }
12202             
12203         });
12204         return new this.recordType(Roo.apply(da, d));
12205     }
12206     
12207 };/*
12208  * Based on:
12209  * Ext JS Library 1.1.1
12210  * Copyright(c) 2006-2007, Ext JS, LLC.
12211  *
12212  * Originally Released Under LGPL - original licence link has changed is not relivant.
12213  *
12214  * Fork - LGPL
12215  * <script type="text/javascript">
12216  */
12217
12218 /**
12219  * @class Roo.data.DataProxy
12220  * @extends Roo.data.Observable
12221  * This class is an abstract base class for implementations which provide retrieval of
12222  * unformatted data objects.<br>
12223  * <p>
12224  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12225  * (of the appropriate type which knows how to parse the data object) to provide a block of
12226  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12227  * <p>
12228  * Custom implementations must implement the load method as described in
12229  * {@link Roo.data.HttpProxy#load}.
12230  */
12231 Roo.data.DataProxy = function(){
12232     this.addEvents({
12233         /**
12234          * @event beforeload
12235          * Fires before a network request is made to retrieve a data object.
12236          * @param {Object} This DataProxy object.
12237          * @param {Object} params The params parameter to the load function.
12238          */
12239         beforeload : true,
12240         /**
12241          * @event load
12242          * Fires before the load method's callback is called.
12243          * @param {Object} This DataProxy object.
12244          * @param {Object} o The data object.
12245          * @param {Object} arg The callback argument object passed to the load function.
12246          */
12247         load : true,
12248         /**
12249          * @event loadexception
12250          * Fires if an Exception occurs during data retrieval.
12251          * @param {Object} This DataProxy object.
12252          * @param {Object} o The data object.
12253          * @param {Object} arg The callback argument object passed to the load function.
12254          * @param {Object} e The Exception.
12255          */
12256         loadexception : true
12257     });
12258     Roo.data.DataProxy.superclass.constructor.call(this);
12259 };
12260
12261 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12262
12263     /**
12264      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12265      */
12266 /*
12267  * Based on:
12268  * Ext JS Library 1.1.1
12269  * Copyright(c) 2006-2007, Ext JS, LLC.
12270  *
12271  * Originally Released Under LGPL - original licence link has changed is not relivant.
12272  *
12273  * Fork - LGPL
12274  * <script type="text/javascript">
12275  */
12276 /**
12277  * @class Roo.data.MemoryProxy
12278  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12279  * to the Reader when its load method is called.
12280  * @constructor
12281  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12282  */
12283 Roo.data.MemoryProxy = function(data){
12284     if (data.data) {
12285         data = data.data;
12286     }
12287     Roo.data.MemoryProxy.superclass.constructor.call(this);
12288     this.data = data;
12289 };
12290
12291 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12292     
12293     /**
12294      * Load data from the requested source (in this case an in-memory
12295      * data object passed to the constructor), read the data object into
12296      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12297      * process that block using the passed callback.
12298      * @param {Object} params This parameter is not used by the MemoryProxy class.
12299      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12300      * object into a block of Roo.data.Records.
12301      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12302      * The function must be passed <ul>
12303      * <li>The Record block object</li>
12304      * <li>The "arg" argument from the load function</li>
12305      * <li>A boolean success indicator</li>
12306      * </ul>
12307      * @param {Object} scope The scope in which to call the callback
12308      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12309      */
12310     load : function(params, reader, callback, scope, arg){
12311         params = params || {};
12312         var result;
12313         try {
12314             result = reader.readRecords(this.data);
12315         }catch(e){
12316             this.fireEvent("loadexception", this, arg, null, e);
12317             callback.call(scope, null, arg, false);
12318             return;
12319         }
12320         callback.call(scope, result, arg, true);
12321     },
12322     
12323     // private
12324     update : function(params, records){
12325         
12326     }
12327 });/*
12328  * Based on:
12329  * Ext JS Library 1.1.1
12330  * Copyright(c) 2006-2007, Ext JS, LLC.
12331  *
12332  * Originally Released Under LGPL - original licence link has changed is not relivant.
12333  *
12334  * Fork - LGPL
12335  * <script type="text/javascript">
12336  */
12337 /**
12338  * @class Roo.data.HttpProxy
12339  * @extends Roo.data.DataProxy
12340  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12341  * configured to reference a certain URL.<br><br>
12342  * <p>
12343  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12344  * from which the running page was served.<br><br>
12345  * <p>
12346  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12347  * <p>
12348  * Be aware that to enable the browser to parse an XML document, the server must set
12349  * the Content-Type header in the HTTP response to "text/xml".
12350  * @constructor
12351  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12352  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12353  * will be used to make the request.
12354  */
12355 Roo.data.HttpProxy = function(conn){
12356     Roo.data.HttpProxy.superclass.constructor.call(this);
12357     // is conn a conn config or a real conn?
12358     this.conn = conn;
12359     this.useAjax = !conn || !conn.events;
12360   
12361 };
12362
12363 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12364     // thse are take from connection...
12365     
12366     /**
12367      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12368      */
12369     /**
12370      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12371      * extra parameters to each request made by this object. (defaults to undefined)
12372      */
12373     /**
12374      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12375      *  to each request made by this object. (defaults to undefined)
12376      */
12377     /**
12378      * @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)
12379      */
12380     /**
12381      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12382      */
12383      /**
12384      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12385      * @type Boolean
12386      */
12387   
12388
12389     /**
12390      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12391      * @type Boolean
12392      */
12393     /**
12394      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12395      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12396      * a finer-grained basis than the DataProxy events.
12397      */
12398     getConnection : function(){
12399         return this.useAjax ? Roo.Ajax : this.conn;
12400     },
12401
12402     /**
12403      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12404      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12405      * process that block using the passed callback.
12406      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12407      * for the request to the remote server.
12408      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12409      * object into a block of Roo.data.Records.
12410      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12411      * The function must be passed <ul>
12412      * <li>The Record block object</li>
12413      * <li>The "arg" argument from the load function</li>
12414      * <li>A boolean success indicator</li>
12415      * </ul>
12416      * @param {Object} scope The scope in which to call the callback
12417      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12418      */
12419     load : function(params, reader, callback, scope, arg){
12420         if(this.fireEvent("beforeload", this, params) !== false){
12421             var  o = {
12422                 params : params || {},
12423                 request: {
12424                     callback : callback,
12425                     scope : scope,
12426                     arg : arg
12427                 },
12428                 reader: reader,
12429                 callback : this.loadResponse,
12430                 scope: this
12431             };
12432             if(this.useAjax){
12433                 Roo.applyIf(o, this.conn);
12434                 if(this.activeRequest){
12435                     Roo.Ajax.abort(this.activeRequest);
12436                 }
12437                 this.activeRequest = Roo.Ajax.request(o);
12438             }else{
12439                 this.conn.request(o);
12440             }
12441         }else{
12442             callback.call(scope||this, null, arg, false);
12443         }
12444     },
12445
12446     // private
12447     loadResponse : function(o, success, response){
12448         delete this.activeRequest;
12449         if(!success){
12450             this.fireEvent("loadexception", this, o, response);
12451             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12452             return;
12453         }
12454         var result;
12455         try {
12456             result = o.reader.read(response);
12457         }catch(e){
12458             this.fireEvent("loadexception", this, o, response, e);
12459             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12460             return;
12461         }
12462         
12463         this.fireEvent("load", this, o, o.request.arg);
12464         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12465     },
12466
12467     // private
12468     update : function(dataSet){
12469
12470     },
12471
12472     // private
12473     updateResponse : function(dataSet){
12474
12475     }
12476 });/*
12477  * Based on:
12478  * Ext JS Library 1.1.1
12479  * Copyright(c) 2006-2007, Ext JS, LLC.
12480  *
12481  * Originally Released Under LGPL - original licence link has changed is not relivant.
12482  *
12483  * Fork - LGPL
12484  * <script type="text/javascript">
12485  */
12486
12487 /**
12488  * @class Roo.data.ScriptTagProxy
12489  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12490  * other than the originating domain of the running page.<br><br>
12491  * <p>
12492  * <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
12493  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12494  * <p>
12495  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12496  * source code that is used as the source inside a &lt;script> tag.<br><br>
12497  * <p>
12498  * In order for the browser to process the returned data, the server must wrap the data object
12499  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12500  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12501  * depending on whether the callback name was passed:
12502  * <p>
12503  * <pre><code>
12504 boolean scriptTag = false;
12505 String cb = request.getParameter("callback");
12506 if (cb != null) {
12507     scriptTag = true;
12508     response.setContentType("text/javascript");
12509 } else {
12510     response.setContentType("application/x-json");
12511 }
12512 Writer out = response.getWriter();
12513 if (scriptTag) {
12514     out.write(cb + "(");
12515 }
12516 out.print(dataBlock.toJsonString());
12517 if (scriptTag) {
12518     out.write(");");
12519 }
12520 </pre></code>
12521  *
12522  * @constructor
12523  * @param {Object} config A configuration object.
12524  */
12525 Roo.data.ScriptTagProxy = function(config){
12526     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12527     Roo.apply(this, config);
12528     this.head = document.getElementsByTagName("head")[0];
12529 };
12530
12531 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12532
12533 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12534     /**
12535      * @cfg {String} url The URL from which to request the data object.
12536      */
12537     /**
12538      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12539      */
12540     timeout : 30000,
12541     /**
12542      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12543      * the server the name of the callback function set up by the load call to process the returned data object.
12544      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12545      * javascript output which calls this named function passing the data object as its only parameter.
12546      */
12547     callbackParam : "callback",
12548     /**
12549      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12550      * name to the request.
12551      */
12552     nocache : true,
12553
12554     /**
12555      * Load data from the configured URL, read the data object into
12556      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12557      * process that block using the passed callback.
12558      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12559      * for the request to the remote server.
12560      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12561      * object into a block of Roo.data.Records.
12562      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12563      * The function must be passed <ul>
12564      * <li>The Record block object</li>
12565      * <li>The "arg" argument from the load function</li>
12566      * <li>A boolean success indicator</li>
12567      * </ul>
12568      * @param {Object} scope The scope in which to call the callback
12569      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12570      */
12571     load : function(params, reader, callback, scope, arg){
12572         if(this.fireEvent("beforeload", this, params) !== false){
12573
12574             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12575
12576             var url = this.url;
12577             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12578             if(this.nocache){
12579                 url += "&_dc=" + (new Date().getTime());
12580             }
12581             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12582             var trans = {
12583                 id : transId,
12584                 cb : "stcCallback"+transId,
12585                 scriptId : "stcScript"+transId,
12586                 params : params,
12587                 arg : arg,
12588                 url : url,
12589                 callback : callback,
12590                 scope : scope,
12591                 reader : reader
12592             };
12593             var conn = this;
12594
12595             window[trans.cb] = function(o){
12596                 conn.handleResponse(o, trans);
12597             };
12598
12599             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12600
12601             if(this.autoAbort !== false){
12602                 this.abort();
12603             }
12604
12605             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12606
12607             var script = document.createElement("script");
12608             script.setAttribute("src", url);
12609             script.setAttribute("type", "text/javascript");
12610             script.setAttribute("id", trans.scriptId);
12611             this.head.appendChild(script);
12612
12613             this.trans = trans;
12614         }else{
12615             callback.call(scope||this, null, arg, false);
12616         }
12617     },
12618
12619     // private
12620     isLoading : function(){
12621         return this.trans ? true : false;
12622     },
12623
12624     /**
12625      * Abort the current server request.
12626      */
12627     abort : function(){
12628         if(this.isLoading()){
12629             this.destroyTrans(this.trans);
12630         }
12631     },
12632
12633     // private
12634     destroyTrans : function(trans, isLoaded){
12635         this.head.removeChild(document.getElementById(trans.scriptId));
12636         clearTimeout(trans.timeoutId);
12637         if(isLoaded){
12638             window[trans.cb] = undefined;
12639             try{
12640                 delete window[trans.cb];
12641             }catch(e){}
12642         }else{
12643             // if hasn't been loaded, wait for load to remove it to prevent script error
12644             window[trans.cb] = function(){
12645                 window[trans.cb] = undefined;
12646                 try{
12647                     delete window[trans.cb];
12648                 }catch(e){}
12649             };
12650         }
12651     },
12652
12653     // private
12654     handleResponse : function(o, trans){
12655         this.trans = false;
12656         this.destroyTrans(trans, true);
12657         var result;
12658         try {
12659             result = trans.reader.readRecords(o);
12660         }catch(e){
12661             this.fireEvent("loadexception", this, o, trans.arg, e);
12662             trans.callback.call(trans.scope||window, null, trans.arg, false);
12663             return;
12664         }
12665         this.fireEvent("load", this, o, trans.arg);
12666         trans.callback.call(trans.scope||window, result, trans.arg, true);
12667     },
12668
12669     // private
12670     handleFailure : function(trans){
12671         this.trans = false;
12672         this.destroyTrans(trans, false);
12673         this.fireEvent("loadexception", this, null, trans.arg);
12674         trans.callback.call(trans.scope||window, null, trans.arg, false);
12675     }
12676 });/*
12677  * Based on:
12678  * Ext JS Library 1.1.1
12679  * Copyright(c) 2006-2007, Ext JS, LLC.
12680  *
12681  * Originally Released Under LGPL - original licence link has changed is not relivant.
12682  *
12683  * Fork - LGPL
12684  * <script type="text/javascript">
12685  */
12686
12687 /**
12688  * @class Roo.data.JsonReader
12689  * @extends Roo.data.DataReader
12690  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12691  * based on mappings in a provided Roo.data.Record constructor.
12692  * 
12693  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12694  * in the reply previously. 
12695  * 
12696  * <p>
12697  * Example code:
12698  * <pre><code>
12699 var RecordDef = Roo.data.Record.create([
12700     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12701     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12702 ]);
12703 var myReader = new Roo.data.JsonReader({
12704     totalProperty: "results",    // The property which contains the total dataset size (optional)
12705     root: "rows",                // The property which contains an Array of row objects
12706     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12707 }, RecordDef);
12708 </code></pre>
12709  * <p>
12710  * This would consume a JSON file like this:
12711  * <pre><code>
12712 { 'results': 2, 'rows': [
12713     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12714     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12715 }
12716 </code></pre>
12717  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12718  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12719  * paged from the remote server.
12720  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12721  * @cfg {String} root name of the property which contains the Array of row objects.
12722  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12723  * @cfg {Array} fields Array of field definition objects
12724  * @constructor
12725  * Create a new JsonReader
12726  * @param {Object} meta Metadata configuration options
12727  * @param {Object} recordType Either an Array of field definition objects,
12728  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12729  */
12730 Roo.data.JsonReader = function(meta, recordType){
12731     
12732     meta = meta || {};
12733     // set some defaults:
12734     Roo.applyIf(meta, {
12735         totalProperty: 'total',
12736         successProperty : 'success',
12737         root : 'data',
12738         id : 'id'
12739     });
12740     
12741     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12742 };
12743 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12744     
12745     /**
12746      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12747      * Used by Store query builder to append _requestMeta to params.
12748      * 
12749      */
12750     metaFromRemote : false,
12751     /**
12752      * This method is only used by a DataProxy which has retrieved data from a remote server.
12753      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12754      * @return {Object} data A data block which is used by an Roo.data.Store object as
12755      * a cache of Roo.data.Records.
12756      */
12757     read : function(response){
12758         var json = response.responseText;
12759        
12760         var o = /* eval:var:o */ eval("("+json+")");
12761         if(!o) {
12762             throw {message: "JsonReader.read: Json object not found"};
12763         }
12764         
12765         if(o.metaData){
12766             
12767             delete this.ef;
12768             this.metaFromRemote = true;
12769             this.meta = o.metaData;
12770             this.recordType = Roo.data.Record.create(o.metaData.fields);
12771             this.onMetaChange(this.meta, this.recordType, o);
12772         }
12773         return this.readRecords(o);
12774     },
12775
12776     // private function a store will implement
12777     onMetaChange : function(meta, recordType, o){
12778
12779     },
12780
12781     /**
12782          * @ignore
12783          */
12784     simpleAccess: function(obj, subsc) {
12785         return obj[subsc];
12786     },
12787
12788         /**
12789          * @ignore
12790          */
12791     getJsonAccessor: function(){
12792         var re = /[\[\.]/;
12793         return function(expr) {
12794             try {
12795                 return(re.test(expr))
12796                     ? new Function("obj", "return obj." + expr)
12797                     : function(obj){
12798                         return obj[expr];
12799                     };
12800             } catch(e){}
12801             return Roo.emptyFn;
12802         };
12803     }(),
12804
12805     /**
12806      * Create a data block containing Roo.data.Records from an XML document.
12807      * @param {Object} o An object which contains an Array of row objects in the property specified
12808      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12809      * which contains the total size of the dataset.
12810      * @return {Object} data A data block which is used by an Roo.data.Store object as
12811      * a cache of Roo.data.Records.
12812      */
12813     readRecords : function(o){
12814         /**
12815          * After any data loads, the raw JSON data is available for further custom processing.
12816          * @type Object
12817          */
12818         this.o = o;
12819         var s = this.meta, Record = this.recordType,
12820             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12821
12822 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12823         if (!this.ef) {
12824             if(s.totalProperty) {
12825                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12826                 }
12827                 if(s.successProperty) {
12828                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12829                 }
12830                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12831                 if (s.id) {
12832                         var g = this.getJsonAccessor(s.id);
12833                         this.getId = function(rec) {
12834                                 var r = g(rec);  
12835                                 return (r === undefined || r === "") ? null : r;
12836                         };
12837                 } else {
12838                         this.getId = function(){return null;};
12839                 }
12840             this.ef = [];
12841             for(var jj = 0; jj < fl; jj++){
12842                 f = fi[jj];
12843                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12844                 this.ef[jj] = this.getJsonAccessor(map);
12845             }
12846         }
12847
12848         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12849         if(s.totalProperty){
12850             var vt = parseInt(this.getTotal(o), 10);
12851             if(!isNaN(vt)){
12852                 totalRecords = vt;
12853             }
12854         }
12855         if(s.successProperty){
12856             var vs = this.getSuccess(o);
12857             if(vs === false || vs === 'false'){
12858                 success = false;
12859             }
12860         }
12861         var records = [];
12862         for(var i = 0; i < c; i++){
12863                 var n = root[i];
12864             var values = {};
12865             var id = this.getId(n);
12866             for(var j = 0; j < fl; j++){
12867                 f = fi[j];
12868             var v = this.ef[j](n);
12869             if (!f.convert) {
12870                 Roo.log('missing convert for ' + f.name);
12871                 Roo.log(f);
12872                 continue;
12873             }
12874             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12875             }
12876             var record = new Record(values, id);
12877             record.json = n;
12878             records[i] = record;
12879         }
12880         return {
12881             raw : o,
12882             success : success,
12883             records : records,
12884             totalRecords : totalRecords
12885         };
12886     }
12887 });/*
12888  * Based on:
12889  * Ext JS Library 1.1.1
12890  * Copyright(c) 2006-2007, Ext JS, LLC.
12891  *
12892  * Originally Released Under LGPL - original licence link has changed is not relivant.
12893  *
12894  * Fork - LGPL
12895  * <script type="text/javascript">
12896  */
12897
12898 /**
12899  * @class Roo.data.ArrayReader
12900  * @extends Roo.data.DataReader
12901  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12902  * Each element of that Array represents a row of data fields. The
12903  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12904  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12905  * <p>
12906  * Example code:.
12907  * <pre><code>
12908 var RecordDef = Roo.data.Record.create([
12909     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12910     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12911 ]);
12912 var myReader = new Roo.data.ArrayReader({
12913     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12914 }, RecordDef);
12915 </code></pre>
12916  * <p>
12917  * This would consume an Array like this:
12918  * <pre><code>
12919 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12920   </code></pre>
12921  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12922  * @constructor
12923  * Create a new JsonReader
12924  * @param {Object} meta Metadata configuration options.
12925  * @param {Object} recordType Either an Array of field definition objects
12926  * as specified to {@link Roo.data.Record#create},
12927  * or an {@link Roo.data.Record} object
12928  * created using {@link Roo.data.Record#create}.
12929  */
12930 Roo.data.ArrayReader = function(meta, recordType){
12931     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12932 };
12933
12934 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12935     /**
12936      * Create a data block containing Roo.data.Records from an XML document.
12937      * @param {Object} o An Array of row objects which represents the dataset.
12938      * @return {Object} data A data block which is used by an Roo.data.Store object as
12939      * a cache of Roo.data.Records.
12940      */
12941     readRecords : function(o){
12942         var sid = this.meta ? this.meta.id : null;
12943         var recordType = this.recordType, fields = recordType.prototype.fields;
12944         var records = [];
12945         var root = o;
12946             for(var i = 0; i < root.length; i++){
12947                     var n = root[i];
12948                 var values = {};
12949                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12950                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12951                 var f = fields.items[j];
12952                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12953                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12954                 v = f.convert(v);
12955                 values[f.name] = v;
12956             }
12957                 var record = new recordType(values, id);
12958                 record.json = n;
12959                 records[records.length] = record;
12960             }
12961             return {
12962                 records : records,
12963                 totalRecords : records.length
12964             };
12965     }
12966 });/*
12967  * - LGPL
12968  * * 
12969  */
12970
12971 /**
12972  * @class Roo.bootstrap.ComboBox
12973  * @extends Roo.bootstrap.TriggerField
12974  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12975  * @cfg {Boolean} append (true|false) default false
12976  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12977  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12978  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12979  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12980  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12981  * @cfg {Boolean} animate default true
12982  * @cfg {Boolean} emptyResultText only for touch device
12983  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12984  * @cfg {String} emptyTitle default ''
12985  * @constructor
12986  * Create a new ComboBox.
12987  * @param {Object} config Configuration options
12988  */
12989 Roo.bootstrap.ComboBox = function(config){
12990     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12991     this.addEvents({
12992         /**
12993          * @event expand
12994          * Fires when the dropdown list is expanded
12995         * @param {Roo.bootstrap.ComboBox} combo This combo box
12996         */
12997         'expand' : true,
12998         /**
12999          * @event collapse
13000          * Fires when the dropdown list is collapsed
13001         * @param {Roo.bootstrap.ComboBox} combo This combo box
13002         */
13003         'collapse' : true,
13004         /**
13005          * @event beforeselect
13006          * Fires before a list item is selected. Return false to cancel the selection.
13007         * @param {Roo.bootstrap.ComboBox} combo This combo box
13008         * @param {Roo.data.Record} record The data record returned from the underlying store
13009         * @param {Number} index The index of the selected item in the dropdown list
13010         */
13011         'beforeselect' : true,
13012         /**
13013          * @event select
13014          * Fires when a list item is selected
13015         * @param {Roo.bootstrap.ComboBox} combo This combo box
13016         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13017         * @param {Number} index The index of the selected item in the dropdown list
13018         */
13019         'select' : true,
13020         /**
13021          * @event beforequery
13022          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13023          * The event object passed has these properties:
13024         * @param {Roo.bootstrap.ComboBox} combo This combo box
13025         * @param {String} query The query
13026         * @param {Boolean} forceAll true to force "all" query
13027         * @param {Boolean} cancel true to cancel the query
13028         * @param {Object} e The query event object
13029         */
13030         'beforequery': true,
13031          /**
13032          * @event add
13033          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13034         * @param {Roo.bootstrap.ComboBox} combo This combo box
13035         */
13036         'add' : true,
13037         /**
13038          * @event edit
13039          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13040         * @param {Roo.bootstrap.ComboBox} combo This combo box
13041         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13042         */
13043         'edit' : true,
13044         /**
13045          * @event remove
13046          * Fires when the remove value from the combobox array
13047         * @param {Roo.bootstrap.ComboBox} combo This combo box
13048         */
13049         'remove' : true,
13050         /**
13051          * @event afterremove
13052          * Fires when the remove value from the combobox array
13053         * @param {Roo.bootstrap.ComboBox} combo This combo box
13054         */
13055         'afterremove' : true,
13056         /**
13057          * @event specialfilter
13058          * Fires when specialfilter
13059             * @param {Roo.bootstrap.ComboBox} combo This combo box
13060             */
13061         'specialfilter' : true,
13062         /**
13063          * @event tick
13064          * Fires when tick the element
13065             * @param {Roo.bootstrap.ComboBox} combo This combo box
13066             */
13067         'tick' : true,
13068         /**
13069          * @event touchviewdisplay
13070          * Fires when touch view require special display (default is using displayField)
13071             * @param {Roo.bootstrap.ComboBox} combo This combo box
13072             * @param {Object} cfg set html .
13073             */
13074         'touchviewdisplay' : true
13075         
13076     });
13077     
13078     this.item = [];
13079     this.tickItems = [];
13080     
13081     this.selectedIndex = -1;
13082     if(this.mode == 'local'){
13083         if(config.queryDelay === undefined){
13084             this.queryDelay = 10;
13085         }
13086         if(config.minChars === undefined){
13087             this.minChars = 0;
13088         }
13089     }
13090 };
13091
13092 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13093      
13094     /**
13095      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13096      * rendering into an Roo.Editor, defaults to false)
13097      */
13098     /**
13099      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13100      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13101      */
13102     /**
13103      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13104      */
13105     /**
13106      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13107      * the dropdown list (defaults to undefined, with no header element)
13108      */
13109
13110      /**
13111      * @cfg {String/Roo.Template} tpl The template to use to render the output
13112      */
13113      
13114      /**
13115      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13116      */
13117     listWidth: undefined,
13118     /**
13119      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13120      * mode = 'remote' or 'text' if mode = 'local')
13121      */
13122     displayField: undefined,
13123     
13124     /**
13125      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13126      * mode = 'remote' or 'value' if mode = 'local'). 
13127      * Note: use of a valueField requires the user make a selection
13128      * in order for a value to be mapped.
13129      */
13130     valueField: undefined,
13131     /**
13132      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13133      */
13134     modalTitle : '',
13135     
13136     /**
13137      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13138      * field's data value (defaults to the underlying DOM element's name)
13139      */
13140     hiddenName: undefined,
13141     /**
13142      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13143      */
13144     listClass: '',
13145     /**
13146      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13147      */
13148     selectedClass: 'active',
13149     
13150     /**
13151      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13152      */
13153     shadow:'sides',
13154     /**
13155      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13156      * anchor positions (defaults to 'tl-bl')
13157      */
13158     listAlign: 'tl-bl?',
13159     /**
13160      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13161      */
13162     maxHeight: 300,
13163     /**
13164      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13165      * query specified by the allQuery config option (defaults to 'query')
13166      */
13167     triggerAction: 'query',
13168     /**
13169      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13170      * (defaults to 4, does not apply if editable = false)
13171      */
13172     minChars : 4,
13173     /**
13174      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13175      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13176      */
13177     typeAhead: false,
13178     /**
13179      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13180      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13181      */
13182     queryDelay: 500,
13183     /**
13184      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13185      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13186      */
13187     pageSize: 0,
13188     /**
13189      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13190      * when editable = true (defaults to false)
13191      */
13192     selectOnFocus:false,
13193     /**
13194      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13195      */
13196     queryParam: 'query',
13197     /**
13198      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13199      * when mode = 'remote' (defaults to 'Loading...')
13200      */
13201     loadingText: 'Loading...',
13202     /**
13203      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13204      */
13205     resizable: false,
13206     /**
13207      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13208      */
13209     handleHeight : 8,
13210     /**
13211      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13212      * traditional select (defaults to true)
13213      */
13214     editable: true,
13215     /**
13216      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13217      */
13218     allQuery: '',
13219     /**
13220      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13221      */
13222     mode: 'remote',
13223     /**
13224      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13225      * listWidth has a higher value)
13226      */
13227     minListWidth : 70,
13228     /**
13229      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13230      * allow the user to set arbitrary text into the field (defaults to false)
13231      */
13232     forceSelection:false,
13233     /**
13234      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13235      * if typeAhead = true (defaults to 250)
13236      */
13237     typeAheadDelay : 250,
13238     /**
13239      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13240      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13241      */
13242     valueNotFoundText : undefined,
13243     /**
13244      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13245      */
13246     blockFocus : false,
13247     
13248     /**
13249      * @cfg {Boolean} disableClear Disable showing of clear button.
13250      */
13251     disableClear : false,
13252     /**
13253      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13254      */
13255     alwaysQuery : false,
13256     
13257     /**
13258      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13259      */
13260     multiple : false,
13261     
13262     /**
13263      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13264      */
13265     invalidClass : "has-warning",
13266     
13267     /**
13268      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13269      */
13270     validClass : "has-success",
13271     
13272     /**
13273      * @cfg {Boolean} specialFilter (true|false) special filter default false
13274      */
13275     specialFilter : false,
13276     
13277     /**
13278      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13279      */
13280     mobileTouchView : true,
13281     
13282     /**
13283      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13284      */
13285     useNativeIOS : false,
13286     
13287     /**
13288      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13289      */
13290     mobile_restrict_height : false,
13291     
13292     ios_options : false,
13293     
13294     //private
13295     addicon : false,
13296     editicon: false,
13297     
13298     page: 0,
13299     hasQuery: false,
13300     append: false,
13301     loadNext: false,
13302     autoFocus : true,
13303     tickable : false,
13304     btnPosition : 'right',
13305     triggerList : true,
13306     showToggleBtn : true,
13307     animate : true,
13308     emptyResultText: 'Empty',
13309     triggerText : 'Select',
13310     emptyTitle : '',
13311     
13312     // element that contains real text value.. (when hidden is used..)
13313     
13314     getAutoCreate : function()
13315     {   
13316         var cfg = false;
13317         //render
13318         /*
13319          * Render classic select for iso
13320          */
13321         
13322         if(Roo.isIOS && this.useNativeIOS){
13323             cfg = this.getAutoCreateNativeIOS();
13324             return cfg;
13325         }
13326         
13327         /*
13328          * Touch Devices
13329          */
13330         
13331         if(Roo.isTouch && this.mobileTouchView){
13332             cfg = this.getAutoCreateTouchView();
13333             return cfg;;
13334         }
13335         
13336         /*
13337          *  Normal ComboBox
13338          */
13339         if(!this.tickable){
13340             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13341             return cfg;
13342         }
13343         
13344         /*
13345          *  ComboBox with tickable selections
13346          */
13347              
13348         var align = this.labelAlign || this.parentLabelAlign();
13349         
13350         cfg = {
13351             cls : 'form-group roo-combobox-tickable' //input-group
13352         };
13353         
13354         var btn_text_select = '';
13355         var btn_text_done = '';
13356         var btn_text_cancel = '';
13357         
13358         if (this.btn_text_show) {
13359             btn_text_select = 'Select';
13360             btn_text_done = 'Done';
13361             btn_text_cancel = 'Cancel'; 
13362         }
13363         
13364         var buttons = {
13365             tag : 'div',
13366             cls : 'tickable-buttons',
13367             cn : [
13368                 {
13369                     tag : 'button',
13370                     type : 'button',
13371                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13372                     //html : this.triggerText
13373                     html: btn_text_select
13374                 },
13375                 {
13376                     tag : 'button',
13377                     type : 'button',
13378                     name : 'ok',
13379                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13380                     //html : 'Done'
13381                     html: btn_text_done
13382                 },
13383                 {
13384                     tag : 'button',
13385                     type : 'button',
13386                     name : 'cancel',
13387                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13388                     //html : 'Cancel'
13389                     html: btn_text_cancel
13390                 }
13391             ]
13392         };
13393         
13394         if(this.editable){
13395             buttons.cn.unshift({
13396                 tag: 'input',
13397                 cls: 'roo-select2-search-field-input'
13398             });
13399         }
13400         
13401         var _this = this;
13402         
13403         Roo.each(buttons.cn, function(c){
13404             if (_this.size) {
13405                 c.cls += ' btn-' + _this.size;
13406             }
13407
13408             if (_this.disabled) {
13409                 c.disabled = true;
13410             }
13411         });
13412         
13413         var box = {
13414             tag: 'div',
13415             style : 'display: contents',
13416             cn: [
13417                 {
13418                     tag: 'input',
13419                     type : 'hidden',
13420                     cls: 'form-hidden-field'
13421                 },
13422                 {
13423                     tag: 'ul',
13424                     cls: 'roo-select2-choices',
13425                     cn:[
13426                         {
13427                             tag: 'li',
13428                             cls: 'roo-select2-search-field',
13429                             cn: [
13430                                 buttons
13431                             ]
13432                         }
13433                     ]
13434                 }
13435             ]
13436         };
13437         
13438         var combobox = {
13439             cls: 'roo-select2-container input-group roo-select2-container-multi',
13440             cn: [
13441                 
13442                 box
13443 //                {
13444 //                    tag: 'ul',
13445 //                    cls: 'typeahead typeahead-long dropdown-menu',
13446 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13447 //                }
13448             ]
13449         };
13450         
13451         if(this.hasFeedback && !this.allowBlank){
13452             
13453             var feedback = {
13454                 tag: 'span',
13455                 cls: 'glyphicon form-control-feedback'
13456             };
13457
13458             combobox.cn.push(feedback);
13459         }
13460         
13461         var indicator = {
13462             tag : 'i',
13463             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13464             tooltip : 'This field is required'
13465         };
13466         if (Roo.bootstrap.version == 4) {
13467             indicator = {
13468                 tag : 'i',
13469                 style : 'display:none'
13470             };
13471         }
13472         if (align ==='left' && this.fieldLabel.length) {
13473             
13474             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13475             
13476             cfg.cn = [
13477                 indicator,
13478                 {
13479                     tag: 'label',
13480                     'for' :  id,
13481                     cls : 'control-label col-form-label',
13482                     html : this.fieldLabel
13483
13484                 },
13485                 {
13486                     cls : "", 
13487                     cn: [
13488                         combobox
13489                     ]
13490                 }
13491
13492             ];
13493             
13494             var labelCfg = cfg.cn[1];
13495             var contentCfg = cfg.cn[2];
13496             
13497
13498             if(this.indicatorpos == 'right'){
13499                 
13500                 cfg.cn = [
13501                     {
13502                         tag: 'label',
13503                         'for' :  id,
13504                         cls : 'control-label col-form-label',
13505                         cn : [
13506                             {
13507                                 tag : 'span',
13508                                 html : this.fieldLabel
13509                             },
13510                             indicator
13511                         ]
13512                     },
13513                     {
13514                         cls : "",
13515                         cn: [
13516                             combobox
13517                         ]
13518                     }
13519
13520                 ];
13521                 
13522                 
13523                 
13524                 labelCfg = cfg.cn[0];
13525                 contentCfg = cfg.cn[1];
13526             
13527             }
13528             
13529             if(this.labelWidth > 12){
13530                 labelCfg.style = "width: " + this.labelWidth + 'px';
13531             }
13532             
13533             if(this.labelWidth < 13 && this.labelmd == 0){
13534                 this.labelmd = this.labelWidth;
13535             }
13536             
13537             if(this.labellg > 0){
13538                 labelCfg.cls += ' col-lg-' + this.labellg;
13539                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13540             }
13541             
13542             if(this.labelmd > 0){
13543                 labelCfg.cls += ' col-md-' + this.labelmd;
13544                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13545             }
13546             
13547             if(this.labelsm > 0){
13548                 labelCfg.cls += ' col-sm-' + this.labelsm;
13549                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13550             }
13551             
13552             if(this.labelxs > 0){
13553                 labelCfg.cls += ' col-xs-' + this.labelxs;
13554                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13555             }
13556                 
13557                 
13558         } else if ( this.fieldLabel.length) {
13559 //                Roo.log(" label");
13560                  cfg.cn = [
13561                    indicator,
13562                     {
13563                         tag: 'label',
13564                         //cls : 'input-group-addon',
13565                         html : this.fieldLabel
13566                     },
13567                     combobox
13568                 ];
13569                 
13570                 if(this.indicatorpos == 'right'){
13571                     cfg.cn = [
13572                         {
13573                             tag: 'label',
13574                             //cls : 'input-group-addon',
13575                             html : this.fieldLabel
13576                         },
13577                         indicator,
13578                         combobox
13579                     ];
13580                     
13581                 }
13582
13583         } else {
13584             
13585 //                Roo.log(" no label && no align");
13586                 cfg = combobox
13587                      
13588                 
13589         }
13590          
13591         var settings=this;
13592         ['xs','sm','md','lg'].map(function(size){
13593             if (settings[size]) {
13594                 cfg.cls += ' col-' + size + '-' + settings[size];
13595             }
13596         });
13597         
13598         return cfg;
13599         
13600     },
13601     
13602     _initEventsCalled : false,
13603     
13604     // private
13605     initEvents: function()
13606     {   
13607         if (this._initEventsCalled) { // as we call render... prevent looping...
13608             return;
13609         }
13610         this._initEventsCalled = true;
13611         
13612         if (!this.store) {
13613             throw "can not find store for combo";
13614         }
13615         
13616         this.indicator = this.indicatorEl();
13617         
13618         this.store = Roo.factory(this.store, Roo.data);
13619         this.store.parent = this;
13620         
13621         // if we are building from html. then this element is so complex, that we can not really
13622         // use the rendered HTML.
13623         // so we have to trash and replace the previous code.
13624         if (Roo.XComponent.build_from_html) {
13625             // remove this element....
13626             var e = this.el.dom, k=0;
13627             while (e ) { e = e.previousSibling;  ++k;}
13628
13629             this.el.remove();
13630             
13631             this.el=false;
13632             this.rendered = false;
13633             
13634             this.render(this.parent().getChildContainer(true), k);
13635         }
13636         
13637         if(Roo.isIOS && this.useNativeIOS){
13638             this.initIOSView();
13639             return;
13640         }
13641         
13642         /*
13643          * Touch Devices
13644          */
13645         
13646         if(Roo.isTouch && this.mobileTouchView){
13647             this.initTouchView();
13648             return;
13649         }
13650         
13651         if(this.tickable){
13652             this.initTickableEvents();
13653             return;
13654         }
13655         
13656         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13657         
13658         if(this.hiddenName){
13659             
13660             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13661             
13662             this.hiddenField.dom.value =
13663                 this.hiddenValue !== undefined ? this.hiddenValue :
13664                 this.value !== undefined ? this.value : '';
13665
13666             // prevent input submission
13667             this.el.dom.removeAttribute('name');
13668             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13669              
13670              
13671         }
13672         //if(Roo.isGecko){
13673         //    this.el.dom.setAttribute('autocomplete', 'off');
13674         //}
13675         
13676         var cls = 'x-combo-list';
13677         
13678         //this.list = new Roo.Layer({
13679         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13680         //});
13681         
13682         var _this = this;
13683         
13684         (function(){
13685             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13686             _this.list.setWidth(lw);
13687         }).defer(100);
13688         
13689         this.list.on('mouseover', this.onViewOver, this);
13690         this.list.on('mousemove', this.onViewMove, this);
13691         this.list.on('scroll', this.onViewScroll, this);
13692         
13693         /*
13694         this.list.swallowEvent('mousewheel');
13695         this.assetHeight = 0;
13696
13697         if(this.title){
13698             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13699             this.assetHeight += this.header.getHeight();
13700         }
13701
13702         this.innerList = this.list.createChild({cls:cls+'-inner'});
13703         this.innerList.on('mouseover', this.onViewOver, this);
13704         this.innerList.on('mousemove', this.onViewMove, this);
13705         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13706         
13707         if(this.allowBlank && !this.pageSize && !this.disableClear){
13708             this.footer = this.list.createChild({cls:cls+'-ft'});
13709             this.pageTb = new Roo.Toolbar(this.footer);
13710            
13711         }
13712         if(this.pageSize){
13713             this.footer = this.list.createChild({cls:cls+'-ft'});
13714             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13715                     {pageSize: this.pageSize});
13716             
13717         }
13718         
13719         if (this.pageTb && this.allowBlank && !this.disableClear) {
13720             var _this = this;
13721             this.pageTb.add(new Roo.Toolbar.Fill(), {
13722                 cls: 'x-btn-icon x-btn-clear',
13723                 text: '&#160;',
13724                 handler: function()
13725                 {
13726                     _this.collapse();
13727                     _this.clearValue();
13728                     _this.onSelect(false, -1);
13729                 }
13730             });
13731         }
13732         if (this.footer) {
13733             this.assetHeight += this.footer.getHeight();
13734         }
13735         */
13736             
13737         if(!this.tpl){
13738             this.tpl = Roo.bootstrap.version == 4 ?
13739                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13740                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13741         }
13742
13743         this.view = new Roo.View(this.list, this.tpl, {
13744             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13745         });
13746         //this.view.wrapEl.setDisplayed(false);
13747         this.view.on('click', this.onViewClick, this);
13748         
13749         
13750         this.store.on('beforeload', this.onBeforeLoad, this);
13751         this.store.on('load', this.onLoad, this);
13752         this.store.on('loadexception', this.onLoadException, this);
13753         /*
13754         if(this.resizable){
13755             this.resizer = new Roo.Resizable(this.list,  {
13756                pinned:true, handles:'se'
13757             });
13758             this.resizer.on('resize', function(r, w, h){
13759                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13760                 this.listWidth = w;
13761                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13762                 this.restrictHeight();
13763             }, this);
13764             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13765         }
13766         */
13767         if(!this.editable){
13768             this.editable = true;
13769             this.setEditable(false);
13770         }
13771         
13772         /*
13773         
13774         if (typeof(this.events.add.listeners) != 'undefined') {
13775             
13776             this.addicon = this.wrap.createChild(
13777                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13778        
13779             this.addicon.on('click', function(e) {
13780                 this.fireEvent('add', this);
13781             }, this);
13782         }
13783         if (typeof(this.events.edit.listeners) != 'undefined') {
13784             
13785             this.editicon = this.wrap.createChild(
13786                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13787             if (this.addicon) {
13788                 this.editicon.setStyle('margin-left', '40px');
13789             }
13790             this.editicon.on('click', function(e) {
13791                 
13792                 // we fire even  if inothing is selected..
13793                 this.fireEvent('edit', this, this.lastData );
13794                 
13795             }, this);
13796         }
13797         */
13798         
13799         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13800             "up" : function(e){
13801                 this.inKeyMode = true;
13802                 this.selectPrev();
13803             },
13804
13805             "down" : function(e){
13806                 if(!this.isExpanded()){
13807                     this.onTriggerClick();
13808                 }else{
13809                     this.inKeyMode = true;
13810                     this.selectNext();
13811                 }
13812             },
13813
13814             "enter" : function(e){
13815 //                this.onViewClick();
13816                 //return true;
13817                 this.collapse();
13818                 
13819                 if(this.fireEvent("specialkey", this, e)){
13820                     this.onViewClick(false);
13821                 }
13822                 
13823                 return true;
13824             },
13825
13826             "esc" : function(e){
13827                 this.collapse();
13828             },
13829
13830             "tab" : function(e){
13831                 this.collapse();
13832                 
13833                 if(this.fireEvent("specialkey", this, e)){
13834                     this.onViewClick(false);
13835                 }
13836                 
13837                 return true;
13838             },
13839
13840             scope : this,
13841
13842             doRelay : function(foo, bar, hname){
13843                 if(hname == 'down' || this.scope.isExpanded()){
13844                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13845                 }
13846                 return true;
13847             },
13848
13849             forceKeyDown: true
13850         });
13851         
13852         
13853         this.queryDelay = Math.max(this.queryDelay || 10,
13854                 this.mode == 'local' ? 10 : 250);
13855         
13856         
13857         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13858         
13859         if(this.typeAhead){
13860             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13861         }
13862         if(this.editable !== false){
13863             this.inputEl().on("keyup", this.onKeyUp, this);
13864         }
13865         if(this.forceSelection){
13866             this.inputEl().on('blur', this.doForce, this);
13867         }
13868         
13869         if(this.multiple){
13870             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13871             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13872         }
13873     },
13874     
13875     initTickableEvents: function()
13876     {   
13877         this.createList();
13878         
13879         if(this.hiddenName){
13880             
13881             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13882             
13883             this.hiddenField.dom.value =
13884                 this.hiddenValue !== undefined ? this.hiddenValue :
13885                 this.value !== undefined ? this.value : '';
13886
13887             // prevent input submission
13888             this.el.dom.removeAttribute('name');
13889             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13890              
13891              
13892         }
13893         
13894 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13895         
13896         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13897         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13898         if(this.triggerList){
13899             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13900         }
13901          
13902         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13903         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13904         
13905         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13906         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13907         
13908         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13909         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13910         
13911         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13912         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13913         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13914         
13915         this.okBtn.hide();
13916         this.cancelBtn.hide();
13917         
13918         var _this = this;
13919         
13920         (function(){
13921             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13922             _this.list.setWidth(lw);
13923         }).defer(100);
13924         
13925         this.list.on('mouseover', this.onViewOver, this);
13926         this.list.on('mousemove', this.onViewMove, this);
13927         
13928         this.list.on('scroll', this.onViewScroll, this);
13929         
13930         if(!this.tpl){
13931             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13932                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13933         }
13934
13935         this.view = new Roo.View(this.list, this.tpl, {
13936             singleSelect:true,
13937             tickable:true,
13938             parent:this,
13939             store: this.store,
13940             selectedClass: this.selectedClass
13941         });
13942         
13943         //this.view.wrapEl.setDisplayed(false);
13944         this.view.on('click', this.onViewClick, this);
13945         
13946         
13947         
13948         this.store.on('beforeload', this.onBeforeLoad, this);
13949         this.store.on('load', this.onLoad, this);
13950         this.store.on('loadexception', this.onLoadException, this);
13951         
13952         if(this.editable){
13953             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13954                 "up" : function(e){
13955                     this.inKeyMode = true;
13956                     this.selectPrev();
13957                 },
13958
13959                 "down" : function(e){
13960                     this.inKeyMode = true;
13961                     this.selectNext();
13962                 },
13963
13964                 "enter" : function(e){
13965                     if(this.fireEvent("specialkey", this, e)){
13966                         this.onViewClick(false);
13967                     }
13968                     
13969                     return true;
13970                 },
13971
13972                 "esc" : function(e){
13973                     this.onTickableFooterButtonClick(e, false, false);
13974                 },
13975
13976                 "tab" : function(e){
13977                     this.fireEvent("specialkey", this, e);
13978                     
13979                     this.onTickableFooterButtonClick(e, false, false);
13980                     
13981                     return true;
13982                 },
13983
13984                 scope : this,
13985
13986                 doRelay : function(e, fn, key){
13987                     if(this.scope.isExpanded()){
13988                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13989                     }
13990                     return true;
13991                 },
13992
13993                 forceKeyDown: true
13994             });
13995         }
13996         
13997         this.queryDelay = Math.max(this.queryDelay || 10,
13998                 this.mode == 'local' ? 10 : 250);
13999         
14000         
14001         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14002         
14003         if(this.typeAhead){
14004             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14005         }
14006         
14007         if(this.editable !== false){
14008             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14009         }
14010         
14011         this.indicator = this.indicatorEl();
14012         
14013         if(this.indicator){
14014             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14015             this.indicator.hide();
14016         }
14017         
14018     },
14019
14020     onDestroy : function(){
14021         if(this.view){
14022             this.view.setStore(null);
14023             this.view.el.removeAllListeners();
14024             this.view.el.remove();
14025             this.view.purgeListeners();
14026         }
14027         if(this.list){
14028             this.list.dom.innerHTML  = '';
14029         }
14030         
14031         if(this.store){
14032             this.store.un('beforeload', this.onBeforeLoad, this);
14033             this.store.un('load', this.onLoad, this);
14034             this.store.un('loadexception', this.onLoadException, this);
14035         }
14036         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14037     },
14038
14039     // private
14040     fireKey : function(e){
14041         if(e.isNavKeyPress() && !this.list.isVisible()){
14042             this.fireEvent("specialkey", this, e);
14043         }
14044     },
14045
14046     // private
14047     onResize: function(w, h){
14048 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14049 //        
14050 //        if(typeof w != 'number'){
14051 //            // we do not handle it!?!?
14052 //            return;
14053 //        }
14054 //        var tw = this.trigger.getWidth();
14055 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14056 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14057 //        var x = w - tw;
14058 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14059 //            
14060 //        //this.trigger.setStyle('left', x+'px');
14061 //        
14062 //        if(this.list && this.listWidth === undefined){
14063 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14064 //            this.list.setWidth(lw);
14065 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14066 //        }
14067         
14068     
14069         
14070     },
14071
14072     /**
14073      * Allow or prevent the user from directly editing the field text.  If false is passed,
14074      * the user will only be able to select from the items defined in the dropdown list.  This method
14075      * is the runtime equivalent of setting the 'editable' config option at config time.
14076      * @param {Boolean} value True to allow the user to directly edit the field text
14077      */
14078     setEditable : function(value){
14079         if(value == this.editable){
14080             return;
14081         }
14082         this.editable = value;
14083         if(!value){
14084             this.inputEl().dom.setAttribute('readOnly', true);
14085             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14086             this.inputEl().addClass('x-combo-noedit');
14087         }else{
14088             this.inputEl().dom.setAttribute('readOnly', false);
14089             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14090             this.inputEl().removeClass('x-combo-noedit');
14091         }
14092     },
14093
14094     // private
14095     
14096     onBeforeLoad : function(combo,opts){
14097         if(!this.hasFocus){
14098             return;
14099         }
14100          if (!opts.add) {
14101             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14102          }
14103         this.restrictHeight();
14104         this.selectedIndex = -1;
14105     },
14106
14107     // private
14108     onLoad : function(){
14109         
14110         this.hasQuery = false;
14111         
14112         if(!this.hasFocus){
14113             return;
14114         }
14115         
14116         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14117             this.loading.hide();
14118         }
14119         
14120         if(this.store.getCount() > 0){
14121             
14122             this.expand();
14123             this.restrictHeight();
14124             if(this.lastQuery == this.allQuery){
14125                 if(this.editable && !this.tickable){
14126                     this.inputEl().dom.select();
14127                 }
14128                 
14129                 if(
14130                     !this.selectByValue(this.value, true) &&
14131                     this.autoFocus && 
14132                     (
14133                         !this.store.lastOptions ||
14134                         typeof(this.store.lastOptions.add) == 'undefined' || 
14135                         this.store.lastOptions.add != true
14136                     )
14137                 ){
14138                     this.select(0, true);
14139                 }
14140             }else{
14141                 if(this.autoFocus){
14142                     this.selectNext();
14143                 }
14144                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14145                     this.taTask.delay(this.typeAheadDelay);
14146                 }
14147             }
14148         }else{
14149             this.onEmptyResults();
14150         }
14151         
14152         //this.el.focus();
14153     },
14154     // private
14155     onLoadException : function()
14156     {
14157         this.hasQuery = false;
14158         
14159         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14160             this.loading.hide();
14161         }
14162         
14163         if(this.tickable && this.editable){
14164             return;
14165         }
14166         
14167         this.collapse();
14168         // only causes errors at present
14169         //Roo.log(this.store.reader.jsonData);
14170         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14171             // fixme
14172             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14173         //}
14174         
14175         
14176     },
14177     // private
14178     onTypeAhead : function(){
14179         if(this.store.getCount() > 0){
14180             var r = this.store.getAt(0);
14181             var newValue = r.data[this.displayField];
14182             var len = newValue.length;
14183             var selStart = this.getRawValue().length;
14184             
14185             if(selStart != len){
14186                 this.setRawValue(newValue);
14187                 this.selectText(selStart, newValue.length);
14188             }
14189         }
14190     },
14191
14192     // private
14193     onSelect : function(record, index){
14194         
14195         if(this.fireEvent('beforeselect', this, record, index) !== false){
14196         
14197             this.setFromData(index > -1 ? record.data : false);
14198             
14199             this.collapse();
14200             this.fireEvent('select', this, record, index);
14201         }
14202     },
14203
14204     /**
14205      * Returns the currently selected field value or empty string if no value is set.
14206      * @return {String} value The selected value
14207      */
14208     getValue : function()
14209     {
14210         if(Roo.isIOS && this.useNativeIOS){
14211             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14212         }
14213         
14214         if(this.multiple){
14215             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14216         }
14217         
14218         if(this.valueField){
14219             return typeof this.value != 'undefined' ? this.value : '';
14220         }else{
14221             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14222         }
14223     },
14224     
14225     getRawValue : function()
14226     {
14227         if(Roo.isIOS && this.useNativeIOS){
14228             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14229         }
14230         
14231         var v = this.inputEl().getValue();
14232         
14233         return v;
14234     },
14235
14236     /**
14237      * Clears any text/value currently set in the field
14238      */
14239     clearValue : function(){
14240         
14241         if(this.hiddenField){
14242             this.hiddenField.dom.value = '';
14243         }
14244         this.value = '';
14245         this.setRawValue('');
14246         this.lastSelectionText = '';
14247         this.lastData = false;
14248         
14249         var close = this.closeTriggerEl();
14250         
14251         if(close){
14252             close.hide();
14253         }
14254         
14255         this.validate();
14256         
14257     },
14258
14259     /**
14260      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14261      * will be displayed in the field.  If the value does not match the data value of an existing item,
14262      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14263      * Otherwise the field will be blank (although the value will still be set).
14264      * @param {String} value The value to match
14265      */
14266     setValue : function(v)
14267     {
14268         if(Roo.isIOS && this.useNativeIOS){
14269             this.setIOSValue(v);
14270             return;
14271         }
14272         
14273         if(this.multiple){
14274             this.syncValue();
14275             return;
14276         }
14277         
14278         var text = v;
14279         if(this.valueField){
14280             var r = this.findRecord(this.valueField, v);
14281             if(r){
14282                 text = r.data[this.displayField];
14283             }else if(this.valueNotFoundText !== undefined){
14284                 text = this.valueNotFoundText;
14285             }
14286         }
14287         this.lastSelectionText = text;
14288         if(this.hiddenField){
14289             this.hiddenField.dom.value = v;
14290         }
14291         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14292         this.value = v;
14293         
14294         var close = this.closeTriggerEl();
14295         
14296         if(close){
14297             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14298         }
14299         
14300         this.validate();
14301     },
14302     /**
14303      * @property {Object} the last set data for the element
14304      */
14305     
14306     lastData : false,
14307     /**
14308      * Sets the value of the field based on a object which is related to the record format for the store.
14309      * @param {Object} value the value to set as. or false on reset?
14310      */
14311     setFromData : function(o){
14312         
14313         if(this.multiple){
14314             this.addItem(o);
14315             return;
14316         }
14317             
14318         var dv = ''; // display value
14319         var vv = ''; // value value..
14320         this.lastData = o;
14321         if (this.displayField) {
14322             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14323         } else {
14324             // this is an error condition!!!
14325             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14326         }
14327         
14328         if(this.valueField){
14329             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14330         }
14331         
14332         var close = this.closeTriggerEl();
14333         
14334         if(close){
14335             if(dv.length || vv * 1 > 0){
14336                 close.show() ;
14337                 this.blockFocus=true;
14338             } else {
14339                 close.hide();
14340             }             
14341         }
14342         
14343         if(this.hiddenField){
14344             this.hiddenField.dom.value = vv;
14345             
14346             this.lastSelectionText = dv;
14347             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14348             this.value = vv;
14349             return;
14350         }
14351         // no hidden field.. - we store the value in 'value', but still display
14352         // display field!!!!
14353         this.lastSelectionText = dv;
14354         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14355         this.value = vv;
14356         
14357         
14358         
14359     },
14360     // private
14361     reset : function(){
14362         // overridden so that last data is reset..
14363         
14364         if(this.multiple){
14365             this.clearItem();
14366             return;
14367         }
14368         
14369         this.setValue(this.originalValue);
14370         //this.clearInvalid();
14371         this.lastData = false;
14372         if (this.view) {
14373             this.view.clearSelections();
14374         }
14375         
14376         this.validate();
14377     },
14378     // private
14379     findRecord : function(prop, value){
14380         var record;
14381         if(this.store.getCount() > 0){
14382             this.store.each(function(r){
14383                 if(r.data[prop] == value){
14384                     record = r;
14385                     return false;
14386                 }
14387                 return true;
14388             });
14389         }
14390         return record;
14391     },
14392     
14393     getName: function()
14394     {
14395         // returns hidden if it's set..
14396         if (!this.rendered) {return ''};
14397         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14398         
14399     },
14400     // private
14401     onViewMove : function(e, t){
14402         this.inKeyMode = false;
14403     },
14404
14405     // private
14406     onViewOver : function(e, t){
14407         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14408             return;
14409         }
14410         var item = this.view.findItemFromChild(t);
14411         
14412         if(item){
14413             var index = this.view.indexOf(item);
14414             this.select(index, false);
14415         }
14416     },
14417
14418     // private
14419     onViewClick : function(view, doFocus, el, e)
14420     {
14421         var index = this.view.getSelectedIndexes()[0];
14422         
14423         var r = this.store.getAt(index);
14424         
14425         if(this.tickable){
14426             
14427             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14428                 return;
14429             }
14430             
14431             var rm = false;
14432             var _this = this;
14433             
14434             Roo.each(this.tickItems, function(v,k){
14435                 
14436                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14437                     Roo.log(v);
14438                     _this.tickItems.splice(k, 1);
14439                     
14440                     if(typeof(e) == 'undefined' && view == false){
14441                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14442                     }
14443                     
14444                     rm = true;
14445                     return;
14446                 }
14447             });
14448             
14449             if(rm){
14450                 return;
14451             }
14452             
14453             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14454                 this.tickItems.push(r.data);
14455             }
14456             
14457             if(typeof(e) == 'undefined' && view == false){
14458                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14459             }
14460                     
14461             return;
14462         }
14463         
14464         if(r){
14465             this.onSelect(r, index);
14466         }
14467         if(doFocus !== false && !this.blockFocus){
14468             this.inputEl().focus();
14469         }
14470     },
14471
14472     // private
14473     restrictHeight : function(){
14474         //this.innerList.dom.style.height = '';
14475         //var inner = this.innerList.dom;
14476         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14477         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14478         //this.list.beginUpdate();
14479         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14480         this.list.alignTo(this.inputEl(), this.listAlign);
14481         this.list.alignTo(this.inputEl(), this.listAlign);
14482         //this.list.endUpdate();
14483     },
14484
14485     // private
14486     onEmptyResults : function(){
14487         
14488         if(this.tickable && this.editable){
14489             this.hasFocus = false;
14490             this.restrictHeight();
14491             return;
14492         }
14493         
14494         this.collapse();
14495     },
14496
14497     /**
14498      * Returns true if the dropdown list is expanded, else false.
14499      */
14500     isExpanded : function(){
14501         return this.list.isVisible();
14502     },
14503
14504     /**
14505      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14506      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14507      * @param {String} value The data value of the item to select
14508      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14509      * selected item if it is not currently in view (defaults to true)
14510      * @return {Boolean} True if the value matched an item in the list, else false
14511      */
14512     selectByValue : function(v, scrollIntoView){
14513         if(v !== undefined && v !== null){
14514             var r = this.findRecord(this.valueField || this.displayField, v);
14515             if(r){
14516                 this.select(this.store.indexOf(r), scrollIntoView);
14517                 return true;
14518             }
14519         }
14520         return false;
14521     },
14522
14523     /**
14524      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14525      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14526      * @param {Number} index The zero-based index of the list item to select
14527      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14528      * selected item if it is not currently in view (defaults to true)
14529      */
14530     select : function(index, scrollIntoView){
14531         this.selectedIndex = index;
14532         this.view.select(index);
14533         if(scrollIntoView !== false){
14534             var el = this.view.getNode(index);
14535             /*
14536              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14537              */
14538             if(el){
14539                 this.list.scrollChildIntoView(el, false);
14540             }
14541         }
14542     },
14543
14544     // private
14545     selectNext : function(){
14546         var ct = this.store.getCount();
14547         if(ct > 0){
14548             if(this.selectedIndex == -1){
14549                 this.select(0);
14550             }else if(this.selectedIndex < ct-1){
14551                 this.select(this.selectedIndex+1);
14552             }
14553         }
14554     },
14555
14556     // private
14557     selectPrev : function(){
14558         var ct = this.store.getCount();
14559         if(ct > 0){
14560             if(this.selectedIndex == -1){
14561                 this.select(0);
14562             }else if(this.selectedIndex != 0){
14563                 this.select(this.selectedIndex-1);
14564             }
14565         }
14566     },
14567
14568     // private
14569     onKeyUp : function(e){
14570         if(this.editable !== false && !e.isSpecialKey()){
14571             this.lastKey = e.getKey();
14572             this.dqTask.delay(this.queryDelay);
14573         }
14574     },
14575
14576     // private
14577     validateBlur : function(){
14578         return !this.list || !this.list.isVisible();   
14579     },
14580
14581     // private
14582     initQuery : function(){
14583         
14584         var v = this.getRawValue();
14585         
14586         if(this.tickable && this.editable){
14587             v = this.tickableInputEl().getValue();
14588         }
14589         
14590         this.doQuery(v);
14591     },
14592
14593     // private
14594     doForce : function(){
14595         if(this.inputEl().dom.value.length > 0){
14596             this.inputEl().dom.value =
14597                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14598              
14599         }
14600     },
14601
14602     /**
14603      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14604      * query allowing the query action to be canceled if needed.
14605      * @param {String} query The SQL query to execute
14606      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14607      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14608      * saved in the current store (defaults to false)
14609      */
14610     doQuery : function(q, forceAll){
14611         
14612         if(q === undefined || q === null){
14613             q = '';
14614         }
14615         var qe = {
14616             query: q,
14617             forceAll: forceAll,
14618             combo: this,
14619             cancel:false
14620         };
14621         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14622             return false;
14623         }
14624         q = qe.query;
14625         
14626         forceAll = qe.forceAll;
14627         if(forceAll === true || (q.length >= this.minChars)){
14628             
14629             this.hasQuery = true;
14630             
14631             if(this.lastQuery != q || this.alwaysQuery){
14632                 this.lastQuery = q;
14633                 if(this.mode == 'local'){
14634                     this.selectedIndex = -1;
14635                     if(forceAll){
14636                         this.store.clearFilter();
14637                     }else{
14638                         
14639                         if(this.specialFilter){
14640                             this.fireEvent('specialfilter', this);
14641                             this.onLoad();
14642                             return;
14643                         }
14644                         
14645                         this.store.filter(this.displayField, q);
14646                     }
14647                     
14648                     this.store.fireEvent("datachanged", this.store);
14649                     
14650                     this.onLoad();
14651                     
14652                     
14653                 }else{
14654                     
14655                     this.store.baseParams[this.queryParam] = q;
14656                     
14657                     var options = {params : this.getParams(q)};
14658                     
14659                     if(this.loadNext){
14660                         options.add = true;
14661                         options.params.start = this.page * this.pageSize;
14662                     }
14663                     
14664                     this.store.load(options);
14665                     
14666                     /*
14667                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14668                      *  we should expand the list on onLoad
14669                      *  so command out it
14670                      */
14671 //                    this.expand();
14672                 }
14673             }else{
14674                 this.selectedIndex = -1;
14675                 this.onLoad();   
14676             }
14677         }
14678         
14679         this.loadNext = false;
14680     },
14681     
14682     // private
14683     getParams : function(q){
14684         var p = {};
14685         //p[this.queryParam] = q;
14686         
14687         if(this.pageSize){
14688             p.start = 0;
14689             p.limit = this.pageSize;
14690         }
14691         return p;
14692     },
14693
14694     /**
14695      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14696      */
14697     collapse : function(){
14698         if(!this.isExpanded()){
14699             return;
14700         }
14701         
14702         this.list.hide();
14703         
14704         this.hasFocus = false;
14705         
14706         if(this.tickable){
14707             this.okBtn.hide();
14708             this.cancelBtn.hide();
14709             this.trigger.show();
14710             
14711             if(this.editable){
14712                 this.tickableInputEl().dom.value = '';
14713                 this.tickableInputEl().blur();
14714             }
14715             
14716         }
14717         
14718         Roo.get(document).un('mousedown', this.collapseIf, this);
14719         Roo.get(document).un('mousewheel', this.collapseIf, this);
14720         if (!this.editable) {
14721             Roo.get(document).un('keydown', this.listKeyPress, this);
14722         }
14723         this.fireEvent('collapse', this);
14724         
14725         this.validate();
14726     },
14727
14728     // private
14729     collapseIf : function(e){
14730         var in_combo  = e.within(this.el);
14731         var in_list =  e.within(this.list);
14732         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14733         
14734         if (in_combo || in_list || is_list) {
14735             //e.stopPropagation();
14736             return;
14737         }
14738         
14739         if(this.tickable){
14740             this.onTickableFooterButtonClick(e, false, false);
14741         }
14742
14743         this.collapse();
14744         
14745     },
14746
14747     /**
14748      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14749      */
14750     expand : function(){
14751        
14752         if(this.isExpanded() || !this.hasFocus){
14753             return;
14754         }
14755         
14756         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14757         this.list.setWidth(lw);
14758         
14759         Roo.log('expand');
14760         
14761         this.list.show();
14762         
14763         this.restrictHeight();
14764         
14765         if(this.tickable){
14766             
14767             this.tickItems = Roo.apply([], this.item);
14768             
14769             this.okBtn.show();
14770             this.cancelBtn.show();
14771             this.trigger.hide();
14772             
14773             if(this.editable){
14774                 this.tickableInputEl().focus();
14775             }
14776             
14777         }
14778         
14779         Roo.get(document).on('mousedown', this.collapseIf, this);
14780         Roo.get(document).on('mousewheel', this.collapseIf, this);
14781         if (!this.editable) {
14782             Roo.get(document).on('keydown', this.listKeyPress, this);
14783         }
14784         
14785         this.fireEvent('expand', this);
14786     },
14787
14788     // private
14789     // Implements the default empty TriggerField.onTriggerClick function
14790     onTriggerClick : function(e)
14791     {
14792         Roo.log('trigger click');
14793         
14794         if(this.disabled || !this.triggerList){
14795             return;
14796         }
14797         
14798         this.page = 0;
14799         this.loadNext = false;
14800         
14801         if(this.isExpanded()){
14802             this.collapse();
14803             if (!this.blockFocus) {
14804                 this.inputEl().focus();
14805             }
14806             
14807         }else {
14808             this.hasFocus = true;
14809             if(this.triggerAction == 'all') {
14810                 this.doQuery(this.allQuery, true);
14811             } else {
14812                 this.doQuery(this.getRawValue());
14813             }
14814             if (!this.blockFocus) {
14815                 this.inputEl().focus();
14816             }
14817         }
14818     },
14819     
14820     onTickableTriggerClick : function(e)
14821     {
14822         if(this.disabled){
14823             return;
14824         }
14825         
14826         this.page = 0;
14827         this.loadNext = false;
14828         this.hasFocus = true;
14829         
14830         if(this.triggerAction == 'all') {
14831             this.doQuery(this.allQuery, true);
14832         } else {
14833             this.doQuery(this.getRawValue());
14834         }
14835     },
14836     
14837     onSearchFieldClick : function(e)
14838     {
14839         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14840             this.onTickableFooterButtonClick(e, false, false);
14841             return;
14842         }
14843         
14844         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14845             return;
14846         }
14847         
14848         this.page = 0;
14849         this.loadNext = false;
14850         this.hasFocus = true;
14851         
14852         if(this.triggerAction == 'all') {
14853             this.doQuery(this.allQuery, true);
14854         } else {
14855             this.doQuery(this.getRawValue());
14856         }
14857     },
14858     
14859     listKeyPress : function(e)
14860     {
14861         //Roo.log('listkeypress');
14862         // scroll to first matching element based on key pres..
14863         if (e.isSpecialKey()) {
14864             return false;
14865         }
14866         var k = String.fromCharCode(e.getKey()).toUpperCase();
14867         //Roo.log(k);
14868         var match  = false;
14869         var csel = this.view.getSelectedNodes();
14870         var cselitem = false;
14871         if (csel.length) {
14872             var ix = this.view.indexOf(csel[0]);
14873             cselitem  = this.store.getAt(ix);
14874             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14875                 cselitem = false;
14876             }
14877             
14878         }
14879         
14880         this.store.each(function(v) { 
14881             if (cselitem) {
14882                 // start at existing selection.
14883                 if (cselitem.id == v.id) {
14884                     cselitem = false;
14885                 }
14886                 return true;
14887             }
14888                 
14889             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14890                 match = this.store.indexOf(v);
14891                 return false;
14892             }
14893             return true;
14894         }, this);
14895         
14896         if (match === false) {
14897             return true; // no more action?
14898         }
14899         // scroll to?
14900         this.view.select(match);
14901         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14902         sn.scrollIntoView(sn.dom.parentNode, false);
14903     },
14904     
14905     onViewScroll : function(e, t){
14906         
14907         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){
14908             return;
14909         }
14910         
14911         this.hasQuery = true;
14912         
14913         this.loading = this.list.select('.loading', true).first();
14914         
14915         if(this.loading === null){
14916             this.list.createChild({
14917                 tag: 'div',
14918                 cls: 'loading roo-select2-more-results roo-select2-active',
14919                 html: 'Loading more results...'
14920             });
14921             
14922             this.loading = this.list.select('.loading', true).first();
14923             
14924             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14925             
14926             this.loading.hide();
14927         }
14928         
14929         this.loading.show();
14930         
14931         var _combo = this;
14932         
14933         this.page++;
14934         this.loadNext = true;
14935         
14936         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14937         
14938         return;
14939     },
14940     
14941     addItem : function(o)
14942     {   
14943         var dv = ''; // display value
14944         
14945         if (this.displayField) {
14946             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14947         } else {
14948             // this is an error condition!!!
14949             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14950         }
14951         
14952         if(!dv.length){
14953             return;
14954         }
14955         
14956         var choice = this.choices.createChild({
14957             tag: 'li',
14958             cls: 'roo-select2-search-choice',
14959             cn: [
14960                 {
14961                     tag: 'div',
14962                     html: dv
14963                 },
14964                 {
14965                     tag: 'a',
14966                     href: '#',
14967                     cls: 'roo-select2-search-choice-close fa fa-times',
14968                     tabindex: '-1'
14969                 }
14970             ]
14971             
14972         }, this.searchField);
14973         
14974         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14975         
14976         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14977         
14978         this.item.push(o);
14979         
14980         this.lastData = o;
14981         
14982         this.syncValue();
14983         
14984         this.inputEl().dom.value = '';
14985         
14986         this.validate();
14987     },
14988     
14989     onRemoveItem : function(e, _self, o)
14990     {
14991         e.preventDefault();
14992         
14993         this.lastItem = Roo.apply([], this.item);
14994         
14995         var index = this.item.indexOf(o.data) * 1;
14996         
14997         if( index < 0){
14998             Roo.log('not this item?!');
14999             return;
15000         }
15001         
15002         this.item.splice(index, 1);
15003         o.item.remove();
15004         
15005         this.syncValue();
15006         
15007         this.fireEvent('remove', this, e);
15008         
15009         this.validate();
15010         
15011     },
15012     
15013     syncValue : function()
15014     {
15015         if(!this.item.length){
15016             this.clearValue();
15017             return;
15018         }
15019             
15020         var value = [];
15021         var _this = this;
15022         Roo.each(this.item, function(i){
15023             if(_this.valueField){
15024                 value.push(i[_this.valueField]);
15025                 return;
15026             }
15027
15028             value.push(i);
15029         });
15030
15031         this.value = value.join(',');
15032
15033         if(this.hiddenField){
15034             this.hiddenField.dom.value = this.value;
15035         }
15036         
15037         this.store.fireEvent("datachanged", this.store);
15038         
15039         this.validate();
15040     },
15041     
15042     clearItem : function()
15043     {
15044         if(!this.multiple){
15045             return;
15046         }
15047         
15048         this.item = [];
15049         
15050         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15051            c.remove();
15052         });
15053         
15054         this.syncValue();
15055         
15056         this.validate();
15057         
15058         if(this.tickable && !Roo.isTouch){
15059             this.view.refresh();
15060         }
15061     },
15062     
15063     inputEl: function ()
15064     {
15065         if(Roo.isIOS && this.useNativeIOS){
15066             return this.el.select('select.roo-ios-select', true).first();
15067         }
15068         
15069         if(Roo.isTouch && this.mobileTouchView){
15070             return this.el.select('input.form-control',true).first();
15071         }
15072         
15073         if(this.tickable){
15074             return this.searchField;
15075         }
15076         
15077         return this.el.select('input.form-control',true).first();
15078     },
15079     
15080     onTickableFooterButtonClick : function(e, btn, el)
15081     {
15082         e.preventDefault();
15083         
15084         this.lastItem = Roo.apply([], this.item);
15085         
15086         if(btn && btn.name == 'cancel'){
15087             this.tickItems = Roo.apply([], this.item);
15088             this.collapse();
15089             return;
15090         }
15091         
15092         this.clearItem();
15093         
15094         var _this = this;
15095         
15096         Roo.each(this.tickItems, function(o){
15097             _this.addItem(o);
15098         });
15099         
15100         this.collapse();
15101         
15102     },
15103     
15104     validate : function()
15105     {
15106         if(this.getVisibilityEl().hasClass('hidden')){
15107             return true;
15108         }
15109         
15110         var v = this.getRawValue();
15111         
15112         if(this.multiple){
15113             v = this.getValue();
15114         }
15115         
15116         if(this.disabled || this.allowBlank || v.length){
15117             this.markValid();
15118             return true;
15119         }
15120         
15121         this.markInvalid();
15122         return false;
15123     },
15124     
15125     tickableInputEl : function()
15126     {
15127         if(!this.tickable || !this.editable){
15128             return this.inputEl();
15129         }
15130         
15131         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15132     },
15133     
15134     
15135     getAutoCreateTouchView : function()
15136     {
15137         var id = Roo.id();
15138         
15139         var cfg = {
15140             cls: 'form-group' //input-group
15141         };
15142         
15143         var input =  {
15144             tag: 'input',
15145             id : id,
15146             type : this.inputType,
15147             cls : 'form-control x-combo-noedit',
15148             autocomplete: 'new-password',
15149             placeholder : this.placeholder || '',
15150             readonly : true
15151         };
15152         
15153         if (this.name) {
15154             input.name = this.name;
15155         }
15156         
15157         if (this.size) {
15158             input.cls += ' input-' + this.size;
15159         }
15160         
15161         if (this.disabled) {
15162             input.disabled = true;
15163         }
15164         
15165         var inputblock = {
15166             cls : '',
15167             cn : [
15168                 input
15169             ]
15170         };
15171         
15172         if(this.before){
15173             inputblock.cls += ' input-group';
15174             
15175             inputblock.cn.unshift({
15176                 tag :'span',
15177                 cls : 'input-group-addon input-group-prepend input-group-text',
15178                 html : this.before
15179             });
15180         }
15181         
15182         if(this.removable && !this.multiple){
15183             inputblock.cls += ' roo-removable';
15184             
15185             inputblock.cn.push({
15186                 tag: 'button',
15187                 html : 'x',
15188                 cls : 'roo-combo-removable-btn close'
15189             });
15190         }
15191
15192         if(this.hasFeedback && !this.allowBlank){
15193             
15194             inputblock.cls += ' has-feedback';
15195             
15196             inputblock.cn.push({
15197                 tag: 'span',
15198                 cls: 'glyphicon form-control-feedback'
15199             });
15200             
15201         }
15202         
15203         if (this.after) {
15204             
15205             inputblock.cls += (this.before) ? '' : ' input-group';
15206             
15207             inputblock.cn.push({
15208                 tag :'span',
15209                 cls : 'input-group-addon input-group-append input-group-text',
15210                 html : this.after
15211             });
15212         }
15213
15214         
15215         var ibwrap = inputblock;
15216         
15217         if(this.multiple){
15218             ibwrap = {
15219                 tag: 'ul',
15220                 cls: 'roo-select2-choices',
15221                 cn:[
15222                     {
15223                         tag: 'li',
15224                         cls: 'roo-select2-search-field',
15225                         cn: [
15226
15227                             inputblock
15228                         ]
15229                     }
15230                 ]
15231             };
15232         
15233             
15234         }
15235         
15236         var combobox = {
15237             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15238             cn: [
15239                 {
15240                     tag: 'input',
15241                     type : 'hidden',
15242                     cls: 'form-hidden-field'
15243                 },
15244                 ibwrap
15245             ]
15246         };
15247         
15248         if(!this.multiple && this.showToggleBtn){
15249             
15250             var caret = {
15251                         tag: 'span',
15252                         cls: 'caret'
15253             };
15254             
15255             if (this.caret != false) {
15256                 caret = {
15257                      tag: 'i',
15258                      cls: 'fa fa-' + this.caret
15259                 };
15260                 
15261             }
15262             
15263             combobox.cn.push({
15264                 tag :'span',
15265                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15266                 cn : [
15267                     caret,
15268                     {
15269                         tag: 'span',
15270                         cls: 'combobox-clear',
15271                         cn  : [
15272                             {
15273                                 tag : 'i',
15274                                 cls: 'icon-remove'
15275                             }
15276                         ]
15277                     }
15278                 ]
15279
15280             })
15281         }
15282         
15283         if(this.multiple){
15284             combobox.cls += ' roo-select2-container-multi';
15285         }
15286         
15287         var align = this.labelAlign || this.parentLabelAlign();
15288         
15289         if (align ==='left' && this.fieldLabel.length) {
15290
15291             cfg.cn = [
15292                 {
15293                    tag : 'i',
15294                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15295                    tooltip : 'This field is required'
15296                 },
15297                 {
15298                     tag: 'label',
15299                     cls : 'control-label col-form-label',
15300                     html : this.fieldLabel
15301
15302                 },
15303                 {
15304                     cls : '', 
15305                     cn: [
15306                         combobox
15307                     ]
15308                 }
15309             ];
15310             
15311             var labelCfg = cfg.cn[1];
15312             var contentCfg = cfg.cn[2];
15313             
15314
15315             if(this.indicatorpos == 'right'){
15316                 cfg.cn = [
15317                     {
15318                         tag: 'label',
15319                         'for' :  id,
15320                         cls : 'control-label col-form-label',
15321                         cn : [
15322                             {
15323                                 tag : 'span',
15324                                 html : this.fieldLabel
15325                             },
15326                             {
15327                                 tag : 'i',
15328                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15329                                 tooltip : 'This field is required'
15330                             }
15331                         ]
15332                     },
15333                     {
15334                         cls : "",
15335                         cn: [
15336                             combobox
15337                         ]
15338                     }
15339
15340                 ];
15341                 
15342                 labelCfg = cfg.cn[0];
15343                 contentCfg = cfg.cn[1];
15344             }
15345             
15346            
15347             
15348             if(this.labelWidth > 12){
15349                 labelCfg.style = "width: " + this.labelWidth + 'px';
15350             }
15351             
15352             if(this.labelWidth < 13 && this.labelmd == 0){
15353                 this.labelmd = this.labelWidth;
15354             }
15355             
15356             if(this.labellg > 0){
15357                 labelCfg.cls += ' col-lg-' + this.labellg;
15358                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15359             }
15360             
15361             if(this.labelmd > 0){
15362                 labelCfg.cls += ' col-md-' + this.labelmd;
15363                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15364             }
15365             
15366             if(this.labelsm > 0){
15367                 labelCfg.cls += ' col-sm-' + this.labelsm;
15368                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15369             }
15370             
15371             if(this.labelxs > 0){
15372                 labelCfg.cls += ' col-xs-' + this.labelxs;
15373                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15374             }
15375                 
15376                 
15377         } else if ( this.fieldLabel.length) {
15378             cfg.cn = [
15379                 {
15380                    tag : 'i',
15381                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15382                    tooltip : 'This field is required'
15383                 },
15384                 {
15385                     tag: 'label',
15386                     cls : 'control-label',
15387                     html : this.fieldLabel
15388
15389                 },
15390                 {
15391                     cls : '', 
15392                     cn: [
15393                         combobox
15394                     ]
15395                 }
15396             ];
15397             
15398             if(this.indicatorpos == 'right'){
15399                 cfg.cn = [
15400                     {
15401                         tag: 'label',
15402                         cls : 'control-label',
15403                         html : this.fieldLabel,
15404                         cn : [
15405                             {
15406                                tag : 'i',
15407                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15408                                tooltip : 'This field is required'
15409                             }
15410                         ]
15411                     },
15412                     {
15413                         cls : '', 
15414                         cn: [
15415                             combobox
15416                         ]
15417                     }
15418                 ];
15419             }
15420         } else {
15421             cfg.cn = combobox;    
15422         }
15423         
15424         
15425         var settings = this;
15426         
15427         ['xs','sm','md','lg'].map(function(size){
15428             if (settings[size]) {
15429                 cfg.cls += ' col-' + size + '-' + settings[size];
15430             }
15431         });
15432         
15433         return cfg;
15434     },
15435     
15436     initTouchView : function()
15437     {
15438         this.renderTouchView();
15439         
15440         this.touchViewEl.on('scroll', function(){
15441             this.el.dom.scrollTop = 0;
15442         }, this);
15443         
15444         this.originalValue = this.getValue();
15445         
15446         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15447         
15448         this.inputEl().on("click", this.showTouchView, this);
15449         if (this.triggerEl) {
15450             this.triggerEl.on("click", this.showTouchView, this);
15451         }
15452         
15453         
15454         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15455         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15456         
15457         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15458         
15459         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15460         this.store.on('load', this.onTouchViewLoad, this);
15461         this.store.on('loadexception', this.onTouchViewLoadException, this);
15462         
15463         if(this.hiddenName){
15464             
15465             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15466             
15467             this.hiddenField.dom.value =
15468                 this.hiddenValue !== undefined ? this.hiddenValue :
15469                 this.value !== undefined ? this.value : '';
15470         
15471             this.el.dom.removeAttribute('name');
15472             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15473         }
15474         
15475         if(this.multiple){
15476             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15477             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15478         }
15479         
15480         if(this.removable && !this.multiple){
15481             var close = this.closeTriggerEl();
15482             if(close){
15483                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15484                 close.on('click', this.removeBtnClick, this, close);
15485             }
15486         }
15487         /*
15488          * fix the bug in Safari iOS8
15489          */
15490         this.inputEl().on("focus", function(e){
15491             document.activeElement.blur();
15492         }, this);
15493         
15494         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15495         
15496         return;
15497         
15498         
15499     },
15500     
15501     renderTouchView : function()
15502     {
15503         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15504         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15505         
15506         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15507         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15508         
15509         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15510         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15511         this.touchViewBodyEl.setStyle('overflow', 'auto');
15512         
15513         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15514         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15515         
15516         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15517         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15518         
15519     },
15520     
15521     showTouchView : function()
15522     {
15523         if(this.disabled){
15524             return;
15525         }
15526         
15527         this.touchViewHeaderEl.hide();
15528
15529         if(this.modalTitle.length){
15530             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15531             this.touchViewHeaderEl.show();
15532         }
15533
15534         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15535         this.touchViewEl.show();
15536
15537         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15538         
15539         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15540         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15541
15542         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15543
15544         if(this.modalTitle.length){
15545             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15546         }
15547         
15548         this.touchViewBodyEl.setHeight(bodyHeight);
15549
15550         if(this.animate){
15551             var _this = this;
15552             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15553         }else{
15554             this.touchViewEl.addClass('in');
15555         }
15556         
15557         if(this._touchViewMask){
15558             Roo.get(document.body).addClass("x-body-masked");
15559             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15560             this._touchViewMask.setStyle('z-index', 10000);
15561             this._touchViewMask.addClass('show');
15562         }
15563         
15564         this.doTouchViewQuery();
15565         
15566     },
15567     
15568     hideTouchView : function()
15569     {
15570         this.touchViewEl.removeClass('in');
15571
15572         if(this.animate){
15573             var _this = this;
15574             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15575         }else{
15576             this.touchViewEl.setStyle('display', 'none');
15577         }
15578         
15579         if(this._touchViewMask){
15580             this._touchViewMask.removeClass('show');
15581             Roo.get(document.body).removeClass("x-body-masked");
15582         }
15583     },
15584     
15585     setTouchViewValue : function()
15586     {
15587         if(this.multiple){
15588             this.clearItem();
15589         
15590             var _this = this;
15591
15592             Roo.each(this.tickItems, function(o){
15593                 this.addItem(o);
15594             }, this);
15595         }
15596         
15597         this.hideTouchView();
15598     },
15599     
15600     doTouchViewQuery : function()
15601     {
15602         var qe = {
15603             query: '',
15604             forceAll: true,
15605             combo: this,
15606             cancel:false
15607         };
15608         
15609         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15610             return false;
15611         }
15612         
15613         if(!this.alwaysQuery || this.mode == 'local'){
15614             this.onTouchViewLoad();
15615             return;
15616         }
15617         
15618         this.store.load();
15619     },
15620     
15621     onTouchViewBeforeLoad : function(combo,opts)
15622     {
15623         return;
15624     },
15625
15626     // private
15627     onTouchViewLoad : function()
15628     {
15629         if(this.store.getCount() < 1){
15630             this.onTouchViewEmptyResults();
15631             return;
15632         }
15633         
15634         this.clearTouchView();
15635         
15636         var rawValue = this.getRawValue();
15637         
15638         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15639         
15640         this.tickItems = [];
15641         
15642         this.store.data.each(function(d, rowIndex){
15643             var row = this.touchViewListGroup.createChild(template);
15644             
15645             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15646                 row.addClass(d.data.cls);
15647             }
15648             
15649             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15650                 var cfg = {
15651                     data : d.data,
15652                     html : d.data[this.displayField]
15653                 };
15654                 
15655                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15656                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15657                 }
15658             }
15659             row.removeClass('selected');
15660             if(!this.multiple && this.valueField &&
15661                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15662             {
15663                 // radio buttons..
15664                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15665                 row.addClass('selected');
15666             }
15667             
15668             if(this.multiple && this.valueField &&
15669                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15670             {
15671                 
15672                 // checkboxes...
15673                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15674                 this.tickItems.push(d.data);
15675             }
15676             
15677             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15678             
15679         }, this);
15680         
15681         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15682         
15683         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15684
15685         if(this.modalTitle.length){
15686             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15687         }
15688
15689         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15690         
15691         if(this.mobile_restrict_height && listHeight < bodyHeight){
15692             this.touchViewBodyEl.setHeight(listHeight);
15693         }
15694         
15695         var _this = this;
15696         
15697         if(firstChecked && listHeight > bodyHeight){
15698             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15699         }
15700         
15701     },
15702     
15703     onTouchViewLoadException : function()
15704     {
15705         this.hideTouchView();
15706     },
15707     
15708     onTouchViewEmptyResults : function()
15709     {
15710         this.clearTouchView();
15711         
15712         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15713         
15714         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15715         
15716     },
15717     
15718     clearTouchView : function()
15719     {
15720         this.touchViewListGroup.dom.innerHTML = '';
15721     },
15722     
15723     onTouchViewClick : function(e, el, o)
15724     {
15725         e.preventDefault();
15726         
15727         var row = o.row;
15728         var rowIndex = o.rowIndex;
15729         
15730         var r = this.store.getAt(rowIndex);
15731         
15732         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15733             
15734             if(!this.multiple){
15735                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15736                     c.dom.removeAttribute('checked');
15737                 }, this);
15738
15739                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15740
15741                 this.setFromData(r.data);
15742
15743                 var close = this.closeTriggerEl();
15744
15745                 if(close){
15746                     close.show();
15747                 }
15748
15749                 this.hideTouchView();
15750
15751                 this.fireEvent('select', this, r, rowIndex);
15752
15753                 return;
15754             }
15755
15756             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15757                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15758                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15759                 return;
15760             }
15761
15762             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15763             this.addItem(r.data);
15764             this.tickItems.push(r.data);
15765         }
15766     },
15767     
15768     getAutoCreateNativeIOS : function()
15769     {
15770         var cfg = {
15771             cls: 'form-group' //input-group,
15772         };
15773         
15774         var combobox =  {
15775             tag: 'select',
15776             cls : 'roo-ios-select'
15777         };
15778         
15779         if (this.name) {
15780             combobox.name = this.name;
15781         }
15782         
15783         if (this.disabled) {
15784             combobox.disabled = true;
15785         }
15786         
15787         var settings = this;
15788         
15789         ['xs','sm','md','lg'].map(function(size){
15790             if (settings[size]) {
15791                 cfg.cls += ' col-' + size + '-' + settings[size];
15792             }
15793         });
15794         
15795         cfg.cn = combobox;
15796         
15797         return cfg;
15798         
15799     },
15800     
15801     initIOSView : function()
15802     {
15803         this.store.on('load', this.onIOSViewLoad, this);
15804         
15805         return;
15806     },
15807     
15808     onIOSViewLoad : function()
15809     {
15810         if(this.store.getCount() < 1){
15811             return;
15812         }
15813         
15814         this.clearIOSView();
15815         
15816         if(this.allowBlank) {
15817             
15818             var default_text = '-- SELECT --';
15819             
15820             if(this.placeholder.length){
15821                 default_text = this.placeholder;
15822             }
15823             
15824             if(this.emptyTitle.length){
15825                 default_text += ' - ' + this.emptyTitle + ' -';
15826             }
15827             
15828             var opt = this.inputEl().createChild({
15829                 tag: 'option',
15830                 value : 0,
15831                 html : default_text
15832             });
15833             
15834             var o = {};
15835             o[this.valueField] = 0;
15836             o[this.displayField] = default_text;
15837             
15838             this.ios_options.push({
15839                 data : o,
15840                 el : opt
15841             });
15842             
15843         }
15844         
15845         this.store.data.each(function(d, rowIndex){
15846             
15847             var html = '';
15848             
15849             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15850                 html = d.data[this.displayField];
15851             }
15852             
15853             var value = '';
15854             
15855             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15856                 value = d.data[this.valueField];
15857             }
15858             
15859             var option = {
15860                 tag: 'option',
15861                 value : value,
15862                 html : html
15863             };
15864             
15865             if(this.value == d.data[this.valueField]){
15866                 option['selected'] = true;
15867             }
15868             
15869             var opt = this.inputEl().createChild(option);
15870             
15871             this.ios_options.push({
15872                 data : d.data,
15873                 el : opt
15874             });
15875             
15876         }, this);
15877         
15878         this.inputEl().on('change', function(){
15879            this.fireEvent('select', this);
15880         }, this);
15881         
15882     },
15883     
15884     clearIOSView: function()
15885     {
15886         this.inputEl().dom.innerHTML = '';
15887         
15888         this.ios_options = [];
15889     },
15890     
15891     setIOSValue: function(v)
15892     {
15893         this.value = v;
15894         
15895         if(!this.ios_options){
15896             return;
15897         }
15898         
15899         Roo.each(this.ios_options, function(opts){
15900            
15901            opts.el.dom.removeAttribute('selected');
15902            
15903            if(opts.data[this.valueField] != v){
15904                return;
15905            }
15906            
15907            opts.el.dom.setAttribute('selected', true);
15908            
15909         }, this);
15910     }
15911
15912     /** 
15913     * @cfg {Boolean} grow 
15914     * @hide 
15915     */
15916     /** 
15917     * @cfg {Number} growMin 
15918     * @hide 
15919     */
15920     /** 
15921     * @cfg {Number} growMax 
15922     * @hide 
15923     */
15924     /**
15925      * @hide
15926      * @method autoSize
15927      */
15928 });
15929
15930 Roo.apply(Roo.bootstrap.ComboBox,  {
15931     
15932     header : {
15933         tag: 'div',
15934         cls: 'modal-header',
15935         cn: [
15936             {
15937                 tag: 'h4',
15938                 cls: 'modal-title'
15939             }
15940         ]
15941     },
15942     
15943     body : {
15944         tag: 'div',
15945         cls: 'modal-body',
15946         cn: [
15947             {
15948                 tag: 'ul',
15949                 cls: 'list-group'
15950             }
15951         ]
15952     },
15953     
15954     listItemRadio : {
15955         tag: 'li',
15956         cls: 'list-group-item',
15957         cn: [
15958             {
15959                 tag: 'span',
15960                 cls: 'roo-combobox-list-group-item-value'
15961             },
15962             {
15963                 tag: 'div',
15964                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15965                 cn: [
15966                     {
15967                         tag: 'input',
15968                         type: 'radio'
15969                     },
15970                     {
15971                         tag: 'label'
15972                     }
15973                 ]
15974             }
15975         ]
15976     },
15977     
15978     listItemCheckbox : {
15979         tag: 'li',
15980         cls: 'list-group-item',
15981         cn: [
15982             {
15983                 tag: 'span',
15984                 cls: 'roo-combobox-list-group-item-value'
15985             },
15986             {
15987                 tag: 'div',
15988                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15989                 cn: [
15990                     {
15991                         tag: 'input',
15992                         type: 'checkbox'
15993                     },
15994                     {
15995                         tag: 'label'
15996                     }
15997                 ]
15998             }
15999         ]
16000     },
16001     
16002     emptyResult : {
16003         tag: 'div',
16004         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16005     },
16006     
16007     footer : {
16008         tag: 'div',
16009         cls: 'modal-footer',
16010         cn: [
16011             {
16012                 tag: 'div',
16013                 cls: 'row',
16014                 cn: [
16015                     {
16016                         tag: 'div',
16017                         cls: 'col-xs-6 text-left',
16018                         cn: {
16019                             tag: 'button',
16020                             cls: 'btn btn-danger roo-touch-view-cancel',
16021                             html: 'Cancel'
16022                         }
16023                     },
16024                     {
16025                         tag: 'div',
16026                         cls: 'col-xs-6 text-right',
16027                         cn: {
16028                             tag: 'button',
16029                             cls: 'btn btn-success roo-touch-view-ok',
16030                             html: 'OK'
16031                         }
16032                     }
16033                 ]
16034             }
16035         ]
16036         
16037     }
16038 });
16039
16040 Roo.apply(Roo.bootstrap.ComboBox,  {
16041     
16042     touchViewTemplate : {
16043         tag: 'div',
16044         cls: 'modal fade roo-combobox-touch-view',
16045         cn: [
16046             {
16047                 tag: 'div',
16048                 cls: 'modal-dialog',
16049                 style : 'position:fixed', // we have to fix position....
16050                 cn: [
16051                     {
16052                         tag: 'div',
16053                         cls: 'modal-content',
16054                         cn: [
16055                             Roo.bootstrap.ComboBox.header,
16056                             Roo.bootstrap.ComboBox.body,
16057                             Roo.bootstrap.ComboBox.footer
16058                         ]
16059                     }
16060                 ]
16061             }
16062         ]
16063     }
16064 });/*
16065  * Based on:
16066  * Ext JS Library 1.1.1
16067  * Copyright(c) 2006-2007, Ext JS, LLC.
16068  *
16069  * Originally Released Under LGPL - original licence link has changed is not relivant.
16070  *
16071  * Fork - LGPL
16072  * <script type="text/javascript">
16073  */
16074
16075 /**
16076  * @class Roo.View
16077  * @extends Roo.util.Observable
16078  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16079  * This class also supports single and multi selection modes. <br>
16080  * Create a data model bound view:
16081  <pre><code>
16082  var store = new Roo.data.Store(...);
16083
16084  var view = new Roo.View({
16085     el : "my-element",
16086     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16087  
16088     singleSelect: true,
16089     selectedClass: "ydataview-selected",
16090     store: store
16091  });
16092
16093  // listen for node click?
16094  view.on("click", function(vw, index, node, e){
16095  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16096  });
16097
16098  // load XML data
16099  dataModel.load("foobar.xml");
16100  </code></pre>
16101  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16102  * <br><br>
16103  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16104  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16105  * 
16106  * Note: old style constructor is still suported (container, template, config)
16107  * 
16108  * @constructor
16109  * Create a new View
16110  * @param {Object} config The config object
16111  * 
16112  */
16113 Roo.View = function(config, depreciated_tpl, depreciated_config){
16114     
16115     this.parent = false;
16116     
16117     if (typeof(depreciated_tpl) == 'undefined') {
16118         // new way.. - universal constructor.
16119         Roo.apply(this, config);
16120         this.el  = Roo.get(this.el);
16121     } else {
16122         // old format..
16123         this.el  = Roo.get(config);
16124         this.tpl = depreciated_tpl;
16125         Roo.apply(this, depreciated_config);
16126     }
16127     this.wrapEl  = this.el.wrap().wrap();
16128     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16129     
16130     
16131     if(typeof(this.tpl) == "string"){
16132         this.tpl = new Roo.Template(this.tpl);
16133     } else {
16134         // support xtype ctors..
16135         this.tpl = new Roo.factory(this.tpl, Roo);
16136     }
16137     
16138     
16139     this.tpl.compile();
16140     
16141     /** @private */
16142     this.addEvents({
16143         /**
16144          * @event beforeclick
16145          * Fires before a click is processed. Returns false to cancel the default action.
16146          * @param {Roo.View} this
16147          * @param {Number} index The index of the target node
16148          * @param {HTMLElement} node The target node
16149          * @param {Roo.EventObject} e The raw event object
16150          */
16151             "beforeclick" : true,
16152         /**
16153          * @event click
16154          * Fires when a template node is clicked.
16155          * @param {Roo.View} this
16156          * @param {Number} index The index of the target node
16157          * @param {HTMLElement} node The target node
16158          * @param {Roo.EventObject} e The raw event object
16159          */
16160             "click" : true,
16161         /**
16162          * @event dblclick
16163          * Fires when a template node is double clicked.
16164          * @param {Roo.View} this
16165          * @param {Number} index The index of the target node
16166          * @param {HTMLElement} node The target node
16167          * @param {Roo.EventObject} e The raw event object
16168          */
16169             "dblclick" : true,
16170         /**
16171          * @event contextmenu
16172          * Fires when a template node is right clicked.
16173          * @param {Roo.View} this
16174          * @param {Number} index The index of the target node
16175          * @param {HTMLElement} node The target node
16176          * @param {Roo.EventObject} e The raw event object
16177          */
16178             "contextmenu" : true,
16179         /**
16180          * @event selectionchange
16181          * Fires when the selected nodes change.
16182          * @param {Roo.View} this
16183          * @param {Array} selections Array of the selected nodes
16184          */
16185             "selectionchange" : true,
16186     
16187         /**
16188          * @event beforeselect
16189          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16190          * @param {Roo.View} this
16191          * @param {HTMLElement} node The node to be selected
16192          * @param {Array} selections Array of currently selected nodes
16193          */
16194             "beforeselect" : true,
16195         /**
16196          * @event preparedata
16197          * Fires on every row to render, to allow you to change the data.
16198          * @param {Roo.View} this
16199          * @param {Object} data to be rendered (change this)
16200          */
16201           "preparedata" : true
16202           
16203           
16204         });
16205
16206
16207
16208     this.el.on({
16209         "click": this.onClick,
16210         "dblclick": this.onDblClick,
16211         "contextmenu": this.onContextMenu,
16212         scope:this
16213     });
16214
16215     this.selections = [];
16216     this.nodes = [];
16217     this.cmp = new Roo.CompositeElementLite([]);
16218     if(this.store){
16219         this.store = Roo.factory(this.store, Roo.data);
16220         this.setStore(this.store, true);
16221     }
16222     
16223     if ( this.footer && this.footer.xtype) {
16224            
16225          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16226         
16227         this.footer.dataSource = this.store;
16228         this.footer.container = fctr;
16229         this.footer = Roo.factory(this.footer, Roo);
16230         fctr.insertFirst(this.el);
16231         
16232         // this is a bit insane - as the paging toolbar seems to detach the el..
16233 //        dom.parentNode.parentNode.parentNode
16234          // they get detached?
16235     }
16236     
16237     
16238     Roo.View.superclass.constructor.call(this);
16239     
16240     
16241 };
16242
16243 Roo.extend(Roo.View, Roo.util.Observable, {
16244     
16245      /**
16246      * @cfg {Roo.data.Store} store Data store to load data from.
16247      */
16248     store : false,
16249     
16250     /**
16251      * @cfg {String|Roo.Element} el The container element.
16252      */
16253     el : '',
16254     
16255     /**
16256      * @cfg {String|Roo.Template} tpl The template used by this View 
16257      */
16258     tpl : false,
16259     /**
16260      * @cfg {String} dataName the named area of the template to use as the data area
16261      *                          Works with domtemplates roo-name="name"
16262      */
16263     dataName: false,
16264     /**
16265      * @cfg {String} selectedClass The css class to add to selected nodes
16266      */
16267     selectedClass : "x-view-selected",
16268      /**
16269      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16270      */
16271     emptyText : "",
16272     
16273     /**
16274      * @cfg {String} text to display on mask (default Loading)
16275      */
16276     mask : false,
16277     /**
16278      * @cfg {Boolean} multiSelect Allow multiple selection
16279      */
16280     multiSelect : false,
16281     /**
16282      * @cfg {Boolean} singleSelect Allow single selection
16283      */
16284     singleSelect:  false,
16285     
16286     /**
16287      * @cfg {Boolean} toggleSelect - selecting 
16288      */
16289     toggleSelect : false,
16290     
16291     /**
16292      * @cfg {Boolean} tickable - selecting 
16293      */
16294     tickable : false,
16295     
16296     /**
16297      * Returns the element this view is bound to.
16298      * @return {Roo.Element}
16299      */
16300     getEl : function(){
16301         return this.wrapEl;
16302     },
16303     
16304     
16305
16306     /**
16307      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16308      */
16309     refresh : function(){
16310         //Roo.log('refresh');
16311         var t = this.tpl;
16312         
16313         // if we are using something like 'domtemplate', then
16314         // the what gets used is:
16315         // t.applySubtemplate(NAME, data, wrapping data..)
16316         // the outer template then get' applied with
16317         //     the store 'extra data'
16318         // and the body get's added to the
16319         //      roo-name="data" node?
16320         //      <span class='roo-tpl-{name}'></span> ?????
16321         
16322         
16323         
16324         this.clearSelections();
16325         this.el.update("");
16326         var html = [];
16327         var records = this.store.getRange();
16328         if(records.length < 1) {
16329             
16330             // is this valid??  = should it render a template??
16331             
16332             this.el.update(this.emptyText);
16333             return;
16334         }
16335         var el = this.el;
16336         if (this.dataName) {
16337             this.el.update(t.apply(this.store.meta)); //????
16338             el = this.el.child('.roo-tpl-' + this.dataName);
16339         }
16340         
16341         for(var i = 0, len = records.length; i < len; i++){
16342             var data = this.prepareData(records[i].data, i, records[i]);
16343             this.fireEvent("preparedata", this, data, i, records[i]);
16344             
16345             var d = Roo.apply({}, data);
16346             
16347             if(this.tickable){
16348                 Roo.apply(d, {'roo-id' : Roo.id()});
16349                 
16350                 var _this = this;
16351             
16352                 Roo.each(this.parent.item, function(item){
16353                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16354                         return;
16355                     }
16356                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16357                 });
16358             }
16359             
16360             html[html.length] = Roo.util.Format.trim(
16361                 this.dataName ?
16362                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16363                     t.apply(d)
16364             );
16365         }
16366         
16367         
16368         
16369         el.update(html.join(""));
16370         this.nodes = el.dom.childNodes;
16371         this.updateIndexes(0);
16372     },
16373     
16374
16375     /**
16376      * Function to override to reformat the data that is sent to
16377      * the template for each node.
16378      * DEPRICATED - use the preparedata event handler.
16379      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16380      * a JSON object for an UpdateManager bound view).
16381      */
16382     prepareData : function(data, index, record)
16383     {
16384         this.fireEvent("preparedata", this, data, index, record);
16385         return data;
16386     },
16387
16388     onUpdate : function(ds, record){
16389         // Roo.log('on update');   
16390         this.clearSelections();
16391         var index = this.store.indexOf(record);
16392         var n = this.nodes[index];
16393         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16394         n.parentNode.removeChild(n);
16395         this.updateIndexes(index, index);
16396     },
16397
16398     
16399     
16400 // --------- FIXME     
16401     onAdd : function(ds, records, index)
16402     {
16403         //Roo.log(['on Add', ds, records, index] );        
16404         this.clearSelections();
16405         if(this.nodes.length == 0){
16406             this.refresh();
16407             return;
16408         }
16409         var n = this.nodes[index];
16410         for(var i = 0, len = records.length; i < len; i++){
16411             var d = this.prepareData(records[i].data, i, records[i]);
16412             if(n){
16413                 this.tpl.insertBefore(n, d);
16414             }else{
16415                 
16416                 this.tpl.append(this.el, d);
16417             }
16418         }
16419         this.updateIndexes(index);
16420     },
16421
16422     onRemove : function(ds, record, index){
16423        // Roo.log('onRemove');
16424         this.clearSelections();
16425         var el = this.dataName  ?
16426             this.el.child('.roo-tpl-' + this.dataName) :
16427             this.el; 
16428         
16429         el.dom.removeChild(this.nodes[index]);
16430         this.updateIndexes(index);
16431     },
16432
16433     /**
16434      * Refresh an individual node.
16435      * @param {Number} index
16436      */
16437     refreshNode : function(index){
16438         this.onUpdate(this.store, this.store.getAt(index));
16439     },
16440
16441     updateIndexes : function(startIndex, endIndex){
16442         var ns = this.nodes;
16443         startIndex = startIndex || 0;
16444         endIndex = endIndex || ns.length - 1;
16445         for(var i = startIndex; i <= endIndex; i++){
16446             ns[i].nodeIndex = i;
16447         }
16448     },
16449
16450     /**
16451      * Changes the data store this view uses and refresh the view.
16452      * @param {Store} store
16453      */
16454     setStore : function(store, initial){
16455         if(!initial && this.store){
16456             this.store.un("datachanged", this.refresh);
16457             this.store.un("add", this.onAdd);
16458             this.store.un("remove", this.onRemove);
16459             this.store.un("update", this.onUpdate);
16460             this.store.un("clear", this.refresh);
16461             this.store.un("beforeload", this.onBeforeLoad);
16462             this.store.un("load", this.onLoad);
16463             this.store.un("loadexception", this.onLoad);
16464         }
16465         if(store){
16466           
16467             store.on("datachanged", this.refresh, this);
16468             store.on("add", this.onAdd, this);
16469             store.on("remove", this.onRemove, this);
16470             store.on("update", this.onUpdate, this);
16471             store.on("clear", this.refresh, this);
16472             store.on("beforeload", this.onBeforeLoad, this);
16473             store.on("load", this.onLoad, this);
16474             store.on("loadexception", this.onLoad, this);
16475         }
16476         
16477         if(store){
16478             this.refresh();
16479         }
16480     },
16481     /**
16482      * onbeforeLoad - masks the loading area.
16483      *
16484      */
16485     onBeforeLoad : function(store,opts)
16486     {
16487          //Roo.log('onBeforeLoad');   
16488         if (!opts.add) {
16489             this.el.update("");
16490         }
16491         this.el.mask(this.mask ? this.mask : "Loading" ); 
16492     },
16493     onLoad : function ()
16494     {
16495         this.el.unmask();
16496     },
16497     
16498
16499     /**
16500      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16501      * @param {HTMLElement} node
16502      * @return {HTMLElement} The template node
16503      */
16504     findItemFromChild : function(node){
16505         var el = this.dataName  ?
16506             this.el.child('.roo-tpl-' + this.dataName,true) :
16507             this.el.dom; 
16508         
16509         if(!node || node.parentNode == el){
16510                     return node;
16511             }
16512             var p = node.parentNode;
16513             while(p && p != el){
16514             if(p.parentNode == el){
16515                 return p;
16516             }
16517             p = p.parentNode;
16518         }
16519             return null;
16520     },
16521
16522     /** @ignore */
16523     onClick : function(e){
16524         var item = this.findItemFromChild(e.getTarget());
16525         if(item){
16526             var index = this.indexOf(item);
16527             if(this.onItemClick(item, index, e) !== false){
16528                 this.fireEvent("click", this, index, item, e);
16529             }
16530         }else{
16531             this.clearSelections();
16532         }
16533     },
16534
16535     /** @ignore */
16536     onContextMenu : function(e){
16537         var item = this.findItemFromChild(e.getTarget());
16538         if(item){
16539             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16540         }
16541     },
16542
16543     /** @ignore */
16544     onDblClick : function(e){
16545         var item = this.findItemFromChild(e.getTarget());
16546         if(item){
16547             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16548         }
16549     },
16550
16551     onItemClick : function(item, index, e)
16552     {
16553         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16554             return false;
16555         }
16556         if (this.toggleSelect) {
16557             var m = this.isSelected(item) ? 'unselect' : 'select';
16558             //Roo.log(m);
16559             var _t = this;
16560             _t[m](item, true, false);
16561             return true;
16562         }
16563         if(this.multiSelect || this.singleSelect){
16564             if(this.multiSelect && e.shiftKey && this.lastSelection){
16565                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16566             }else{
16567                 this.select(item, this.multiSelect && e.ctrlKey);
16568                 this.lastSelection = item;
16569             }
16570             
16571             if(!this.tickable){
16572                 e.preventDefault();
16573             }
16574             
16575         }
16576         return true;
16577     },
16578
16579     /**
16580      * Get the number of selected nodes.
16581      * @return {Number}
16582      */
16583     getSelectionCount : function(){
16584         return this.selections.length;
16585     },
16586
16587     /**
16588      * Get the currently selected nodes.
16589      * @return {Array} An array of HTMLElements
16590      */
16591     getSelectedNodes : function(){
16592         return this.selections;
16593     },
16594
16595     /**
16596      * Get the indexes of the selected nodes.
16597      * @return {Array}
16598      */
16599     getSelectedIndexes : function(){
16600         var indexes = [], s = this.selections;
16601         for(var i = 0, len = s.length; i < len; i++){
16602             indexes.push(s[i].nodeIndex);
16603         }
16604         return indexes;
16605     },
16606
16607     /**
16608      * Clear all selections
16609      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16610      */
16611     clearSelections : function(suppressEvent){
16612         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16613             this.cmp.elements = this.selections;
16614             this.cmp.removeClass(this.selectedClass);
16615             this.selections = [];
16616             if(!suppressEvent){
16617                 this.fireEvent("selectionchange", this, this.selections);
16618             }
16619         }
16620     },
16621
16622     /**
16623      * Returns true if the passed node is selected
16624      * @param {HTMLElement/Number} node The node or node index
16625      * @return {Boolean}
16626      */
16627     isSelected : function(node){
16628         var s = this.selections;
16629         if(s.length < 1){
16630             return false;
16631         }
16632         node = this.getNode(node);
16633         return s.indexOf(node) !== -1;
16634     },
16635
16636     /**
16637      * Selects nodes.
16638      * @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
16639      * @param {Boolean} keepExisting (optional) true to keep existing selections
16640      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16641      */
16642     select : function(nodeInfo, keepExisting, suppressEvent){
16643         if(nodeInfo instanceof Array){
16644             if(!keepExisting){
16645                 this.clearSelections(true);
16646             }
16647             for(var i = 0, len = nodeInfo.length; i < len; i++){
16648                 this.select(nodeInfo[i], true, true);
16649             }
16650             return;
16651         } 
16652         var node = this.getNode(nodeInfo);
16653         if(!node || this.isSelected(node)){
16654             return; // already selected.
16655         }
16656         if(!keepExisting){
16657             this.clearSelections(true);
16658         }
16659         
16660         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16661             Roo.fly(node).addClass(this.selectedClass);
16662             this.selections.push(node);
16663             if(!suppressEvent){
16664                 this.fireEvent("selectionchange", this, this.selections);
16665             }
16666         }
16667         
16668         
16669     },
16670       /**
16671      * Unselects nodes.
16672      * @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
16673      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16674      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16675      */
16676     unselect : function(nodeInfo, keepExisting, suppressEvent)
16677     {
16678         if(nodeInfo instanceof Array){
16679             Roo.each(this.selections, function(s) {
16680                 this.unselect(s, nodeInfo);
16681             }, this);
16682             return;
16683         }
16684         var node = this.getNode(nodeInfo);
16685         if(!node || !this.isSelected(node)){
16686             //Roo.log("not selected");
16687             return; // not selected.
16688         }
16689         // fireevent???
16690         var ns = [];
16691         Roo.each(this.selections, function(s) {
16692             if (s == node ) {
16693                 Roo.fly(node).removeClass(this.selectedClass);
16694
16695                 return;
16696             }
16697             ns.push(s);
16698         },this);
16699         
16700         this.selections= ns;
16701         this.fireEvent("selectionchange", this, this.selections);
16702     },
16703
16704     /**
16705      * Gets a template node.
16706      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16707      * @return {HTMLElement} The node or null if it wasn't found
16708      */
16709     getNode : function(nodeInfo){
16710         if(typeof nodeInfo == "string"){
16711             return document.getElementById(nodeInfo);
16712         }else if(typeof nodeInfo == "number"){
16713             return this.nodes[nodeInfo];
16714         }
16715         return nodeInfo;
16716     },
16717
16718     /**
16719      * Gets a range template nodes.
16720      * @param {Number} startIndex
16721      * @param {Number} endIndex
16722      * @return {Array} An array of nodes
16723      */
16724     getNodes : function(start, end){
16725         var ns = this.nodes;
16726         start = start || 0;
16727         end = typeof end == "undefined" ? ns.length - 1 : end;
16728         var nodes = [];
16729         if(start <= end){
16730             for(var i = start; i <= end; i++){
16731                 nodes.push(ns[i]);
16732             }
16733         } else{
16734             for(var i = start; i >= end; i--){
16735                 nodes.push(ns[i]);
16736             }
16737         }
16738         return nodes;
16739     },
16740
16741     /**
16742      * Finds the index of the passed node
16743      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16744      * @return {Number} The index of the node or -1
16745      */
16746     indexOf : function(node){
16747         node = this.getNode(node);
16748         if(typeof node.nodeIndex == "number"){
16749             return node.nodeIndex;
16750         }
16751         var ns = this.nodes;
16752         for(var i = 0, len = ns.length; i < len; i++){
16753             if(ns[i] == node){
16754                 return i;
16755             }
16756         }
16757         return -1;
16758     }
16759 });
16760 /*
16761  * - LGPL
16762  *
16763  * based on jquery fullcalendar
16764  * 
16765  */
16766
16767 Roo.bootstrap = Roo.bootstrap || {};
16768 /**
16769  * @class Roo.bootstrap.Calendar
16770  * @extends Roo.bootstrap.Component
16771  * Bootstrap Calendar class
16772  * @cfg {Boolean} loadMask (true|false) default false
16773  * @cfg {Object} header generate the user specific header of the calendar, default false
16774
16775  * @constructor
16776  * Create a new Container
16777  * @param {Object} config The config object
16778  */
16779
16780
16781
16782 Roo.bootstrap.Calendar = function(config){
16783     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16784      this.addEvents({
16785         /**
16786              * @event select
16787              * Fires when a date is selected
16788              * @param {DatePicker} this
16789              * @param {Date} date The selected date
16790              */
16791         'select': true,
16792         /**
16793              * @event monthchange
16794              * Fires when the displayed month changes 
16795              * @param {DatePicker} this
16796              * @param {Date} date The selected month
16797              */
16798         'monthchange': true,
16799         /**
16800              * @event evententer
16801              * Fires when mouse over an event
16802              * @param {Calendar} this
16803              * @param {event} Event
16804              */
16805         'evententer': true,
16806         /**
16807              * @event eventleave
16808              * Fires when the mouse leaves an
16809              * @param {Calendar} this
16810              * @param {event}
16811              */
16812         'eventleave': true,
16813         /**
16814              * @event eventclick
16815              * Fires when the mouse click an
16816              * @param {Calendar} this
16817              * @param {event}
16818              */
16819         'eventclick': true
16820         
16821     });
16822
16823 };
16824
16825 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16826     
16827      /**
16828      * @cfg {Number} startDay
16829      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16830      */
16831     startDay : 0,
16832     
16833     loadMask : false,
16834     
16835     header : false,
16836       
16837     getAutoCreate : function(){
16838         
16839         
16840         var fc_button = function(name, corner, style, content ) {
16841             return Roo.apply({},{
16842                 tag : 'span',
16843                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16844                          (corner.length ?
16845                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16846                             ''
16847                         ),
16848                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16849                 unselectable: 'on'
16850             });
16851         };
16852         
16853         var header = {};
16854         
16855         if(!this.header){
16856             header = {
16857                 tag : 'table',
16858                 cls : 'fc-header',
16859                 style : 'width:100%',
16860                 cn : [
16861                     {
16862                         tag: 'tr',
16863                         cn : [
16864                             {
16865                                 tag : 'td',
16866                                 cls : 'fc-header-left',
16867                                 cn : [
16868                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16869                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16870                                     { tag: 'span', cls: 'fc-header-space' },
16871                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16872
16873
16874                                 ]
16875                             },
16876
16877                             {
16878                                 tag : 'td',
16879                                 cls : 'fc-header-center',
16880                                 cn : [
16881                                     {
16882                                         tag: 'span',
16883                                         cls: 'fc-header-title',
16884                                         cn : {
16885                                             tag: 'H2',
16886                                             html : 'month / year'
16887                                         }
16888                                     }
16889
16890                                 ]
16891                             },
16892                             {
16893                                 tag : 'td',
16894                                 cls : 'fc-header-right',
16895                                 cn : [
16896                               /*      fc_button('month', 'left', '', 'month' ),
16897                                     fc_button('week', '', '', 'week' ),
16898                                     fc_button('day', 'right', '', 'day' )
16899                                 */    
16900
16901                                 ]
16902                             }
16903
16904                         ]
16905                     }
16906                 ]
16907             };
16908         }
16909         
16910         header = this.header;
16911         
16912        
16913         var cal_heads = function() {
16914             var ret = [];
16915             // fixme - handle this.
16916             
16917             for (var i =0; i < Date.dayNames.length; i++) {
16918                 var d = Date.dayNames[i];
16919                 ret.push({
16920                     tag: 'th',
16921                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16922                     html : d.substring(0,3)
16923                 });
16924                 
16925             }
16926             ret[0].cls += ' fc-first';
16927             ret[6].cls += ' fc-last';
16928             return ret;
16929         };
16930         var cal_cell = function(n) {
16931             return  {
16932                 tag: 'td',
16933                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16934                 cn : [
16935                     {
16936                         cn : [
16937                             {
16938                                 cls: 'fc-day-number',
16939                                 html: 'D'
16940                             },
16941                             {
16942                                 cls: 'fc-day-content',
16943                              
16944                                 cn : [
16945                                      {
16946                                         style: 'position: relative;' // height: 17px;
16947                                     }
16948                                 ]
16949                             }
16950                             
16951                             
16952                         ]
16953                     }
16954                 ]
16955                 
16956             }
16957         };
16958         var cal_rows = function() {
16959             
16960             var ret = [];
16961             for (var r = 0; r < 6; r++) {
16962                 var row= {
16963                     tag : 'tr',
16964                     cls : 'fc-week',
16965                     cn : []
16966                 };
16967                 
16968                 for (var i =0; i < Date.dayNames.length; i++) {
16969                     var d = Date.dayNames[i];
16970                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16971
16972                 }
16973                 row.cn[0].cls+=' fc-first';
16974                 row.cn[0].cn[0].style = 'min-height:90px';
16975                 row.cn[6].cls+=' fc-last';
16976                 ret.push(row);
16977                 
16978             }
16979             ret[0].cls += ' fc-first';
16980             ret[4].cls += ' fc-prev-last';
16981             ret[5].cls += ' fc-last';
16982             return ret;
16983             
16984         };
16985         
16986         var cal_table = {
16987             tag: 'table',
16988             cls: 'fc-border-separate',
16989             style : 'width:100%',
16990             cellspacing  : 0,
16991             cn : [
16992                 { 
16993                     tag: 'thead',
16994                     cn : [
16995                         { 
16996                             tag: 'tr',
16997                             cls : 'fc-first fc-last',
16998                             cn : cal_heads()
16999                         }
17000                     ]
17001                 },
17002                 { 
17003                     tag: 'tbody',
17004                     cn : cal_rows()
17005                 }
17006                   
17007             ]
17008         };
17009          
17010          var cfg = {
17011             cls : 'fc fc-ltr',
17012             cn : [
17013                 header,
17014                 {
17015                     cls : 'fc-content',
17016                     style : "position: relative;",
17017                     cn : [
17018                         {
17019                             cls : 'fc-view fc-view-month fc-grid',
17020                             style : 'position: relative',
17021                             unselectable : 'on',
17022                             cn : [
17023                                 {
17024                                     cls : 'fc-event-container',
17025                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17026                                 },
17027                                 cal_table
17028                             ]
17029                         }
17030                     ]
17031     
17032                 }
17033            ] 
17034             
17035         };
17036         
17037          
17038         
17039         return cfg;
17040     },
17041     
17042     
17043     initEvents : function()
17044     {
17045         if(!this.store){
17046             throw "can not find store for calendar";
17047         }
17048         
17049         var mark = {
17050             tag: "div",
17051             cls:"x-dlg-mask",
17052             style: "text-align:center",
17053             cn: [
17054                 {
17055                     tag: "div",
17056                     style: "background-color:white;width:50%;margin:250 auto",
17057                     cn: [
17058                         {
17059                             tag: "img",
17060                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17061                         },
17062                         {
17063                             tag: "span",
17064                             html: "Loading"
17065                         }
17066                         
17067                     ]
17068                 }
17069             ]
17070         };
17071         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17072         
17073         var size = this.el.select('.fc-content', true).first().getSize();
17074         this.maskEl.setSize(size.width, size.height);
17075         this.maskEl.enableDisplayMode("block");
17076         if(!this.loadMask){
17077             this.maskEl.hide();
17078         }
17079         
17080         this.store = Roo.factory(this.store, Roo.data);
17081         this.store.on('load', this.onLoad, this);
17082         this.store.on('beforeload', this.onBeforeLoad, this);
17083         
17084         this.resize();
17085         
17086         this.cells = this.el.select('.fc-day',true);
17087         //Roo.log(this.cells);
17088         this.textNodes = this.el.query('.fc-day-number');
17089         this.cells.addClassOnOver('fc-state-hover');
17090         
17091         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17092         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17093         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17094         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17095         
17096         this.on('monthchange', this.onMonthChange, this);
17097         
17098         this.update(new Date().clearTime());
17099     },
17100     
17101     resize : function() {
17102         var sz  = this.el.getSize();
17103         
17104         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17105         this.el.select('.fc-day-content div',true).setHeight(34);
17106     },
17107     
17108     
17109     // private
17110     showPrevMonth : function(e){
17111         this.update(this.activeDate.add("mo", -1));
17112     },
17113     showToday : function(e){
17114         this.update(new Date().clearTime());
17115     },
17116     // private
17117     showNextMonth : function(e){
17118         this.update(this.activeDate.add("mo", 1));
17119     },
17120
17121     // private
17122     showPrevYear : function(){
17123         this.update(this.activeDate.add("y", -1));
17124     },
17125
17126     // private
17127     showNextYear : function(){
17128         this.update(this.activeDate.add("y", 1));
17129     },
17130
17131     
17132    // private
17133     update : function(date)
17134     {
17135         var vd = this.activeDate;
17136         this.activeDate = date;
17137 //        if(vd && this.el){
17138 //            var t = date.getTime();
17139 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17140 //                Roo.log('using add remove');
17141 //                
17142 //                this.fireEvent('monthchange', this, date);
17143 //                
17144 //                this.cells.removeClass("fc-state-highlight");
17145 //                this.cells.each(function(c){
17146 //                   if(c.dateValue == t){
17147 //                       c.addClass("fc-state-highlight");
17148 //                       setTimeout(function(){
17149 //                            try{c.dom.firstChild.focus();}catch(e){}
17150 //                       }, 50);
17151 //                       return false;
17152 //                   }
17153 //                   return true;
17154 //                });
17155 //                return;
17156 //            }
17157 //        }
17158         
17159         var days = date.getDaysInMonth();
17160         
17161         var firstOfMonth = date.getFirstDateOfMonth();
17162         var startingPos = firstOfMonth.getDay()-this.startDay;
17163         
17164         if(startingPos < this.startDay){
17165             startingPos += 7;
17166         }
17167         
17168         var pm = date.add(Date.MONTH, -1);
17169         var prevStart = pm.getDaysInMonth()-startingPos;
17170 //        
17171         this.cells = this.el.select('.fc-day',true);
17172         this.textNodes = this.el.query('.fc-day-number');
17173         this.cells.addClassOnOver('fc-state-hover');
17174         
17175         var cells = this.cells.elements;
17176         var textEls = this.textNodes;
17177         
17178         Roo.each(cells, function(cell){
17179             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17180         });
17181         
17182         days += startingPos;
17183
17184         // convert everything to numbers so it's fast
17185         var day = 86400000;
17186         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17187         //Roo.log(d);
17188         //Roo.log(pm);
17189         //Roo.log(prevStart);
17190         
17191         var today = new Date().clearTime().getTime();
17192         var sel = date.clearTime().getTime();
17193         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17194         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17195         var ddMatch = this.disabledDatesRE;
17196         var ddText = this.disabledDatesText;
17197         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17198         var ddaysText = this.disabledDaysText;
17199         var format = this.format;
17200         
17201         var setCellClass = function(cal, cell){
17202             cell.row = 0;
17203             cell.events = [];
17204             cell.more = [];
17205             //Roo.log('set Cell Class');
17206             cell.title = "";
17207             var t = d.getTime();
17208             
17209             //Roo.log(d);
17210             
17211             cell.dateValue = t;
17212             if(t == today){
17213                 cell.className += " fc-today";
17214                 cell.className += " fc-state-highlight";
17215                 cell.title = cal.todayText;
17216             }
17217             if(t == sel){
17218                 // disable highlight in other month..
17219                 //cell.className += " fc-state-highlight";
17220                 
17221             }
17222             // disabling
17223             if(t < min) {
17224                 cell.className = " fc-state-disabled";
17225                 cell.title = cal.minText;
17226                 return;
17227             }
17228             if(t > max) {
17229                 cell.className = " fc-state-disabled";
17230                 cell.title = cal.maxText;
17231                 return;
17232             }
17233             if(ddays){
17234                 if(ddays.indexOf(d.getDay()) != -1){
17235                     cell.title = ddaysText;
17236                     cell.className = " fc-state-disabled";
17237                 }
17238             }
17239             if(ddMatch && format){
17240                 var fvalue = d.dateFormat(format);
17241                 if(ddMatch.test(fvalue)){
17242                     cell.title = ddText.replace("%0", fvalue);
17243                     cell.className = " fc-state-disabled";
17244                 }
17245             }
17246             
17247             if (!cell.initialClassName) {
17248                 cell.initialClassName = cell.dom.className;
17249             }
17250             
17251             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17252         };
17253
17254         var i = 0;
17255         
17256         for(; i < startingPos; i++) {
17257             textEls[i].innerHTML = (++prevStart);
17258             d.setDate(d.getDate()+1);
17259             
17260             cells[i].className = "fc-past fc-other-month";
17261             setCellClass(this, cells[i]);
17262         }
17263         
17264         var intDay = 0;
17265         
17266         for(; i < days; i++){
17267             intDay = i - startingPos + 1;
17268             textEls[i].innerHTML = (intDay);
17269             d.setDate(d.getDate()+1);
17270             
17271             cells[i].className = ''; // "x-date-active";
17272             setCellClass(this, cells[i]);
17273         }
17274         var extraDays = 0;
17275         
17276         for(; i < 42; i++) {
17277             textEls[i].innerHTML = (++extraDays);
17278             d.setDate(d.getDate()+1);
17279             
17280             cells[i].className = "fc-future fc-other-month";
17281             setCellClass(this, cells[i]);
17282         }
17283         
17284         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17285         
17286         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17287         
17288         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17289         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17290         
17291         if(totalRows != 6){
17292             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17293             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17294         }
17295         
17296         this.fireEvent('monthchange', this, date);
17297         
17298         
17299         /*
17300         if(!this.internalRender){
17301             var main = this.el.dom.firstChild;
17302             var w = main.offsetWidth;
17303             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17304             Roo.fly(main).setWidth(w);
17305             this.internalRender = true;
17306             // opera does not respect the auto grow header center column
17307             // then, after it gets a width opera refuses to recalculate
17308             // without a second pass
17309             if(Roo.isOpera && !this.secondPass){
17310                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17311                 this.secondPass = true;
17312                 this.update.defer(10, this, [date]);
17313             }
17314         }
17315         */
17316         
17317     },
17318     
17319     findCell : function(dt) {
17320         dt = dt.clearTime().getTime();
17321         var ret = false;
17322         this.cells.each(function(c){
17323             //Roo.log("check " +c.dateValue + '?=' + dt);
17324             if(c.dateValue == dt){
17325                 ret = c;
17326                 return false;
17327             }
17328             return true;
17329         });
17330         
17331         return ret;
17332     },
17333     
17334     findCells : function(ev) {
17335         var s = ev.start.clone().clearTime().getTime();
17336        // Roo.log(s);
17337         var e= ev.end.clone().clearTime().getTime();
17338        // Roo.log(e);
17339         var ret = [];
17340         this.cells.each(function(c){
17341              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17342             
17343             if(c.dateValue > e){
17344                 return ;
17345             }
17346             if(c.dateValue < s){
17347                 return ;
17348             }
17349             ret.push(c);
17350         });
17351         
17352         return ret;    
17353     },
17354     
17355 //    findBestRow: function(cells)
17356 //    {
17357 //        var ret = 0;
17358 //        
17359 //        for (var i =0 ; i < cells.length;i++) {
17360 //            ret  = Math.max(cells[i].rows || 0,ret);
17361 //        }
17362 //        return ret;
17363 //        
17364 //    },
17365     
17366     
17367     addItem : function(ev)
17368     {
17369         // look for vertical location slot in
17370         var cells = this.findCells(ev);
17371         
17372 //        ev.row = this.findBestRow(cells);
17373         
17374         // work out the location.
17375         
17376         var crow = false;
17377         var rows = [];
17378         for(var i =0; i < cells.length; i++) {
17379             
17380             cells[i].row = cells[0].row;
17381             
17382             if(i == 0){
17383                 cells[i].row = cells[i].row + 1;
17384             }
17385             
17386             if (!crow) {
17387                 crow = {
17388                     start : cells[i],
17389                     end :  cells[i]
17390                 };
17391                 continue;
17392             }
17393             if (crow.start.getY() == cells[i].getY()) {
17394                 // on same row.
17395                 crow.end = cells[i];
17396                 continue;
17397             }
17398             // different row.
17399             rows.push(crow);
17400             crow = {
17401                 start: cells[i],
17402                 end : cells[i]
17403             };
17404             
17405         }
17406         
17407         rows.push(crow);
17408         ev.els = [];
17409         ev.rows = rows;
17410         ev.cells = cells;
17411         
17412         cells[0].events.push(ev);
17413         
17414         this.calevents.push(ev);
17415     },
17416     
17417     clearEvents: function() {
17418         
17419         if(!this.calevents){
17420             return;
17421         }
17422         
17423         Roo.each(this.cells.elements, function(c){
17424             c.row = 0;
17425             c.events = [];
17426             c.more = [];
17427         });
17428         
17429         Roo.each(this.calevents, function(e) {
17430             Roo.each(e.els, function(el) {
17431                 el.un('mouseenter' ,this.onEventEnter, this);
17432                 el.un('mouseleave' ,this.onEventLeave, this);
17433                 el.remove();
17434             },this);
17435         },this);
17436         
17437         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17438             e.remove();
17439         });
17440         
17441     },
17442     
17443     renderEvents: function()
17444     {   
17445         var _this = this;
17446         
17447         this.cells.each(function(c) {
17448             
17449             if(c.row < 5){
17450                 return;
17451             }
17452             
17453             var ev = c.events;
17454             
17455             var r = 4;
17456             if(c.row != c.events.length){
17457                 r = 4 - (4 - (c.row - c.events.length));
17458             }
17459             
17460             c.events = ev.slice(0, r);
17461             c.more = ev.slice(r);
17462             
17463             if(c.more.length && c.more.length == 1){
17464                 c.events.push(c.more.pop());
17465             }
17466             
17467             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17468             
17469         });
17470             
17471         this.cells.each(function(c) {
17472             
17473             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17474             
17475             
17476             for (var e = 0; e < c.events.length; e++){
17477                 var ev = c.events[e];
17478                 var rows = ev.rows;
17479                 
17480                 for(var i = 0; i < rows.length; i++) {
17481                 
17482                     // how many rows should it span..
17483
17484                     var  cfg = {
17485                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17486                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17487
17488                         unselectable : "on",
17489                         cn : [
17490                             {
17491                                 cls: 'fc-event-inner',
17492                                 cn : [
17493     //                                {
17494     //                                  tag:'span',
17495     //                                  cls: 'fc-event-time',
17496     //                                  html : cells.length > 1 ? '' : ev.time
17497     //                                },
17498                                     {
17499                                       tag:'span',
17500                                       cls: 'fc-event-title',
17501                                       html : String.format('{0}', ev.title)
17502                                     }
17503
17504
17505                                 ]
17506                             },
17507                             {
17508                                 cls: 'ui-resizable-handle ui-resizable-e',
17509                                 html : '&nbsp;&nbsp;&nbsp'
17510                             }
17511
17512                         ]
17513                     };
17514
17515                     if (i == 0) {
17516                         cfg.cls += ' fc-event-start';
17517                     }
17518                     if ((i+1) == rows.length) {
17519                         cfg.cls += ' fc-event-end';
17520                     }
17521
17522                     var ctr = _this.el.select('.fc-event-container',true).first();
17523                     var cg = ctr.createChild(cfg);
17524
17525                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17526                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17527
17528                     var r = (c.more.length) ? 1 : 0;
17529                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17530                     cg.setWidth(ebox.right - sbox.x -2);
17531
17532                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17533                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17534                     cg.on('click', _this.onEventClick, _this, ev);
17535
17536                     ev.els.push(cg);
17537                     
17538                 }
17539                 
17540             }
17541             
17542             
17543             if(c.more.length){
17544                 var  cfg = {
17545                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17546                     style : 'position: absolute',
17547                     unselectable : "on",
17548                     cn : [
17549                         {
17550                             cls: 'fc-event-inner',
17551                             cn : [
17552                                 {
17553                                   tag:'span',
17554                                   cls: 'fc-event-title',
17555                                   html : 'More'
17556                                 }
17557
17558
17559                             ]
17560                         },
17561                         {
17562                             cls: 'ui-resizable-handle ui-resizable-e',
17563                             html : '&nbsp;&nbsp;&nbsp'
17564                         }
17565
17566                     ]
17567                 };
17568
17569                 var ctr = _this.el.select('.fc-event-container',true).first();
17570                 var cg = ctr.createChild(cfg);
17571
17572                 var sbox = c.select('.fc-day-content',true).first().getBox();
17573                 var ebox = c.select('.fc-day-content',true).first().getBox();
17574                 //Roo.log(cg);
17575                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17576                 cg.setWidth(ebox.right - sbox.x -2);
17577
17578                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17579                 
17580             }
17581             
17582         });
17583         
17584         
17585         
17586     },
17587     
17588     onEventEnter: function (e, el,event,d) {
17589         this.fireEvent('evententer', this, el, event);
17590     },
17591     
17592     onEventLeave: function (e, el,event,d) {
17593         this.fireEvent('eventleave', this, el, event);
17594     },
17595     
17596     onEventClick: function (e, el,event,d) {
17597         this.fireEvent('eventclick', this, el, event);
17598     },
17599     
17600     onMonthChange: function () {
17601         this.store.load();
17602     },
17603     
17604     onMoreEventClick: function(e, el, more)
17605     {
17606         var _this = this;
17607         
17608         this.calpopover.placement = 'right';
17609         this.calpopover.setTitle('More');
17610         
17611         this.calpopover.setContent('');
17612         
17613         var ctr = this.calpopover.el.select('.popover-content', true).first();
17614         
17615         Roo.each(more, function(m){
17616             var cfg = {
17617                 cls : 'fc-event-hori fc-event-draggable',
17618                 html : m.title
17619             };
17620             var cg = ctr.createChild(cfg);
17621             
17622             cg.on('click', _this.onEventClick, _this, m);
17623         });
17624         
17625         this.calpopover.show(el);
17626         
17627         
17628     },
17629     
17630     onLoad: function () 
17631     {   
17632         this.calevents = [];
17633         var cal = this;
17634         
17635         if(this.store.getCount() > 0){
17636             this.store.data.each(function(d){
17637                cal.addItem({
17638                     id : d.data.id,
17639                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17640                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17641                     time : d.data.start_time,
17642                     title : d.data.title,
17643                     description : d.data.description,
17644                     venue : d.data.venue
17645                 });
17646             });
17647         }
17648         
17649         this.renderEvents();
17650         
17651         if(this.calevents.length && this.loadMask){
17652             this.maskEl.hide();
17653         }
17654     },
17655     
17656     onBeforeLoad: function()
17657     {
17658         this.clearEvents();
17659         if(this.loadMask){
17660             this.maskEl.show();
17661         }
17662     }
17663 });
17664
17665  
17666  /*
17667  * - LGPL
17668  *
17669  * element
17670  * 
17671  */
17672
17673 /**
17674  * @class Roo.bootstrap.Popover
17675  * @extends Roo.bootstrap.Component
17676  * Bootstrap Popover class
17677  * @cfg {String} html contents of the popover   (or false to use children..)
17678  * @cfg {String} title of popover (or false to hide)
17679  * @cfg {String} placement how it is placed
17680  * @cfg {String} trigger click || hover (or false to trigger manually)
17681  * @cfg {String} over what (parent or false to trigger manually.)
17682  * @cfg {Number} delay - delay before showing
17683  
17684  * @constructor
17685  * Create a new Popover
17686  * @param {Object} config The config object
17687  */
17688
17689 Roo.bootstrap.Popover = function(config){
17690     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17691     
17692     this.addEvents({
17693         // raw events
17694          /**
17695          * @event show
17696          * After the popover show
17697          * 
17698          * @param {Roo.bootstrap.Popover} this
17699          */
17700         "show" : true,
17701         /**
17702          * @event hide
17703          * After the popover hide
17704          * 
17705          * @param {Roo.bootstrap.Popover} this
17706          */
17707         "hide" : true
17708     });
17709 };
17710
17711 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17712     
17713     title: 'Fill in a title',
17714     html: false,
17715     
17716     placement : 'right',
17717     trigger : 'hover', // hover
17718     
17719     delay : 0,
17720     
17721     over: 'parent',
17722     
17723     can_build_overlaid : false,
17724     
17725     getChildContainer : function()
17726     {
17727         return this.el.select('.popover-content',true).first();
17728     },
17729     
17730     getAutoCreate : function(){
17731          
17732         var cfg = {
17733            cls : 'popover roo-dynamic',
17734            style: 'display:block',
17735            cn : [
17736                 {
17737                     cls : 'arrow'
17738                 },
17739                 {
17740                     cls : 'popover-inner',
17741                     cn : [
17742                         {
17743                             tag: 'h3',
17744                             cls: 'popover-title popover-header',
17745                             html : this.title
17746                         },
17747                         {
17748                             cls : 'popover-content popover-body',
17749                             html : this.html
17750                         }
17751                     ]
17752                     
17753                 }
17754            ]
17755         };
17756         
17757         return cfg;
17758     },
17759     setTitle: function(str)
17760     {
17761         this.title = str;
17762         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17763     },
17764     setContent: function(str)
17765     {
17766         this.html = str;
17767         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17768     },
17769     // as it get's added to the bottom of the page.
17770     onRender : function(ct, position)
17771     {
17772         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17773         if(!this.el){
17774             var cfg = Roo.apply({},  this.getAutoCreate());
17775             cfg.id = Roo.id();
17776             
17777             if (this.cls) {
17778                 cfg.cls += ' ' + this.cls;
17779             }
17780             if (this.style) {
17781                 cfg.style = this.style;
17782             }
17783             //Roo.log("adding to ");
17784             this.el = Roo.get(document.body).createChild(cfg, position);
17785 //            Roo.log(this.el);
17786         }
17787         this.initEvents();
17788     },
17789     
17790     initEvents : function()
17791     {
17792         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17793         this.el.enableDisplayMode('block');
17794         this.el.hide();
17795         if (this.over === false) {
17796             return; 
17797         }
17798         if (this.triggers === false) {
17799             return;
17800         }
17801         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17802         var triggers = this.trigger ? this.trigger.split(' ') : [];
17803         Roo.each(triggers, function(trigger) {
17804         
17805             if (trigger == 'click') {
17806                 on_el.on('click', this.toggle, this);
17807             } else if (trigger != 'manual') {
17808                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17809                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17810       
17811                 on_el.on(eventIn  ,this.enter, this);
17812                 on_el.on(eventOut, this.leave, this);
17813             }
17814         }, this);
17815         
17816     },
17817     
17818     
17819     // private
17820     timeout : null,
17821     hoverState : null,
17822     
17823     toggle : function () {
17824         this.hoverState == 'in' ? this.leave() : this.enter();
17825     },
17826     
17827     enter : function () {
17828         
17829         clearTimeout(this.timeout);
17830     
17831         this.hoverState = 'in';
17832     
17833         if (!this.delay || !this.delay.show) {
17834             this.show();
17835             return;
17836         }
17837         var _t = this;
17838         this.timeout = setTimeout(function () {
17839             if (_t.hoverState == 'in') {
17840                 _t.show();
17841             }
17842         }, this.delay.show)
17843     },
17844     
17845     leave : function() {
17846         clearTimeout(this.timeout);
17847     
17848         this.hoverState = 'out';
17849     
17850         if (!this.delay || !this.delay.hide) {
17851             this.hide();
17852             return;
17853         }
17854         var _t = this;
17855         this.timeout = setTimeout(function () {
17856             if (_t.hoverState == 'out') {
17857                 _t.hide();
17858             }
17859         }, this.delay.hide)
17860     },
17861     
17862     show : function (on_el)
17863     {
17864         if (!on_el) {
17865             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17866         }
17867         
17868         // set content.
17869         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17870         if (this.html !== false) {
17871             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17872         }
17873         this.el.removeClass([
17874             'fade','top','bottom', 'left', 'right','in',
17875             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17876         ]);
17877         if (!this.title.length) {
17878             this.el.select('.popover-title',true).hide();
17879         }
17880         
17881         var placement = typeof this.placement == 'function' ?
17882             this.placement.call(this, this.el, on_el) :
17883             this.placement;
17884             
17885         var autoToken = /\s?auto?\s?/i;
17886         var autoPlace = autoToken.test(placement);
17887         if (autoPlace) {
17888             placement = placement.replace(autoToken, '') || 'top';
17889         }
17890         
17891         //this.el.detach()
17892         //this.el.setXY([0,0]);
17893         this.el.show();
17894         this.el.dom.style.display='block';
17895         this.el.addClass(placement);
17896         
17897         //this.el.appendTo(on_el);
17898         
17899         var p = this.getPosition();
17900         var box = this.el.getBox();
17901         
17902         if (autoPlace) {
17903             // fixme..
17904         }
17905         var align = Roo.bootstrap.Popover.alignment[placement];
17906         
17907 //        Roo.log(align);
17908         this.el.alignTo(on_el, align[0],align[1]);
17909         //var arrow = this.el.select('.arrow',true).first();
17910         //arrow.set(align[2], 
17911         
17912         this.el.addClass('in');
17913         
17914         
17915         if (this.el.hasClass('fade')) {
17916             // fade it?
17917         }
17918         
17919         this.hoverState = 'in';
17920         
17921         this.fireEvent('show', this);
17922         
17923     },
17924     hide : function()
17925     {
17926         this.el.setXY([0,0]);
17927         this.el.removeClass('in');
17928         this.el.hide();
17929         this.hoverState = null;
17930         
17931         this.fireEvent('hide', this);
17932     }
17933     
17934 });
17935
17936 Roo.bootstrap.Popover.alignment = {
17937     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17938     'right' : ['l-r', [10,0], 'left bs-popover-left'],
17939     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17940     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17941 };
17942
17943  /*
17944  * - LGPL
17945  *
17946  * Progress
17947  * 
17948  */
17949
17950 /**
17951  * @class Roo.bootstrap.Progress
17952  * @extends Roo.bootstrap.Component
17953  * Bootstrap Progress class
17954  * @cfg {Boolean} striped striped of the progress bar
17955  * @cfg {Boolean} active animated of the progress bar
17956  * 
17957  * 
17958  * @constructor
17959  * Create a new Progress
17960  * @param {Object} config The config object
17961  */
17962
17963 Roo.bootstrap.Progress = function(config){
17964     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17965 };
17966
17967 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17968     
17969     striped : false,
17970     active: false,
17971     
17972     getAutoCreate : function(){
17973         var cfg = {
17974             tag: 'div',
17975             cls: 'progress'
17976         };
17977         
17978         
17979         if(this.striped){
17980             cfg.cls += ' progress-striped';
17981         }
17982       
17983         if(this.active){
17984             cfg.cls += ' active';
17985         }
17986         
17987         
17988         return cfg;
17989     }
17990    
17991 });
17992
17993  
17994
17995  /*
17996  * - LGPL
17997  *
17998  * ProgressBar
17999  * 
18000  */
18001
18002 /**
18003  * @class Roo.bootstrap.ProgressBar
18004  * @extends Roo.bootstrap.Component
18005  * Bootstrap ProgressBar class
18006  * @cfg {Number} aria_valuenow aria-value now
18007  * @cfg {Number} aria_valuemin aria-value min
18008  * @cfg {Number} aria_valuemax aria-value max
18009  * @cfg {String} label label for the progress bar
18010  * @cfg {String} panel (success | info | warning | danger )
18011  * @cfg {String} role role of the progress bar
18012  * @cfg {String} sr_only text
18013  * 
18014  * 
18015  * @constructor
18016  * Create a new ProgressBar
18017  * @param {Object} config The config object
18018  */
18019
18020 Roo.bootstrap.ProgressBar = function(config){
18021     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18022 };
18023
18024 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18025     
18026     aria_valuenow : 0,
18027     aria_valuemin : 0,
18028     aria_valuemax : 100,
18029     label : false,
18030     panel : false,
18031     role : false,
18032     sr_only: false,
18033     
18034     getAutoCreate : function()
18035     {
18036         
18037         var cfg = {
18038             tag: 'div',
18039             cls: 'progress-bar',
18040             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18041         };
18042         
18043         if(this.sr_only){
18044             cfg.cn = {
18045                 tag: 'span',
18046                 cls: 'sr-only',
18047                 html: this.sr_only
18048             }
18049         }
18050         
18051         if(this.role){
18052             cfg.role = this.role;
18053         }
18054         
18055         if(this.aria_valuenow){
18056             cfg['aria-valuenow'] = this.aria_valuenow;
18057         }
18058         
18059         if(this.aria_valuemin){
18060             cfg['aria-valuemin'] = this.aria_valuemin;
18061         }
18062         
18063         if(this.aria_valuemax){
18064             cfg['aria-valuemax'] = this.aria_valuemax;
18065         }
18066         
18067         if(this.label && !this.sr_only){
18068             cfg.html = this.label;
18069         }
18070         
18071         if(this.panel){
18072             cfg.cls += ' progress-bar-' + this.panel;
18073         }
18074         
18075         return cfg;
18076     },
18077     
18078     update : function(aria_valuenow)
18079     {
18080         this.aria_valuenow = aria_valuenow;
18081         
18082         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18083     }
18084    
18085 });
18086
18087  
18088
18089  /*
18090  * - LGPL
18091  *
18092  * column
18093  * 
18094  */
18095
18096 /**
18097  * @class Roo.bootstrap.TabGroup
18098  * @extends Roo.bootstrap.Column
18099  * Bootstrap Column class
18100  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18101  * @cfg {Boolean} carousel true to make the group behave like a carousel
18102  * @cfg {Boolean} bullets show bullets for the panels
18103  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18104  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18105  * @cfg {Boolean} showarrow (true|false) show arrow default true
18106  * 
18107  * @constructor
18108  * Create a new TabGroup
18109  * @param {Object} config The config object
18110  */
18111
18112 Roo.bootstrap.TabGroup = function(config){
18113     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18114     if (!this.navId) {
18115         this.navId = Roo.id();
18116     }
18117     this.tabs = [];
18118     Roo.bootstrap.TabGroup.register(this);
18119     
18120 };
18121
18122 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18123     
18124     carousel : false,
18125     transition : false,
18126     bullets : 0,
18127     timer : 0,
18128     autoslide : false,
18129     slideFn : false,
18130     slideOnTouch : false,
18131     showarrow : true,
18132     
18133     getAutoCreate : function()
18134     {
18135         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18136         
18137         cfg.cls += ' tab-content';
18138         
18139         if (this.carousel) {
18140             cfg.cls += ' carousel slide';
18141             
18142             cfg.cn = [{
18143                cls : 'carousel-inner',
18144                cn : []
18145             }];
18146         
18147             if(this.bullets  && !Roo.isTouch){
18148                 
18149                 var bullets = {
18150                     cls : 'carousel-bullets',
18151                     cn : []
18152                 };
18153                
18154                 if(this.bullets_cls){
18155                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18156                 }
18157                 
18158                 bullets.cn.push({
18159                     cls : 'clear'
18160                 });
18161                 
18162                 cfg.cn[0].cn.push(bullets);
18163             }
18164             
18165             if(this.showarrow){
18166                 cfg.cn[0].cn.push({
18167                     tag : 'div',
18168                     class : 'carousel-arrow',
18169                     cn : [
18170                         {
18171                             tag : 'div',
18172                             class : 'carousel-prev',
18173                             cn : [
18174                                 {
18175                                     tag : 'i',
18176                                     class : 'fa fa-chevron-left'
18177                                 }
18178                             ]
18179                         },
18180                         {
18181                             tag : 'div',
18182                             class : 'carousel-next',
18183                             cn : [
18184                                 {
18185                                     tag : 'i',
18186                                     class : 'fa fa-chevron-right'
18187                                 }
18188                             ]
18189                         }
18190                     ]
18191                 });
18192             }
18193             
18194         }
18195         
18196         return cfg;
18197     },
18198     
18199     initEvents:  function()
18200     {
18201 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18202 //            this.el.on("touchstart", this.onTouchStart, this);
18203 //        }
18204         
18205         if(this.autoslide){
18206             var _this = this;
18207             
18208             this.slideFn = window.setInterval(function() {
18209                 _this.showPanelNext();
18210             }, this.timer);
18211         }
18212         
18213         if(this.showarrow){
18214             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18215             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18216         }
18217         
18218         
18219     },
18220     
18221 //    onTouchStart : function(e, el, o)
18222 //    {
18223 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18224 //            return;
18225 //        }
18226 //        
18227 //        this.showPanelNext();
18228 //    },
18229     
18230     
18231     getChildContainer : function()
18232     {
18233         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18234     },
18235     
18236     /**
18237     * register a Navigation item
18238     * @param {Roo.bootstrap.NavItem} the navitem to add
18239     */
18240     register : function(item)
18241     {
18242         this.tabs.push( item);
18243         item.navId = this.navId; // not really needed..
18244         this.addBullet();
18245     
18246     },
18247     
18248     getActivePanel : function()
18249     {
18250         var r = false;
18251         Roo.each(this.tabs, function(t) {
18252             if (t.active) {
18253                 r = t;
18254                 return false;
18255             }
18256             return null;
18257         });
18258         return r;
18259         
18260     },
18261     getPanelByName : function(n)
18262     {
18263         var r = false;
18264         Roo.each(this.tabs, function(t) {
18265             if (t.tabId == n) {
18266                 r = t;
18267                 return false;
18268             }
18269             return null;
18270         });
18271         return r;
18272     },
18273     indexOfPanel : function(p)
18274     {
18275         var r = false;
18276         Roo.each(this.tabs, function(t,i) {
18277             if (t.tabId == p.tabId) {
18278                 r = i;
18279                 return false;
18280             }
18281             return null;
18282         });
18283         return r;
18284     },
18285     /**
18286      * show a specific panel
18287      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18288      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18289      */
18290     showPanel : function (pan)
18291     {
18292         if(this.transition || typeof(pan) == 'undefined'){
18293             Roo.log("waiting for the transitionend");
18294             return;
18295         }
18296         
18297         if (typeof(pan) == 'number') {
18298             pan = this.tabs[pan];
18299         }
18300         
18301         if (typeof(pan) == 'string') {
18302             pan = this.getPanelByName(pan);
18303         }
18304         
18305         var cur = this.getActivePanel();
18306         
18307         if(!pan || !cur){
18308             Roo.log('pan or acitve pan is undefined');
18309             return false;
18310         }
18311         
18312         if (pan.tabId == this.getActivePanel().tabId) {
18313             return true;
18314         }
18315         
18316         if (false === cur.fireEvent('beforedeactivate')) {
18317             return false;
18318         }
18319         
18320         if(this.bullets > 0 && !Roo.isTouch){
18321             this.setActiveBullet(this.indexOfPanel(pan));
18322         }
18323         
18324         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18325             
18326             this.transition = true;
18327             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18328             var lr = dir == 'next' ? 'left' : 'right';
18329             pan.el.addClass(dir); // or prev
18330             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18331             cur.el.addClass(lr); // or right
18332             pan.el.addClass(lr);
18333             
18334             var _this = this;
18335             cur.el.on('transitionend', function() {
18336                 Roo.log("trans end?");
18337                 
18338                 pan.el.removeClass([lr,dir]);
18339                 pan.setActive(true);
18340                 
18341                 cur.el.removeClass([lr]);
18342                 cur.setActive(false);
18343                 
18344                 _this.transition = false;
18345                 
18346             }, this, { single:  true } );
18347             
18348             return true;
18349         }
18350         
18351         cur.setActive(false);
18352         pan.setActive(true);
18353         
18354         return true;
18355         
18356     },
18357     showPanelNext : function()
18358     {
18359         var i = this.indexOfPanel(this.getActivePanel());
18360         
18361         if (i >= this.tabs.length - 1 && !this.autoslide) {
18362             return;
18363         }
18364         
18365         if (i >= this.tabs.length - 1 && this.autoslide) {
18366             i = -1;
18367         }
18368         
18369         this.showPanel(this.tabs[i+1]);
18370     },
18371     
18372     showPanelPrev : function()
18373     {
18374         var i = this.indexOfPanel(this.getActivePanel());
18375         
18376         if (i  < 1 && !this.autoslide) {
18377             return;
18378         }
18379         
18380         if (i < 1 && this.autoslide) {
18381             i = this.tabs.length;
18382         }
18383         
18384         this.showPanel(this.tabs[i-1]);
18385     },
18386     
18387     
18388     addBullet: function()
18389     {
18390         if(!this.bullets || Roo.isTouch){
18391             return;
18392         }
18393         var ctr = this.el.select('.carousel-bullets',true).first();
18394         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18395         var bullet = ctr.createChild({
18396             cls : 'bullet bullet-' + i
18397         },ctr.dom.lastChild);
18398         
18399         
18400         var _this = this;
18401         
18402         bullet.on('click', (function(e, el, o, ii, t){
18403
18404             e.preventDefault();
18405
18406             this.showPanel(ii);
18407
18408             if(this.autoslide && this.slideFn){
18409                 clearInterval(this.slideFn);
18410                 this.slideFn = window.setInterval(function() {
18411                     _this.showPanelNext();
18412                 }, this.timer);
18413             }
18414
18415         }).createDelegate(this, [i, bullet], true));
18416                 
18417         
18418     },
18419      
18420     setActiveBullet : function(i)
18421     {
18422         if(Roo.isTouch){
18423             return;
18424         }
18425         
18426         Roo.each(this.el.select('.bullet', true).elements, function(el){
18427             el.removeClass('selected');
18428         });
18429
18430         var bullet = this.el.select('.bullet-' + i, true).first();
18431         
18432         if(!bullet){
18433             return;
18434         }
18435         
18436         bullet.addClass('selected');
18437     }
18438     
18439     
18440   
18441 });
18442
18443  
18444
18445  
18446  
18447 Roo.apply(Roo.bootstrap.TabGroup, {
18448     
18449     groups: {},
18450      /**
18451     * register a Navigation Group
18452     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18453     */
18454     register : function(navgrp)
18455     {
18456         this.groups[navgrp.navId] = navgrp;
18457         
18458     },
18459     /**
18460     * fetch a Navigation Group based on the navigation ID
18461     * if one does not exist , it will get created.
18462     * @param {string} the navgroup to add
18463     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18464     */
18465     get: function(navId) {
18466         if (typeof(this.groups[navId]) == 'undefined') {
18467             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18468         }
18469         return this.groups[navId] ;
18470     }
18471     
18472     
18473     
18474 });
18475
18476  /*
18477  * - LGPL
18478  *
18479  * TabPanel
18480  * 
18481  */
18482
18483 /**
18484  * @class Roo.bootstrap.TabPanel
18485  * @extends Roo.bootstrap.Component
18486  * Bootstrap TabPanel class
18487  * @cfg {Boolean} active panel active
18488  * @cfg {String} html panel content
18489  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18490  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18491  * @cfg {String} href click to link..
18492  * 
18493  * 
18494  * @constructor
18495  * Create a new TabPanel
18496  * @param {Object} config The config object
18497  */
18498
18499 Roo.bootstrap.TabPanel = function(config){
18500     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18501     this.addEvents({
18502         /**
18503              * @event changed
18504              * Fires when the active status changes
18505              * @param {Roo.bootstrap.TabPanel} this
18506              * @param {Boolean} state the new state
18507             
18508          */
18509         'changed': true,
18510         /**
18511              * @event beforedeactivate
18512              * Fires before a tab is de-activated - can be used to do validation on a form.
18513              * @param {Roo.bootstrap.TabPanel} this
18514              * @return {Boolean} false if there is an error
18515             
18516          */
18517         'beforedeactivate': true
18518      });
18519     
18520     this.tabId = this.tabId || Roo.id();
18521   
18522 };
18523
18524 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18525     
18526     active: false,
18527     html: false,
18528     tabId: false,
18529     navId : false,
18530     href : '',
18531     
18532     getAutoCreate : function(){
18533         var cfg = {
18534             tag: 'div',
18535             // item is needed for carousel - not sure if it has any effect otherwise
18536             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18537             html: this.html || ''
18538         };
18539         
18540         if(this.active){
18541             cfg.cls += ' active';
18542         }
18543         
18544         if(this.tabId){
18545             cfg.tabId = this.tabId;
18546         }
18547         
18548         
18549         return cfg;
18550     },
18551     
18552     initEvents:  function()
18553     {
18554         var p = this.parent();
18555         
18556         this.navId = this.navId || p.navId;
18557         
18558         if (typeof(this.navId) != 'undefined') {
18559             // not really needed.. but just in case.. parent should be a NavGroup.
18560             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18561             
18562             tg.register(this);
18563             
18564             var i = tg.tabs.length - 1;
18565             
18566             if(this.active && tg.bullets > 0 && i < tg.bullets){
18567                 tg.setActiveBullet(i);
18568             }
18569         }
18570         
18571         this.el.on('click', this.onClick, this);
18572         
18573         if(Roo.isTouch){
18574             this.el.on("touchstart", this.onTouchStart, this);
18575             this.el.on("touchmove", this.onTouchMove, this);
18576             this.el.on("touchend", this.onTouchEnd, this);
18577         }
18578         
18579     },
18580     
18581     onRender : function(ct, position)
18582     {
18583         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18584     },
18585     
18586     setActive : function(state)
18587     {
18588         Roo.log("panel - set active " + this.tabId + "=" + state);
18589         
18590         this.active = state;
18591         if (!state) {
18592             this.el.removeClass('active');
18593             
18594         } else  if (!this.el.hasClass('active')) {
18595             this.el.addClass('active');
18596         }
18597         
18598         this.fireEvent('changed', this, state);
18599     },
18600     
18601     onClick : function(e)
18602     {
18603         e.preventDefault();
18604         
18605         if(!this.href.length){
18606             return;
18607         }
18608         
18609         window.location.href = this.href;
18610     },
18611     
18612     startX : 0,
18613     startY : 0,
18614     endX : 0,
18615     endY : 0,
18616     swiping : false,
18617     
18618     onTouchStart : function(e)
18619     {
18620         this.swiping = false;
18621         
18622         this.startX = e.browserEvent.touches[0].clientX;
18623         this.startY = e.browserEvent.touches[0].clientY;
18624     },
18625     
18626     onTouchMove : function(e)
18627     {
18628         this.swiping = true;
18629         
18630         this.endX = e.browserEvent.touches[0].clientX;
18631         this.endY = e.browserEvent.touches[0].clientY;
18632     },
18633     
18634     onTouchEnd : function(e)
18635     {
18636         if(!this.swiping){
18637             this.onClick(e);
18638             return;
18639         }
18640         
18641         var tabGroup = this.parent();
18642         
18643         if(this.endX > this.startX){ // swiping right
18644             tabGroup.showPanelPrev();
18645             return;
18646         }
18647         
18648         if(this.startX > this.endX){ // swiping left
18649             tabGroup.showPanelNext();
18650             return;
18651         }
18652     }
18653     
18654     
18655 });
18656  
18657
18658  
18659
18660  /*
18661  * - LGPL
18662  *
18663  * DateField
18664  * 
18665  */
18666
18667 /**
18668  * @class Roo.bootstrap.DateField
18669  * @extends Roo.bootstrap.Input
18670  * Bootstrap DateField class
18671  * @cfg {Number} weekStart default 0
18672  * @cfg {String} viewMode default empty, (months|years)
18673  * @cfg {String} minViewMode default empty, (months|years)
18674  * @cfg {Number} startDate default -Infinity
18675  * @cfg {Number} endDate default Infinity
18676  * @cfg {Boolean} todayHighlight default false
18677  * @cfg {Boolean} todayBtn default false
18678  * @cfg {Boolean} calendarWeeks default false
18679  * @cfg {Object} daysOfWeekDisabled default empty
18680  * @cfg {Boolean} singleMode default false (true | false)
18681  * 
18682  * @cfg {Boolean} keyboardNavigation default true
18683  * @cfg {String} language default en
18684  * 
18685  * @constructor
18686  * Create a new DateField
18687  * @param {Object} config The config object
18688  */
18689
18690 Roo.bootstrap.DateField = function(config){
18691     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18692      this.addEvents({
18693             /**
18694              * @event show
18695              * Fires when this field show.
18696              * @param {Roo.bootstrap.DateField} this
18697              * @param {Mixed} date The date value
18698              */
18699             show : true,
18700             /**
18701              * @event show
18702              * Fires when this field hide.
18703              * @param {Roo.bootstrap.DateField} this
18704              * @param {Mixed} date The date value
18705              */
18706             hide : true,
18707             /**
18708              * @event select
18709              * Fires when select a date.
18710              * @param {Roo.bootstrap.DateField} this
18711              * @param {Mixed} date The date value
18712              */
18713             select : true,
18714             /**
18715              * @event beforeselect
18716              * Fires when before select a date.
18717              * @param {Roo.bootstrap.DateField} this
18718              * @param {Mixed} date The date value
18719              */
18720             beforeselect : true
18721         });
18722 };
18723
18724 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18725     
18726     /**
18727      * @cfg {String} format
18728      * The default date format string which can be overriden for localization support.  The format must be
18729      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18730      */
18731     format : "m/d/y",
18732     /**
18733      * @cfg {String} altFormats
18734      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18735      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18736      */
18737     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18738     
18739     weekStart : 0,
18740     
18741     viewMode : '',
18742     
18743     minViewMode : '',
18744     
18745     todayHighlight : false,
18746     
18747     todayBtn: false,
18748     
18749     language: 'en',
18750     
18751     keyboardNavigation: true,
18752     
18753     calendarWeeks: false,
18754     
18755     startDate: -Infinity,
18756     
18757     endDate: Infinity,
18758     
18759     daysOfWeekDisabled: [],
18760     
18761     _events: [],
18762     
18763     singleMode : false,
18764     
18765     UTCDate: function()
18766     {
18767         return new Date(Date.UTC.apply(Date, arguments));
18768     },
18769     
18770     UTCToday: function()
18771     {
18772         var today = new Date();
18773         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18774     },
18775     
18776     getDate: function() {
18777             var d = this.getUTCDate();
18778             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18779     },
18780     
18781     getUTCDate: function() {
18782             return this.date;
18783     },
18784     
18785     setDate: function(d) {
18786             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18787     },
18788     
18789     setUTCDate: function(d) {
18790             this.date = d;
18791             this.setValue(this.formatDate(this.date));
18792     },
18793         
18794     onRender: function(ct, position)
18795     {
18796         
18797         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18798         
18799         this.language = this.language || 'en';
18800         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18801         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18802         
18803         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18804         this.format = this.format || 'm/d/y';
18805         this.isInline = false;
18806         this.isInput = true;
18807         this.component = this.el.select('.add-on', true).first() || false;
18808         this.component = (this.component && this.component.length === 0) ? false : this.component;
18809         this.hasInput = this.component && this.inputEl().length;
18810         
18811         if (typeof(this.minViewMode === 'string')) {
18812             switch (this.minViewMode) {
18813                 case 'months':
18814                     this.minViewMode = 1;
18815                     break;
18816                 case 'years':
18817                     this.minViewMode = 2;
18818                     break;
18819                 default:
18820                     this.minViewMode = 0;
18821                     break;
18822             }
18823         }
18824         
18825         if (typeof(this.viewMode === 'string')) {
18826             switch (this.viewMode) {
18827                 case 'months':
18828                     this.viewMode = 1;
18829                     break;
18830                 case 'years':
18831                     this.viewMode = 2;
18832                     break;
18833                 default:
18834                     this.viewMode = 0;
18835                     break;
18836             }
18837         }
18838                 
18839         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18840         
18841 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18842         
18843         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18844         
18845         this.picker().on('mousedown', this.onMousedown, this);
18846         this.picker().on('click', this.onClick, this);
18847         
18848         this.picker().addClass('datepicker-dropdown');
18849         
18850         this.startViewMode = this.viewMode;
18851         
18852         if(this.singleMode){
18853             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18854                 v.setVisibilityMode(Roo.Element.DISPLAY);
18855                 v.hide();
18856             });
18857             
18858             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18859                 v.setStyle('width', '189px');
18860             });
18861         }
18862         
18863         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18864             if(!this.calendarWeeks){
18865                 v.remove();
18866                 return;
18867             }
18868             
18869             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18870             v.attr('colspan', function(i, val){
18871                 return parseInt(val) + 1;
18872             });
18873         });
18874                         
18875         
18876         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18877         
18878         this.setStartDate(this.startDate);
18879         this.setEndDate(this.endDate);
18880         
18881         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18882         
18883         this.fillDow();
18884         this.fillMonths();
18885         this.update();
18886         this.showMode();
18887         
18888         if(this.isInline) {
18889             this.showPopup();
18890         }
18891     },
18892     
18893     picker : function()
18894     {
18895         return this.pickerEl;
18896 //        return this.el.select('.datepicker', true).first();
18897     },
18898     
18899     fillDow: function()
18900     {
18901         var dowCnt = this.weekStart;
18902         
18903         var dow = {
18904             tag: 'tr',
18905             cn: [
18906                 
18907             ]
18908         };
18909         
18910         if(this.calendarWeeks){
18911             dow.cn.push({
18912                 tag: 'th',
18913                 cls: 'cw',
18914                 html: '&nbsp;'
18915             })
18916         }
18917         
18918         while (dowCnt < this.weekStart + 7) {
18919             dow.cn.push({
18920                 tag: 'th',
18921                 cls: 'dow',
18922                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18923             });
18924         }
18925         
18926         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18927     },
18928     
18929     fillMonths: function()
18930     {    
18931         var i = 0;
18932         var months = this.picker().select('>.datepicker-months td', true).first();
18933         
18934         months.dom.innerHTML = '';
18935         
18936         while (i < 12) {
18937             var month = {
18938                 tag: 'span',
18939                 cls: 'month',
18940                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18941             };
18942             
18943             months.createChild(month);
18944         }
18945         
18946     },
18947     
18948     update: function()
18949     {
18950         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;
18951         
18952         if (this.date < this.startDate) {
18953             this.viewDate = new Date(this.startDate);
18954         } else if (this.date > this.endDate) {
18955             this.viewDate = new Date(this.endDate);
18956         } else {
18957             this.viewDate = new Date(this.date);
18958         }
18959         
18960         this.fill();
18961     },
18962     
18963     fill: function() 
18964     {
18965         var d = new Date(this.viewDate),
18966                 year = d.getUTCFullYear(),
18967                 month = d.getUTCMonth(),
18968                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18969                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18970                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18971                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18972                 currentDate = this.date && this.date.valueOf(),
18973                 today = this.UTCToday();
18974         
18975         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18976         
18977 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18978         
18979 //        this.picker.select('>tfoot th.today').
18980 //                                              .text(dates[this.language].today)
18981 //                                              .toggle(this.todayBtn !== false);
18982     
18983         this.updateNavArrows();
18984         this.fillMonths();
18985                                                 
18986         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18987         
18988         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18989          
18990         prevMonth.setUTCDate(day);
18991         
18992         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18993         
18994         var nextMonth = new Date(prevMonth);
18995         
18996         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18997         
18998         nextMonth = nextMonth.valueOf();
18999         
19000         var fillMonths = false;
19001         
19002         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19003         
19004         while(prevMonth.valueOf() <= nextMonth) {
19005             var clsName = '';
19006             
19007             if (prevMonth.getUTCDay() === this.weekStart) {
19008                 if(fillMonths){
19009                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19010                 }
19011                     
19012                 fillMonths = {
19013                     tag: 'tr',
19014                     cn: []
19015                 };
19016                 
19017                 if(this.calendarWeeks){
19018                     // ISO 8601: First week contains first thursday.
19019                     // ISO also states week starts on Monday, but we can be more abstract here.
19020                     var
19021                     // Start of current week: based on weekstart/current date
19022                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19023                     // Thursday of this week
19024                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19025                     // First Thursday of year, year from thursday
19026                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19027                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19028                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19029                     
19030                     fillMonths.cn.push({
19031                         tag: 'td',
19032                         cls: 'cw',
19033                         html: calWeek
19034                     });
19035                 }
19036             }
19037             
19038             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19039                 clsName += ' old';
19040             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19041                 clsName += ' new';
19042             }
19043             if (this.todayHighlight &&
19044                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19045                 prevMonth.getUTCMonth() == today.getMonth() &&
19046                 prevMonth.getUTCDate() == today.getDate()) {
19047                 clsName += ' today';
19048             }
19049             
19050             if (currentDate && prevMonth.valueOf() === currentDate) {
19051                 clsName += ' active';
19052             }
19053             
19054             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19055                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19056                     clsName += ' disabled';
19057             }
19058             
19059             fillMonths.cn.push({
19060                 tag: 'td',
19061                 cls: 'day ' + clsName,
19062                 html: prevMonth.getDate()
19063             });
19064             
19065             prevMonth.setDate(prevMonth.getDate()+1);
19066         }
19067           
19068         var currentYear = this.date && this.date.getUTCFullYear();
19069         var currentMonth = this.date && this.date.getUTCMonth();
19070         
19071         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19072         
19073         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19074             v.removeClass('active');
19075             
19076             if(currentYear === year && k === currentMonth){
19077                 v.addClass('active');
19078             }
19079             
19080             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19081                 v.addClass('disabled');
19082             }
19083             
19084         });
19085         
19086         
19087         year = parseInt(year/10, 10) * 10;
19088         
19089         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19090         
19091         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19092         
19093         year -= 1;
19094         for (var i = -1; i < 11; i++) {
19095             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19096                 tag: 'span',
19097                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19098                 html: year
19099             });
19100             
19101             year += 1;
19102         }
19103     },
19104     
19105     showMode: function(dir) 
19106     {
19107         if (dir) {
19108             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19109         }
19110         
19111         Roo.each(this.picker().select('>div',true).elements, function(v){
19112             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19113             v.hide();
19114         });
19115         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19116     },
19117     
19118     place: function()
19119     {
19120         if(this.isInline) {
19121             return;
19122         }
19123         
19124         this.picker().removeClass(['bottom', 'top']);
19125         
19126         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19127             /*
19128              * place to the top of element!
19129              *
19130              */
19131             
19132             this.picker().addClass('top');
19133             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19134             
19135             return;
19136         }
19137         
19138         this.picker().addClass('bottom');
19139         
19140         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19141     },
19142     
19143     parseDate : function(value)
19144     {
19145         if(!value || value instanceof Date){
19146             return value;
19147         }
19148         var v = Date.parseDate(value, this.format);
19149         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19150             v = Date.parseDate(value, 'Y-m-d');
19151         }
19152         if(!v && this.altFormats){
19153             if(!this.altFormatsArray){
19154                 this.altFormatsArray = this.altFormats.split("|");
19155             }
19156             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19157                 v = Date.parseDate(value, this.altFormatsArray[i]);
19158             }
19159         }
19160         return v;
19161     },
19162     
19163     formatDate : function(date, fmt)
19164     {   
19165         return (!date || !(date instanceof Date)) ?
19166         date : date.dateFormat(fmt || this.format);
19167     },
19168     
19169     onFocus : function()
19170     {
19171         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19172         this.showPopup();
19173     },
19174     
19175     onBlur : function()
19176     {
19177         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19178         
19179         var d = this.inputEl().getValue();
19180         
19181         this.setValue(d);
19182                 
19183         this.hidePopup();
19184     },
19185     
19186     showPopup : function()
19187     {
19188         this.picker().show();
19189         this.update();
19190         this.place();
19191         
19192         this.fireEvent('showpopup', this, this.date);
19193     },
19194     
19195     hidePopup : function()
19196     {
19197         if(this.isInline) {
19198             return;
19199         }
19200         this.picker().hide();
19201         this.viewMode = this.startViewMode;
19202         this.showMode();
19203         
19204         this.fireEvent('hidepopup', this, this.date);
19205         
19206     },
19207     
19208     onMousedown: function(e)
19209     {
19210         e.stopPropagation();
19211         e.preventDefault();
19212     },
19213     
19214     keyup: function(e)
19215     {
19216         Roo.bootstrap.DateField.superclass.keyup.call(this);
19217         this.update();
19218     },
19219
19220     setValue: function(v)
19221     {
19222         if(this.fireEvent('beforeselect', this, v) !== false){
19223             var d = new Date(this.parseDate(v) ).clearTime();
19224         
19225             if(isNaN(d.getTime())){
19226                 this.date = this.viewDate = '';
19227                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19228                 return;
19229             }
19230
19231             v = this.formatDate(d);
19232
19233             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19234
19235             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19236
19237             this.update();
19238
19239             this.fireEvent('select', this, this.date);
19240         }
19241     },
19242     
19243     getValue: function()
19244     {
19245         return this.formatDate(this.date);
19246     },
19247     
19248     fireKey: function(e)
19249     {
19250         if (!this.picker().isVisible()){
19251             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19252                 this.showPopup();
19253             }
19254             return;
19255         }
19256         
19257         var dateChanged = false,
19258         dir, day, month,
19259         newDate, newViewDate;
19260         
19261         switch(e.keyCode){
19262             case 27: // escape
19263                 this.hidePopup();
19264                 e.preventDefault();
19265                 break;
19266             case 37: // left
19267             case 39: // right
19268                 if (!this.keyboardNavigation) {
19269                     break;
19270                 }
19271                 dir = e.keyCode == 37 ? -1 : 1;
19272                 
19273                 if (e.ctrlKey){
19274                     newDate = this.moveYear(this.date, dir);
19275                     newViewDate = this.moveYear(this.viewDate, dir);
19276                 } else if (e.shiftKey){
19277                     newDate = this.moveMonth(this.date, dir);
19278                     newViewDate = this.moveMonth(this.viewDate, dir);
19279                 } else {
19280                     newDate = new Date(this.date);
19281                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19282                     newViewDate = new Date(this.viewDate);
19283                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19284                 }
19285                 if (this.dateWithinRange(newDate)){
19286                     this.date = newDate;
19287                     this.viewDate = newViewDate;
19288                     this.setValue(this.formatDate(this.date));
19289 //                    this.update();
19290                     e.preventDefault();
19291                     dateChanged = true;
19292                 }
19293                 break;
19294             case 38: // up
19295             case 40: // down
19296                 if (!this.keyboardNavigation) {
19297                     break;
19298                 }
19299                 dir = e.keyCode == 38 ? -1 : 1;
19300                 if (e.ctrlKey){
19301                     newDate = this.moveYear(this.date, dir);
19302                     newViewDate = this.moveYear(this.viewDate, dir);
19303                 } else if (e.shiftKey){
19304                     newDate = this.moveMonth(this.date, dir);
19305                     newViewDate = this.moveMonth(this.viewDate, dir);
19306                 } else {
19307                     newDate = new Date(this.date);
19308                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19309                     newViewDate = new Date(this.viewDate);
19310                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19311                 }
19312                 if (this.dateWithinRange(newDate)){
19313                     this.date = newDate;
19314                     this.viewDate = newViewDate;
19315                     this.setValue(this.formatDate(this.date));
19316 //                    this.update();
19317                     e.preventDefault();
19318                     dateChanged = true;
19319                 }
19320                 break;
19321             case 13: // enter
19322                 this.setValue(this.formatDate(this.date));
19323                 this.hidePopup();
19324                 e.preventDefault();
19325                 break;
19326             case 9: // tab
19327                 this.setValue(this.formatDate(this.date));
19328                 this.hidePopup();
19329                 break;
19330             case 16: // shift
19331             case 17: // ctrl
19332             case 18: // alt
19333                 break;
19334             default :
19335                 this.hidePopup();
19336                 
19337         }
19338     },
19339     
19340     
19341     onClick: function(e) 
19342     {
19343         e.stopPropagation();
19344         e.preventDefault();
19345         
19346         var target = e.getTarget();
19347         
19348         if(target.nodeName.toLowerCase() === 'i'){
19349             target = Roo.get(target).dom.parentNode;
19350         }
19351         
19352         var nodeName = target.nodeName;
19353         var className = target.className;
19354         var html = target.innerHTML;
19355         //Roo.log(nodeName);
19356         
19357         switch(nodeName.toLowerCase()) {
19358             case 'th':
19359                 switch(className) {
19360                     case 'switch':
19361                         this.showMode(1);
19362                         break;
19363                     case 'prev':
19364                     case 'next':
19365                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19366                         switch(this.viewMode){
19367                                 case 0:
19368                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19369                                         break;
19370                                 case 1:
19371                                 case 2:
19372                                         this.viewDate = this.moveYear(this.viewDate, dir);
19373                                         break;
19374                         }
19375                         this.fill();
19376                         break;
19377                     case 'today':
19378                         var date = new Date();
19379                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19380 //                        this.fill()
19381                         this.setValue(this.formatDate(this.date));
19382                         
19383                         this.hidePopup();
19384                         break;
19385                 }
19386                 break;
19387             case 'span':
19388                 if (className.indexOf('disabled') < 0) {
19389                     this.viewDate.setUTCDate(1);
19390                     if (className.indexOf('month') > -1) {
19391                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19392                     } else {
19393                         var year = parseInt(html, 10) || 0;
19394                         this.viewDate.setUTCFullYear(year);
19395                         
19396                     }
19397                     
19398                     if(this.singleMode){
19399                         this.setValue(this.formatDate(this.viewDate));
19400                         this.hidePopup();
19401                         return;
19402                     }
19403                     
19404                     this.showMode(-1);
19405                     this.fill();
19406                 }
19407                 break;
19408                 
19409             case 'td':
19410                 //Roo.log(className);
19411                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19412                     var day = parseInt(html, 10) || 1;
19413                     var year = this.viewDate.getUTCFullYear(),
19414                         month = this.viewDate.getUTCMonth();
19415
19416                     if (className.indexOf('old') > -1) {
19417                         if(month === 0 ){
19418                             month = 11;
19419                             year -= 1;
19420                         }else{
19421                             month -= 1;
19422                         }
19423                     } else if (className.indexOf('new') > -1) {
19424                         if (month == 11) {
19425                             month = 0;
19426                             year += 1;
19427                         } else {
19428                             month += 1;
19429                         }
19430                     }
19431                     //Roo.log([year,month,day]);
19432                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19433                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19434 //                    this.fill();
19435                     //Roo.log(this.formatDate(this.date));
19436                     this.setValue(this.formatDate(this.date));
19437                     this.hidePopup();
19438                 }
19439                 break;
19440         }
19441     },
19442     
19443     setStartDate: function(startDate)
19444     {
19445         this.startDate = startDate || -Infinity;
19446         if (this.startDate !== -Infinity) {
19447             this.startDate = this.parseDate(this.startDate);
19448         }
19449         this.update();
19450         this.updateNavArrows();
19451     },
19452
19453     setEndDate: function(endDate)
19454     {
19455         this.endDate = endDate || Infinity;
19456         if (this.endDate !== Infinity) {
19457             this.endDate = this.parseDate(this.endDate);
19458         }
19459         this.update();
19460         this.updateNavArrows();
19461     },
19462     
19463     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19464     {
19465         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19466         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19467             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19468         }
19469         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19470             return parseInt(d, 10);
19471         });
19472         this.update();
19473         this.updateNavArrows();
19474     },
19475     
19476     updateNavArrows: function() 
19477     {
19478         if(this.singleMode){
19479             return;
19480         }
19481         
19482         var d = new Date(this.viewDate),
19483         year = d.getUTCFullYear(),
19484         month = d.getUTCMonth();
19485         
19486         Roo.each(this.picker().select('.prev', true).elements, function(v){
19487             v.show();
19488             switch (this.viewMode) {
19489                 case 0:
19490
19491                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19492                         v.hide();
19493                     }
19494                     break;
19495                 case 1:
19496                 case 2:
19497                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19498                         v.hide();
19499                     }
19500                     break;
19501             }
19502         });
19503         
19504         Roo.each(this.picker().select('.next', true).elements, function(v){
19505             v.show();
19506             switch (this.viewMode) {
19507                 case 0:
19508
19509                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19510                         v.hide();
19511                     }
19512                     break;
19513                 case 1:
19514                 case 2:
19515                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19516                         v.hide();
19517                     }
19518                     break;
19519             }
19520         })
19521     },
19522     
19523     moveMonth: function(date, dir)
19524     {
19525         if (!dir) {
19526             return date;
19527         }
19528         var new_date = new Date(date.valueOf()),
19529         day = new_date.getUTCDate(),
19530         month = new_date.getUTCMonth(),
19531         mag = Math.abs(dir),
19532         new_month, test;
19533         dir = dir > 0 ? 1 : -1;
19534         if (mag == 1){
19535             test = dir == -1
19536             // If going back one month, make sure month is not current month
19537             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19538             ? function(){
19539                 return new_date.getUTCMonth() == month;
19540             }
19541             // If going forward one month, make sure month is as expected
19542             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19543             : function(){
19544                 return new_date.getUTCMonth() != new_month;
19545             };
19546             new_month = month + dir;
19547             new_date.setUTCMonth(new_month);
19548             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19549             if (new_month < 0 || new_month > 11) {
19550                 new_month = (new_month + 12) % 12;
19551             }
19552         } else {
19553             // For magnitudes >1, move one month at a time...
19554             for (var i=0; i<mag; i++) {
19555                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19556                 new_date = this.moveMonth(new_date, dir);
19557             }
19558             // ...then reset the day, keeping it in the new month
19559             new_month = new_date.getUTCMonth();
19560             new_date.setUTCDate(day);
19561             test = function(){
19562                 return new_month != new_date.getUTCMonth();
19563             };
19564         }
19565         // Common date-resetting loop -- if date is beyond end of month, make it
19566         // end of month
19567         while (test()){
19568             new_date.setUTCDate(--day);
19569             new_date.setUTCMonth(new_month);
19570         }
19571         return new_date;
19572     },
19573
19574     moveYear: function(date, dir)
19575     {
19576         return this.moveMonth(date, dir*12);
19577     },
19578
19579     dateWithinRange: function(date)
19580     {
19581         return date >= this.startDate && date <= this.endDate;
19582     },
19583
19584     
19585     remove: function() 
19586     {
19587         this.picker().remove();
19588     },
19589     
19590     validateValue : function(value)
19591     {
19592         if(this.getVisibilityEl().hasClass('hidden')){
19593             return true;
19594         }
19595         
19596         if(value.length < 1)  {
19597             if(this.allowBlank){
19598                 return true;
19599             }
19600             return false;
19601         }
19602         
19603         if(value.length < this.minLength){
19604             return false;
19605         }
19606         if(value.length > this.maxLength){
19607             return false;
19608         }
19609         if(this.vtype){
19610             var vt = Roo.form.VTypes;
19611             if(!vt[this.vtype](value, this)){
19612                 return false;
19613             }
19614         }
19615         if(typeof this.validator == "function"){
19616             var msg = this.validator(value);
19617             if(msg !== true){
19618                 return false;
19619             }
19620         }
19621         
19622         if(this.regex && !this.regex.test(value)){
19623             return false;
19624         }
19625         
19626         if(typeof(this.parseDate(value)) == 'undefined'){
19627             return false;
19628         }
19629         
19630         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19631             return false;
19632         }      
19633         
19634         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19635             return false;
19636         } 
19637         
19638         
19639         return true;
19640     },
19641     
19642     reset : function()
19643     {
19644         this.date = this.viewDate = '';
19645         
19646         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19647     }
19648    
19649 });
19650
19651 Roo.apply(Roo.bootstrap.DateField,  {
19652     
19653     head : {
19654         tag: 'thead',
19655         cn: [
19656         {
19657             tag: 'tr',
19658             cn: [
19659             {
19660                 tag: 'th',
19661                 cls: 'prev',
19662                 html: '<i class="fa fa-arrow-left"/>'
19663             },
19664             {
19665                 tag: 'th',
19666                 cls: 'switch',
19667                 colspan: '5'
19668             },
19669             {
19670                 tag: 'th',
19671                 cls: 'next',
19672                 html: '<i class="fa fa-arrow-right"/>'
19673             }
19674
19675             ]
19676         }
19677         ]
19678     },
19679     
19680     content : {
19681         tag: 'tbody',
19682         cn: [
19683         {
19684             tag: 'tr',
19685             cn: [
19686             {
19687                 tag: 'td',
19688                 colspan: '7'
19689             }
19690             ]
19691         }
19692         ]
19693     },
19694     
19695     footer : {
19696         tag: 'tfoot',
19697         cn: [
19698         {
19699             tag: 'tr',
19700             cn: [
19701             {
19702                 tag: 'th',
19703                 colspan: '7',
19704                 cls: 'today'
19705             }
19706                     
19707             ]
19708         }
19709         ]
19710     },
19711     
19712     dates:{
19713         en: {
19714             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19715             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19716             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19717             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19718             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19719             today: "Today"
19720         }
19721     },
19722     
19723     modes: [
19724     {
19725         clsName: 'days',
19726         navFnc: 'Month',
19727         navStep: 1
19728     },
19729     {
19730         clsName: 'months',
19731         navFnc: 'FullYear',
19732         navStep: 1
19733     },
19734     {
19735         clsName: 'years',
19736         navFnc: 'FullYear',
19737         navStep: 10
19738     }]
19739 });
19740
19741 Roo.apply(Roo.bootstrap.DateField,  {
19742   
19743     template : {
19744         tag: 'div',
19745         cls: 'datepicker dropdown-menu roo-dynamic',
19746         cn: [
19747         {
19748             tag: 'div',
19749             cls: 'datepicker-days',
19750             cn: [
19751             {
19752                 tag: 'table',
19753                 cls: 'table-condensed',
19754                 cn:[
19755                 Roo.bootstrap.DateField.head,
19756                 {
19757                     tag: 'tbody'
19758                 },
19759                 Roo.bootstrap.DateField.footer
19760                 ]
19761             }
19762             ]
19763         },
19764         {
19765             tag: 'div',
19766             cls: 'datepicker-months',
19767             cn: [
19768             {
19769                 tag: 'table',
19770                 cls: 'table-condensed',
19771                 cn:[
19772                 Roo.bootstrap.DateField.head,
19773                 Roo.bootstrap.DateField.content,
19774                 Roo.bootstrap.DateField.footer
19775                 ]
19776             }
19777             ]
19778         },
19779         {
19780             tag: 'div',
19781             cls: 'datepicker-years',
19782             cn: [
19783             {
19784                 tag: 'table',
19785                 cls: 'table-condensed',
19786                 cn:[
19787                 Roo.bootstrap.DateField.head,
19788                 Roo.bootstrap.DateField.content,
19789                 Roo.bootstrap.DateField.footer
19790                 ]
19791             }
19792             ]
19793         }
19794         ]
19795     }
19796 });
19797
19798  
19799
19800  /*
19801  * - LGPL
19802  *
19803  * TimeField
19804  * 
19805  */
19806
19807 /**
19808  * @class Roo.bootstrap.TimeField
19809  * @extends Roo.bootstrap.Input
19810  * Bootstrap DateField class
19811  * 
19812  * 
19813  * @constructor
19814  * Create a new TimeField
19815  * @param {Object} config The config object
19816  */
19817
19818 Roo.bootstrap.TimeField = function(config){
19819     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19820     this.addEvents({
19821             /**
19822              * @event show
19823              * Fires when this field show.
19824              * @param {Roo.bootstrap.DateField} thisthis
19825              * @param {Mixed} date The date value
19826              */
19827             show : true,
19828             /**
19829              * @event show
19830              * Fires when this field hide.
19831              * @param {Roo.bootstrap.DateField} this
19832              * @param {Mixed} date The date value
19833              */
19834             hide : true,
19835             /**
19836              * @event select
19837              * Fires when select a date.
19838              * @param {Roo.bootstrap.DateField} this
19839              * @param {Mixed} date The date value
19840              */
19841             select : true
19842         });
19843 };
19844
19845 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19846     
19847     /**
19848      * @cfg {String} format
19849      * The default time format string which can be overriden for localization support.  The format must be
19850      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19851      */
19852     format : "H:i",
19853        
19854     onRender: function(ct, position)
19855     {
19856         
19857         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19858                 
19859         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19860         
19861         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19862         
19863         this.pop = this.picker().select('>.datepicker-time',true).first();
19864         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19865         
19866         this.picker().on('mousedown', this.onMousedown, this);
19867         this.picker().on('click', this.onClick, this);
19868         
19869         this.picker().addClass('datepicker-dropdown');
19870     
19871         this.fillTime();
19872         this.update();
19873             
19874         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19875         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19876         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19877         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19878         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19879         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19880
19881     },
19882     
19883     fireKey: function(e){
19884         if (!this.picker().isVisible()){
19885             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19886                 this.show();
19887             }
19888             return;
19889         }
19890
19891         e.preventDefault();
19892         
19893         switch(e.keyCode){
19894             case 27: // escape
19895                 this.hide();
19896                 break;
19897             case 37: // left
19898             case 39: // right
19899                 this.onTogglePeriod();
19900                 break;
19901             case 38: // up
19902                 this.onIncrementMinutes();
19903                 break;
19904             case 40: // down
19905                 this.onDecrementMinutes();
19906                 break;
19907             case 13: // enter
19908             case 9: // tab
19909                 this.setTime();
19910                 break;
19911         }
19912     },
19913     
19914     onClick: function(e) {
19915         e.stopPropagation();
19916         e.preventDefault();
19917     },
19918     
19919     picker : function()
19920     {
19921         return this.el.select('.datepicker', true).first();
19922     },
19923     
19924     fillTime: function()
19925     {    
19926         var time = this.pop.select('tbody', true).first();
19927         
19928         time.dom.innerHTML = '';
19929         
19930         time.createChild({
19931             tag: 'tr',
19932             cn: [
19933                 {
19934                     tag: 'td',
19935                     cn: [
19936                         {
19937                             tag: 'a',
19938                             href: '#',
19939                             cls: 'btn',
19940                             cn: [
19941                                 {
19942                                     tag: 'span',
19943                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19944                                 }
19945                             ]
19946                         } 
19947                     ]
19948                 },
19949                 {
19950                     tag: 'td',
19951                     cls: 'separator'
19952                 },
19953                 {
19954                     tag: 'td',
19955                     cn: [
19956                         {
19957                             tag: 'a',
19958                             href: '#',
19959                             cls: 'btn',
19960                             cn: [
19961                                 {
19962                                     tag: 'span',
19963                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19964                                 }
19965                             ]
19966                         }
19967                     ]
19968                 },
19969                 {
19970                     tag: 'td',
19971                     cls: 'separator'
19972                 }
19973             ]
19974         });
19975         
19976         time.createChild({
19977             tag: 'tr',
19978             cn: [
19979                 {
19980                     tag: 'td',
19981                     cn: [
19982                         {
19983                             tag: 'span',
19984                             cls: 'timepicker-hour',
19985                             html: '00'
19986                         }  
19987                     ]
19988                 },
19989                 {
19990                     tag: 'td',
19991                     cls: 'separator',
19992                     html: ':'
19993                 },
19994                 {
19995                     tag: 'td',
19996                     cn: [
19997                         {
19998                             tag: 'span',
19999                             cls: 'timepicker-minute',
20000                             html: '00'
20001                         }  
20002                     ]
20003                 },
20004                 {
20005                     tag: 'td',
20006                     cls: 'separator'
20007                 },
20008                 {
20009                     tag: 'td',
20010                     cn: [
20011                         {
20012                             tag: 'button',
20013                             type: 'button',
20014                             cls: 'btn btn-primary period',
20015                             html: 'AM'
20016                             
20017                         }
20018                     ]
20019                 }
20020             ]
20021         });
20022         
20023         time.createChild({
20024             tag: 'tr',
20025             cn: [
20026                 {
20027                     tag: 'td',
20028                     cn: [
20029                         {
20030                             tag: 'a',
20031                             href: '#',
20032                             cls: 'btn',
20033                             cn: [
20034                                 {
20035                                     tag: 'span',
20036                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20037                                 }
20038                             ]
20039                         }
20040                     ]
20041                 },
20042                 {
20043                     tag: 'td',
20044                     cls: 'separator'
20045                 },
20046                 {
20047                     tag: 'td',
20048                     cn: [
20049                         {
20050                             tag: 'a',
20051                             href: '#',
20052                             cls: 'btn',
20053                             cn: [
20054                                 {
20055                                     tag: 'span',
20056                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20057                                 }
20058                             ]
20059                         }
20060                     ]
20061                 },
20062                 {
20063                     tag: 'td',
20064                     cls: 'separator'
20065                 }
20066             ]
20067         });
20068         
20069     },
20070     
20071     update: function()
20072     {
20073         
20074         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20075         
20076         this.fill();
20077     },
20078     
20079     fill: function() 
20080     {
20081         var hours = this.time.getHours();
20082         var minutes = this.time.getMinutes();
20083         var period = 'AM';
20084         
20085         if(hours > 11){
20086             period = 'PM';
20087         }
20088         
20089         if(hours == 0){
20090             hours = 12;
20091         }
20092         
20093         
20094         if(hours > 12){
20095             hours = hours - 12;
20096         }
20097         
20098         if(hours < 10){
20099             hours = '0' + hours;
20100         }
20101         
20102         if(minutes < 10){
20103             minutes = '0' + minutes;
20104         }
20105         
20106         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20107         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20108         this.pop.select('button', true).first().dom.innerHTML = period;
20109         
20110     },
20111     
20112     place: function()
20113     {   
20114         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20115         
20116         var cls = ['bottom'];
20117         
20118         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20119             cls.pop();
20120             cls.push('top');
20121         }
20122         
20123         cls.push('right');
20124         
20125         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20126             cls.pop();
20127             cls.push('left');
20128         }
20129         
20130         this.picker().addClass(cls.join('-'));
20131         
20132         var _this = this;
20133         
20134         Roo.each(cls, function(c){
20135             if(c == 'bottom'){
20136                 _this.picker().setTop(_this.inputEl().getHeight());
20137                 return;
20138             }
20139             if(c == 'top'){
20140                 _this.picker().setTop(0 - _this.picker().getHeight());
20141                 return;
20142             }
20143             
20144             if(c == 'left'){
20145                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20146                 return;
20147             }
20148             if(c == 'right'){
20149                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20150                 return;
20151             }
20152         });
20153         
20154     },
20155   
20156     onFocus : function()
20157     {
20158         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20159         this.show();
20160     },
20161     
20162     onBlur : function()
20163     {
20164         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20165         this.hide();
20166     },
20167     
20168     show : function()
20169     {
20170         this.picker().show();
20171         this.pop.show();
20172         this.update();
20173         this.place();
20174         
20175         this.fireEvent('show', this, this.date);
20176     },
20177     
20178     hide : function()
20179     {
20180         this.picker().hide();
20181         this.pop.hide();
20182         
20183         this.fireEvent('hide', this, this.date);
20184     },
20185     
20186     setTime : function()
20187     {
20188         this.hide();
20189         this.setValue(this.time.format(this.format));
20190         
20191         this.fireEvent('select', this, this.date);
20192         
20193         
20194     },
20195     
20196     onMousedown: function(e){
20197         e.stopPropagation();
20198         e.preventDefault();
20199     },
20200     
20201     onIncrementHours: function()
20202     {
20203         Roo.log('onIncrementHours');
20204         this.time = this.time.add(Date.HOUR, 1);
20205         this.update();
20206         
20207     },
20208     
20209     onDecrementHours: function()
20210     {
20211         Roo.log('onDecrementHours');
20212         this.time = this.time.add(Date.HOUR, -1);
20213         this.update();
20214     },
20215     
20216     onIncrementMinutes: function()
20217     {
20218         Roo.log('onIncrementMinutes');
20219         this.time = this.time.add(Date.MINUTE, 1);
20220         this.update();
20221     },
20222     
20223     onDecrementMinutes: function()
20224     {
20225         Roo.log('onDecrementMinutes');
20226         this.time = this.time.add(Date.MINUTE, -1);
20227         this.update();
20228     },
20229     
20230     onTogglePeriod: function()
20231     {
20232         Roo.log('onTogglePeriod');
20233         this.time = this.time.add(Date.HOUR, 12);
20234         this.update();
20235     }
20236     
20237    
20238 });
20239
20240 Roo.apply(Roo.bootstrap.TimeField,  {
20241     
20242     content : {
20243         tag: 'tbody',
20244         cn: [
20245             {
20246                 tag: 'tr',
20247                 cn: [
20248                 {
20249                     tag: 'td',
20250                     colspan: '7'
20251                 }
20252                 ]
20253             }
20254         ]
20255     },
20256     
20257     footer : {
20258         tag: 'tfoot',
20259         cn: [
20260             {
20261                 tag: 'tr',
20262                 cn: [
20263                 {
20264                     tag: 'th',
20265                     colspan: '7',
20266                     cls: '',
20267                     cn: [
20268                         {
20269                             tag: 'button',
20270                             cls: 'btn btn-info ok',
20271                             html: 'OK'
20272                         }
20273                     ]
20274                 }
20275
20276                 ]
20277             }
20278         ]
20279     }
20280 });
20281
20282 Roo.apply(Roo.bootstrap.TimeField,  {
20283   
20284     template : {
20285         tag: 'div',
20286         cls: 'datepicker dropdown-menu',
20287         cn: [
20288             {
20289                 tag: 'div',
20290                 cls: 'datepicker-time',
20291                 cn: [
20292                 {
20293                     tag: 'table',
20294                     cls: 'table-condensed',
20295                     cn:[
20296                     Roo.bootstrap.TimeField.content,
20297                     Roo.bootstrap.TimeField.footer
20298                     ]
20299                 }
20300                 ]
20301             }
20302         ]
20303     }
20304 });
20305
20306  
20307
20308  /*
20309  * - LGPL
20310  *
20311  * MonthField
20312  * 
20313  */
20314
20315 /**
20316  * @class Roo.bootstrap.MonthField
20317  * @extends Roo.bootstrap.Input
20318  * Bootstrap MonthField class
20319  * 
20320  * @cfg {String} language default en
20321  * 
20322  * @constructor
20323  * Create a new MonthField
20324  * @param {Object} config The config object
20325  */
20326
20327 Roo.bootstrap.MonthField = function(config){
20328     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20329     
20330     this.addEvents({
20331         /**
20332          * @event show
20333          * Fires when this field show.
20334          * @param {Roo.bootstrap.MonthField} this
20335          * @param {Mixed} date The date value
20336          */
20337         show : true,
20338         /**
20339          * @event show
20340          * Fires when this field hide.
20341          * @param {Roo.bootstrap.MonthField} this
20342          * @param {Mixed} date The date value
20343          */
20344         hide : true,
20345         /**
20346          * @event select
20347          * Fires when select a date.
20348          * @param {Roo.bootstrap.MonthField} this
20349          * @param {String} oldvalue The old value
20350          * @param {String} newvalue The new value
20351          */
20352         select : true
20353     });
20354 };
20355
20356 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20357     
20358     onRender: function(ct, position)
20359     {
20360         
20361         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20362         
20363         this.language = this.language || 'en';
20364         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20365         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20366         
20367         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20368         this.isInline = false;
20369         this.isInput = true;
20370         this.component = this.el.select('.add-on', true).first() || false;
20371         this.component = (this.component && this.component.length === 0) ? false : this.component;
20372         this.hasInput = this.component && this.inputEL().length;
20373         
20374         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20375         
20376         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20377         
20378         this.picker().on('mousedown', this.onMousedown, this);
20379         this.picker().on('click', this.onClick, this);
20380         
20381         this.picker().addClass('datepicker-dropdown');
20382         
20383         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20384             v.setStyle('width', '189px');
20385         });
20386         
20387         this.fillMonths();
20388         
20389         this.update();
20390         
20391         if(this.isInline) {
20392             this.show();
20393         }
20394         
20395     },
20396     
20397     setValue: function(v, suppressEvent)
20398     {   
20399         var o = this.getValue();
20400         
20401         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20402         
20403         this.update();
20404
20405         if(suppressEvent !== true){
20406             this.fireEvent('select', this, o, v);
20407         }
20408         
20409     },
20410     
20411     getValue: function()
20412     {
20413         return this.value;
20414     },
20415     
20416     onClick: function(e) 
20417     {
20418         e.stopPropagation();
20419         e.preventDefault();
20420         
20421         var target = e.getTarget();
20422         
20423         if(target.nodeName.toLowerCase() === 'i'){
20424             target = Roo.get(target).dom.parentNode;
20425         }
20426         
20427         var nodeName = target.nodeName;
20428         var className = target.className;
20429         var html = target.innerHTML;
20430         
20431         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20432             return;
20433         }
20434         
20435         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20436         
20437         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20438         
20439         this.hide();
20440                         
20441     },
20442     
20443     picker : function()
20444     {
20445         return this.pickerEl;
20446     },
20447     
20448     fillMonths: function()
20449     {    
20450         var i = 0;
20451         var months = this.picker().select('>.datepicker-months td', true).first();
20452         
20453         months.dom.innerHTML = '';
20454         
20455         while (i < 12) {
20456             var month = {
20457                 tag: 'span',
20458                 cls: 'month',
20459                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20460             };
20461             
20462             months.createChild(month);
20463         }
20464         
20465     },
20466     
20467     update: function()
20468     {
20469         var _this = this;
20470         
20471         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20472             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20473         }
20474         
20475         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20476             e.removeClass('active');
20477             
20478             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20479                 e.addClass('active');
20480             }
20481         })
20482     },
20483     
20484     place: function()
20485     {
20486         if(this.isInline) {
20487             return;
20488         }
20489         
20490         this.picker().removeClass(['bottom', 'top']);
20491         
20492         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20493             /*
20494              * place to the top of element!
20495              *
20496              */
20497             
20498             this.picker().addClass('top');
20499             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20500             
20501             return;
20502         }
20503         
20504         this.picker().addClass('bottom');
20505         
20506         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20507     },
20508     
20509     onFocus : function()
20510     {
20511         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20512         this.show();
20513     },
20514     
20515     onBlur : function()
20516     {
20517         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20518         
20519         var d = this.inputEl().getValue();
20520         
20521         this.setValue(d);
20522                 
20523         this.hide();
20524     },
20525     
20526     show : function()
20527     {
20528         this.picker().show();
20529         this.picker().select('>.datepicker-months', true).first().show();
20530         this.update();
20531         this.place();
20532         
20533         this.fireEvent('show', this, this.date);
20534     },
20535     
20536     hide : function()
20537     {
20538         if(this.isInline) {
20539             return;
20540         }
20541         this.picker().hide();
20542         this.fireEvent('hide', this, this.date);
20543         
20544     },
20545     
20546     onMousedown: function(e)
20547     {
20548         e.stopPropagation();
20549         e.preventDefault();
20550     },
20551     
20552     keyup: function(e)
20553     {
20554         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20555         this.update();
20556     },
20557
20558     fireKey: function(e)
20559     {
20560         if (!this.picker().isVisible()){
20561             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20562                 this.show();
20563             }
20564             return;
20565         }
20566         
20567         var dir;
20568         
20569         switch(e.keyCode){
20570             case 27: // escape
20571                 this.hide();
20572                 e.preventDefault();
20573                 break;
20574             case 37: // left
20575             case 39: // right
20576                 dir = e.keyCode == 37 ? -1 : 1;
20577                 
20578                 this.vIndex = this.vIndex + dir;
20579                 
20580                 if(this.vIndex < 0){
20581                     this.vIndex = 0;
20582                 }
20583                 
20584                 if(this.vIndex > 11){
20585                     this.vIndex = 11;
20586                 }
20587                 
20588                 if(isNaN(this.vIndex)){
20589                     this.vIndex = 0;
20590                 }
20591                 
20592                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20593                 
20594                 break;
20595             case 38: // up
20596             case 40: // down
20597                 
20598                 dir = e.keyCode == 38 ? -1 : 1;
20599                 
20600                 this.vIndex = this.vIndex + dir * 4;
20601                 
20602                 if(this.vIndex < 0){
20603                     this.vIndex = 0;
20604                 }
20605                 
20606                 if(this.vIndex > 11){
20607                     this.vIndex = 11;
20608                 }
20609                 
20610                 if(isNaN(this.vIndex)){
20611                     this.vIndex = 0;
20612                 }
20613                 
20614                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20615                 break;
20616                 
20617             case 13: // enter
20618                 
20619                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20620                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20621                 }
20622                 
20623                 this.hide();
20624                 e.preventDefault();
20625                 break;
20626             case 9: // tab
20627                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20628                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20629                 }
20630                 this.hide();
20631                 break;
20632             case 16: // shift
20633             case 17: // ctrl
20634             case 18: // alt
20635                 break;
20636             default :
20637                 this.hide();
20638                 
20639         }
20640     },
20641     
20642     remove: function() 
20643     {
20644         this.picker().remove();
20645     }
20646    
20647 });
20648
20649 Roo.apply(Roo.bootstrap.MonthField,  {
20650     
20651     content : {
20652         tag: 'tbody',
20653         cn: [
20654         {
20655             tag: 'tr',
20656             cn: [
20657             {
20658                 tag: 'td',
20659                 colspan: '7'
20660             }
20661             ]
20662         }
20663         ]
20664     },
20665     
20666     dates:{
20667         en: {
20668             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20669             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20670         }
20671     }
20672 });
20673
20674 Roo.apply(Roo.bootstrap.MonthField,  {
20675   
20676     template : {
20677         tag: 'div',
20678         cls: 'datepicker dropdown-menu roo-dynamic',
20679         cn: [
20680             {
20681                 tag: 'div',
20682                 cls: 'datepicker-months',
20683                 cn: [
20684                 {
20685                     tag: 'table',
20686                     cls: 'table-condensed',
20687                     cn:[
20688                         Roo.bootstrap.DateField.content
20689                     ]
20690                 }
20691                 ]
20692             }
20693         ]
20694     }
20695 });
20696
20697  
20698
20699  
20700  /*
20701  * - LGPL
20702  *
20703  * CheckBox
20704  * 
20705  */
20706
20707 /**
20708  * @class Roo.bootstrap.CheckBox
20709  * @extends Roo.bootstrap.Input
20710  * Bootstrap CheckBox class
20711  * 
20712  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20713  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20714  * @cfg {String} boxLabel The text that appears beside the checkbox
20715  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20716  * @cfg {Boolean} checked initnal the element
20717  * @cfg {Boolean} inline inline the element (default false)
20718  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20719  * @cfg {String} tooltip label tooltip
20720  * 
20721  * @constructor
20722  * Create a new CheckBox
20723  * @param {Object} config The config object
20724  */
20725
20726 Roo.bootstrap.CheckBox = function(config){
20727     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20728    
20729     this.addEvents({
20730         /**
20731         * @event check
20732         * Fires when the element is checked or unchecked.
20733         * @param {Roo.bootstrap.CheckBox} this This input
20734         * @param {Boolean} checked The new checked value
20735         */
20736        check : true,
20737        /**
20738         * @event click
20739         * Fires when the element is click.
20740         * @param {Roo.bootstrap.CheckBox} this This input
20741         */
20742        click : true
20743     });
20744     
20745 };
20746
20747 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20748   
20749     inputType: 'checkbox',
20750     inputValue: 1,
20751     valueOff: 0,
20752     boxLabel: false,
20753     checked: false,
20754     weight : false,
20755     inline: false,
20756     tooltip : '',
20757     
20758     getAutoCreate : function()
20759     {
20760         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20761         
20762         var id = Roo.id();
20763         
20764         var cfg = {};
20765         
20766         cfg.cls = 'form-group ' + this.inputType; //input-group
20767         
20768         if(this.inline){
20769             cfg.cls += ' ' + this.inputType + '-inline';
20770         }
20771         
20772         var input =  {
20773             tag: 'input',
20774             id : id,
20775             type : this.inputType,
20776             value : this.inputValue,
20777             cls : 'roo-' + this.inputType, //'form-box',
20778             placeholder : this.placeholder || ''
20779             
20780         };
20781         
20782         if(this.inputType != 'radio'){
20783             var hidden =  {
20784                 tag: 'input',
20785                 type : 'hidden',
20786                 cls : 'roo-hidden-value',
20787                 value : this.checked ? this.inputValue : this.valueOff
20788             };
20789         }
20790         
20791             
20792         if (this.weight) { // Validity check?
20793             cfg.cls += " " + this.inputType + "-" + this.weight;
20794         }
20795         
20796         if (this.disabled) {
20797             input.disabled=true;
20798         }
20799         
20800         if(this.checked){
20801             input.checked = this.checked;
20802         }
20803         
20804         if (this.name) {
20805             
20806             input.name = this.name;
20807             
20808             if(this.inputType != 'radio'){
20809                 hidden.name = this.name;
20810                 input.name = '_hidden_' + this.name;
20811             }
20812         }
20813         
20814         if (this.size) {
20815             input.cls += ' input-' + this.size;
20816         }
20817         
20818         var settings=this;
20819         
20820         ['xs','sm','md','lg'].map(function(size){
20821             if (settings[size]) {
20822                 cfg.cls += ' col-' + size + '-' + settings[size];
20823             }
20824         });
20825         
20826         var inputblock = input;
20827          
20828         if (this.before || this.after) {
20829             
20830             inputblock = {
20831                 cls : 'input-group',
20832                 cn :  [] 
20833             };
20834             
20835             if (this.before) {
20836                 inputblock.cn.push({
20837                     tag :'span',
20838                     cls : 'input-group-addon',
20839                     html : this.before
20840                 });
20841             }
20842             
20843             inputblock.cn.push(input);
20844             
20845             if(this.inputType != 'radio'){
20846                 inputblock.cn.push(hidden);
20847             }
20848             
20849             if (this.after) {
20850                 inputblock.cn.push({
20851                     tag :'span',
20852                     cls : 'input-group-addon',
20853                     html : this.after
20854                 });
20855             }
20856             
20857         }
20858         
20859         if (align ==='left' && this.fieldLabel.length) {
20860 //                Roo.log("left and has label");
20861             cfg.cn = [
20862                 {
20863                     tag: 'label',
20864                     'for' :  id,
20865                     cls : 'control-label',
20866                     html : this.fieldLabel
20867                 },
20868                 {
20869                     cls : "", 
20870                     cn: [
20871                         inputblock
20872                     ]
20873                 }
20874             ];
20875             
20876             if(this.labelWidth > 12){
20877                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20878             }
20879             
20880             if(this.labelWidth < 13 && this.labelmd == 0){
20881                 this.labelmd = this.labelWidth;
20882             }
20883             
20884             if(this.labellg > 0){
20885                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20886                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20887             }
20888             
20889             if(this.labelmd > 0){
20890                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20891                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20892             }
20893             
20894             if(this.labelsm > 0){
20895                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20896                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20897             }
20898             
20899             if(this.labelxs > 0){
20900                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20901                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20902             }
20903             
20904         } else if ( this.fieldLabel.length) {
20905 //                Roo.log(" label");
20906                 cfg.cn = [
20907                    
20908                     {
20909                         tag: this.boxLabel ? 'span' : 'label',
20910                         'for': id,
20911                         cls: 'control-label box-input-label',
20912                         //cls : 'input-group-addon',
20913                         html : this.fieldLabel
20914                     },
20915                     
20916                     inputblock
20917                     
20918                 ];
20919
20920         } else {
20921             
20922 //                Roo.log(" no label && no align");
20923                 cfg.cn = [  inputblock ] ;
20924                 
20925                 
20926         }
20927         
20928         if(this.boxLabel){
20929              var boxLabelCfg = {
20930                 tag: 'label',
20931                 //'for': id, // box label is handled by onclick - so no for...
20932                 cls: 'box-label',
20933                 html: this.boxLabel
20934             };
20935             
20936             if(this.tooltip){
20937                 boxLabelCfg.tooltip = this.tooltip;
20938             }
20939              
20940             cfg.cn.push(boxLabelCfg);
20941         }
20942         
20943         if(this.inputType != 'radio'){
20944             cfg.cn.push(hidden);
20945         }
20946         
20947         return cfg;
20948         
20949     },
20950     
20951     /**
20952      * return the real input element.
20953      */
20954     inputEl: function ()
20955     {
20956         return this.el.select('input.roo-' + this.inputType,true).first();
20957     },
20958     hiddenEl: function ()
20959     {
20960         return this.el.select('input.roo-hidden-value',true).first();
20961     },
20962     
20963     labelEl: function()
20964     {
20965         return this.el.select('label.control-label',true).first();
20966     },
20967     /* depricated... */
20968     
20969     label: function()
20970     {
20971         return this.labelEl();
20972     },
20973     
20974     boxLabelEl: function()
20975     {
20976         return this.el.select('label.box-label',true).first();
20977     },
20978     
20979     initEvents : function()
20980     {
20981 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20982         
20983         this.inputEl().on('click', this.onClick,  this);
20984         
20985         if (this.boxLabel) { 
20986             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20987         }
20988         
20989         this.startValue = this.getValue();
20990         
20991         if(this.groupId){
20992             Roo.bootstrap.CheckBox.register(this);
20993         }
20994     },
20995     
20996     onClick : function(e)
20997     {   
20998         if(this.fireEvent('click', this, e) !== false){
20999             this.setChecked(!this.checked);
21000         }
21001         
21002     },
21003     
21004     setChecked : function(state,suppressEvent)
21005     {
21006         this.startValue = this.getValue();
21007
21008         if(this.inputType == 'radio'){
21009             
21010             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21011                 e.dom.checked = false;
21012             });
21013             
21014             this.inputEl().dom.checked = true;
21015             
21016             this.inputEl().dom.value = this.inputValue;
21017             
21018             if(suppressEvent !== true){
21019                 this.fireEvent('check', this, true);
21020             }
21021             
21022             this.validate();
21023             
21024             return;
21025         }
21026         
21027         this.checked = state;
21028         
21029         this.inputEl().dom.checked = state;
21030         
21031         
21032         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21033         
21034         if(suppressEvent !== true){
21035             this.fireEvent('check', this, state);
21036         }
21037         
21038         this.validate();
21039     },
21040     
21041     getValue : function()
21042     {
21043         if(this.inputType == 'radio'){
21044             return this.getGroupValue();
21045         }
21046         
21047         return this.hiddenEl().dom.value;
21048         
21049     },
21050     
21051     getGroupValue : function()
21052     {
21053         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21054             return '';
21055         }
21056         
21057         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21058     },
21059     
21060     setValue : function(v,suppressEvent)
21061     {
21062         if(this.inputType == 'radio'){
21063             this.setGroupValue(v, suppressEvent);
21064             return;
21065         }
21066         
21067         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21068         
21069         this.validate();
21070     },
21071     
21072     setGroupValue : function(v, suppressEvent)
21073     {
21074         this.startValue = this.getValue();
21075         
21076         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21077             e.dom.checked = false;
21078             
21079             if(e.dom.value == v){
21080                 e.dom.checked = true;
21081             }
21082         });
21083         
21084         if(suppressEvent !== true){
21085             this.fireEvent('check', this, true);
21086         }
21087
21088         this.validate();
21089         
21090         return;
21091     },
21092     
21093     validate : function()
21094     {
21095         if(this.getVisibilityEl().hasClass('hidden')){
21096             return true;
21097         }
21098         
21099         if(
21100                 this.disabled || 
21101                 (this.inputType == 'radio' && this.validateRadio()) ||
21102                 (this.inputType == 'checkbox' && this.validateCheckbox())
21103         ){
21104             this.markValid();
21105             return true;
21106         }
21107         
21108         this.markInvalid();
21109         return false;
21110     },
21111     
21112     validateRadio : function()
21113     {
21114         if(this.getVisibilityEl().hasClass('hidden')){
21115             return true;
21116         }
21117         
21118         if(this.allowBlank){
21119             return true;
21120         }
21121         
21122         var valid = false;
21123         
21124         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21125             if(!e.dom.checked){
21126                 return;
21127             }
21128             
21129             valid = true;
21130             
21131             return false;
21132         });
21133         
21134         return valid;
21135     },
21136     
21137     validateCheckbox : function()
21138     {
21139         if(!this.groupId){
21140             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21141             //return (this.getValue() == this.inputValue) ? true : false;
21142         }
21143         
21144         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21145         
21146         if(!group){
21147             return false;
21148         }
21149         
21150         var r = false;
21151         
21152         for(var i in group){
21153             if(group[i].el.isVisible(true)){
21154                 r = false;
21155                 break;
21156             }
21157             
21158             r = true;
21159         }
21160         
21161         for(var i in group){
21162             if(r){
21163                 break;
21164             }
21165             
21166             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21167         }
21168         
21169         return r;
21170     },
21171     
21172     /**
21173      * Mark this field as valid
21174      */
21175     markValid : function()
21176     {
21177         var _this = this;
21178         
21179         this.fireEvent('valid', this);
21180         
21181         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21182         
21183         if(this.groupId){
21184             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21185         }
21186         
21187         if(label){
21188             label.markValid();
21189         }
21190
21191         if(this.inputType == 'radio'){
21192             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21193                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21194                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21195             });
21196             
21197             return;
21198         }
21199
21200         if(!this.groupId){
21201             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21202             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21203             return;
21204         }
21205         
21206         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21207         
21208         if(!group){
21209             return;
21210         }
21211         
21212         for(var i in group){
21213             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21214             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21215         }
21216     },
21217     
21218      /**
21219      * Mark this field as invalid
21220      * @param {String} msg The validation message
21221      */
21222     markInvalid : function(msg)
21223     {
21224         if(this.allowBlank){
21225             return;
21226         }
21227         
21228         var _this = this;
21229         
21230         this.fireEvent('invalid', this, msg);
21231         
21232         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21233         
21234         if(this.groupId){
21235             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21236         }
21237         
21238         if(label){
21239             label.markInvalid();
21240         }
21241             
21242         if(this.inputType == 'radio'){
21243             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21244                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21245                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21246             });
21247             
21248             return;
21249         }
21250         
21251         if(!this.groupId){
21252             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21253             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21254             return;
21255         }
21256         
21257         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21258         
21259         if(!group){
21260             return;
21261         }
21262         
21263         for(var i in group){
21264             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21265             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21266         }
21267         
21268     },
21269     
21270     clearInvalid : function()
21271     {
21272         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21273         
21274         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21275         
21276         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21277         
21278         if (label && label.iconEl) {
21279             label.iconEl.removeClass(label.validClass);
21280             label.iconEl.removeClass(label.invalidClass);
21281         }
21282     },
21283     
21284     disable : function()
21285     {
21286         if(this.inputType != 'radio'){
21287             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21288             return;
21289         }
21290         
21291         var _this = this;
21292         
21293         if(this.rendered){
21294             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21295                 _this.getActionEl().addClass(this.disabledClass);
21296                 e.dom.disabled = true;
21297             });
21298         }
21299         
21300         this.disabled = true;
21301         this.fireEvent("disable", this);
21302         return this;
21303     },
21304
21305     enable : function()
21306     {
21307         if(this.inputType != 'radio'){
21308             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21309             return;
21310         }
21311         
21312         var _this = this;
21313         
21314         if(this.rendered){
21315             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21316                 _this.getActionEl().removeClass(this.disabledClass);
21317                 e.dom.disabled = false;
21318             });
21319         }
21320         
21321         this.disabled = false;
21322         this.fireEvent("enable", this);
21323         return this;
21324     },
21325     
21326     setBoxLabel : function(v)
21327     {
21328         this.boxLabel = v;
21329         
21330         if(this.rendered){
21331             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21332         }
21333     }
21334
21335 });
21336
21337 Roo.apply(Roo.bootstrap.CheckBox, {
21338     
21339     groups: {},
21340     
21341      /**
21342     * register a CheckBox Group
21343     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21344     */
21345     register : function(checkbox)
21346     {
21347         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21348             this.groups[checkbox.groupId] = {};
21349         }
21350         
21351         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21352             return;
21353         }
21354         
21355         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21356         
21357     },
21358     /**
21359     * fetch a CheckBox Group based on the group ID
21360     * @param {string} the group ID
21361     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21362     */
21363     get: function(groupId) {
21364         if (typeof(this.groups[groupId]) == 'undefined') {
21365             return false;
21366         }
21367         
21368         return this.groups[groupId] ;
21369     }
21370     
21371     
21372 });
21373 /*
21374  * - LGPL
21375  *
21376  * RadioItem
21377  * 
21378  */
21379
21380 /**
21381  * @class Roo.bootstrap.Radio
21382  * @extends Roo.bootstrap.Component
21383  * Bootstrap Radio class
21384  * @cfg {String} boxLabel - the label associated
21385  * @cfg {String} value - the value of radio
21386  * 
21387  * @constructor
21388  * Create a new Radio
21389  * @param {Object} config The config object
21390  */
21391 Roo.bootstrap.Radio = function(config){
21392     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21393     
21394 };
21395
21396 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21397     
21398     boxLabel : '',
21399     
21400     value : '',
21401     
21402     getAutoCreate : function()
21403     {
21404         var cfg = {
21405             tag : 'div',
21406             cls : 'form-group radio',
21407             cn : [
21408                 {
21409                     tag : 'label',
21410                     cls : 'box-label',
21411                     html : this.boxLabel
21412                 }
21413             ]
21414         };
21415         
21416         return cfg;
21417     },
21418     
21419     initEvents : function() 
21420     {
21421         this.parent().register(this);
21422         
21423         this.el.on('click', this.onClick, this);
21424         
21425     },
21426     
21427     onClick : function(e)
21428     {
21429         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21430             this.setChecked(true);
21431         }
21432     },
21433     
21434     setChecked : function(state, suppressEvent)
21435     {
21436         this.parent().setValue(this.value, suppressEvent);
21437         
21438     },
21439     
21440     setBoxLabel : function(v)
21441     {
21442         this.boxLabel = v;
21443         
21444         if(this.rendered){
21445             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21446         }
21447     }
21448     
21449 });
21450  
21451
21452  /*
21453  * - LGPL
21454  *
21455  * Input
21456  * 
21457  */
21458
21459 /**
21460  * @class Roo.bootstrap.SecurePass
21461  * @extends Roo.bootstrap.Input
21462  * Bootstrap SecurePass class
21463  *
21464  * 
21465  * @constructor
21466  * Create a new SecurePass
21467  * @param {Object} config The config object
21468  */
21469  
21470 Roo.bootstrap.SecurePass = function (config) {
21471     // these go here, so the translation tool can replace them..
21472     this.errors = {
21473         PwdEmpty: "Please type a password, and then retype it to confirm.",
21474         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21475         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21476         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21477         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21478         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21479         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21480         TooWeak: "Your password is Too Weak."
21481     },
21482     this.meterLabel = "Password strength:";
21483     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21484     this.meterClass = [
21485         "roo-password-meter-tooweak", 
21486         "roo-password-meter-weak", 
21487         "roo-password-meter-medium", 
21488         "roo-password-meter-strong", 
21489         "roo-password-meter-grey"
21490     ];
21491     
21492     this.errors = {};
21493     
21494     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21495 }
21496
21497 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21498     /**
21499      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21500      * {
21501      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21502      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21503      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21504      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21505      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21506      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21507      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21508      * })
21509      */
21510     // private
21511     
21512     meterWidth: 300,
21513     errorMsg :'',    
21514     errors: false,
21515     imageRoot: '/',
21516     /**
21517      * @cfg {String/Object} Label for the strength meter (defaults to
21518      * 'Password strength:')
21519      */
21520     // private
21521     meterLabel: '',
21522     /**
21523      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21524      * ['Weak', 'Medium', 'Strong'])
21525      */
21526     // private    
21527     pwdStrengths: false,    
21528     // private
21529     strength: 0,
21530     // private
21531     _lastPwd: null,
21532     // private
21533     kCapitalLetter: 0,
21534     kSmallLetter: 1,
21535     kDigit: 2,
21536     kPunctuation: 3,
21537     
21538     insecure: false,
21539     // private
21540     initEvents: function ()
21541     {
21542         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21543
21544         if (this.el.is('input[type=password]') && Roo.isSafari) {
21545             this.el.on('keydown', this.SafariOnKeyDown, this);
21546         }
21547
21548         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21549     },
21550     // private
21551     onRender: function (ct, position)
21552     {
21553         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21554         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21555         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21556
21557         this.trigger.createChild({
21558                    cn: [
21559                     {
21560                     //id: 'PwdMeter',
21561                     tag: 'div',
21562                     cls: 'roo-password-meter-grey col-xs-12',
21563                     style: {
21564                         //width: 0,
21565                         //width: this.meterWidth + 'px'                                                
21566                         }
21567                     },
21568                     {                            
21569                          cls: 'roo-password-meter-text'                          
21570                     }
21571                 ]            
21572         });
21573
21574          
21575         if (this.hideTrigger) {
21576             this.trigger.setDisplayed(false);
21577         }
21578         this.setSize(this.width || '', this.height || '');
21579     },
21580     // private
21581     onDestroy: function ()
21582     {
21583         if (this.trigger) {
21584             this.trigger.removeAllListeners();
21585             this.trigger.remove();
21586         }
21587         if (this.wrap) {
21588             this.wrap.remove();
21589         }
21590         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21591     },
21592     // private
21593     checkStrength: function ()
21594     {
21595         var pwd = this.inputEl().getValue();
21596         if (pwd == this._lastPwd) {
21597             return;
21598         }
21599
21600         var strength;
21601         if (this.ClientSideStrongPassword(pwd)) {
21602             strength = 3;
21603         } else if (this.ClientSideMediumPassword(pwd)) {
21604             strength = 2;
21605         } else if (this.ClientSideWeakPassword(pwd)) {
21606             strength = 1;
21607         } else {
21608             strength = 0;
21609         }
21610         
21611         Roo.log('strength1: ' + strength);
21612         
21613         //var pm = this.trigger.child('div/div/div').dom;
21614         var pm = this.trigger.child('div/div');
21615         pm.removeClass(this.meterClass);
21616         pm.addClass(this.meterClass[strength]);
21617                 
21618         
21619         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21620                 
21621         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21622         
21623         this._lastPwd = pwd;
21624     },
21625     reset: function ()
21626     {
21627         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21628         
21629         this._lastPwd = '';
21630         
21631         var pm = this.trigger.child('div/div');
21632         pm.removeClass(this.meterClass);
21633         pm.addClass('roo-password-meter-grey');        
21634         
21635         
21636         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21637         
21638         pt.innerHTML = '';
21639         this.inputEl().dom.type='password';
21640     },
21641     // private
21642     validateValue: function (value)
21643     {
21644         
21645         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21646             return false;
21647         }
21648         if (value.length == 0) {
21649             if (this.allowBlank) {
21650                 this.clearInvalid();
21651                 return true;
21652             }
21653
21654             this.markInvalid(this.errors.PwdEmpty);
21655             this.errorMsg = this.errors.PwdEmpty;
21656             return false;
21657         }
21658         
21659         if(this.insecure){
21660             return true;
21661         }
21662         
21663         if ('[\x21-\x7e]*'.match(value)) {
21664             this.markInvalid(this.errors.PwdBadChar);
21665             this.errorMsg = this.errors.PwdBadChar;
21666             return false;
21667         }
21668         if (value.length < 6) {
21669             this.markInvalid(this.errors.PwdShort);
21670             this.errorMsg = this.errors.PwdShort;
21671             return false;
21672         }
21673         if (value.length > 16) {
21674             this.markInvalid(this.errors.PwdLong);
21675             this.errorMsg = this.errors.PwdLong;
21676             return false;
21677         }
21678         var strength;
21679         if (this.ClientSideStrongPassword(value)) {
21680             strength = 3;
21681         } else if (this.ClientSideMediumPassword(value)) {
21682             strength = 2;
21683         } else if (this.ClientSideWeakPassword(value)) {
21684             strength = 1;
21685         } else {
21686             strength = 0;
21687         }
21688
21689         
21690         if (strength < 2) {
21691             //this.markInvalid(this.errors.TooWeak);
21692             this.errorMsg = this.errors.TooWeak;
21693             //return false;
21694         }
21695         
21696         
21697         console.log('strength2: ' + strength);
21698         
21699         //var pm = this.trigger.child('div/div/div').dom;
21700         
21701         var pm = this.trigger.child('div/div');
21702         pm.removeClass(this.meterClass);
21703         pm.addClass(this.meterClass[strength]);
21704                 
21705         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21706                 
21707         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21708         
21709         this.errorMsg = ''; 
21710         return true;
21711     },
21712     // private
21713     CharacterSetChecks: function (type)
21714     {
21715         this.type = type;
21716         this.fResult = false;
21717     },
21718     // private
21719     isctype: function (character, type)
21720     {
21721         switch (type) {  
21722             case this.kCapitalLetter:
21723                 if (character >= 'A' && character <= 'Z') {
21724                     return true;
21725                 }
21726                 break;
21727             
21728             case this.kSmallLetter:
21729                 if (character >= 'a' && character <= 'z') {
21730                     return true;
21731                 }
21732                 break;
21733             
21734             case this.kDigit:
21735                 if (character >= '0' && character <= '9') {
21736                     return true;
21737                 }
21738                 break;
21739             
21740             case this.kPunctuation:
21741                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21742                     return true;
21743                 }
21744                 break;
21745             
21746             default:
21747                 return false;
21748         }
21749
21750     },
21751     // private
21752     IsLongEnough: function (pwd, size)
21753     {
21754         return !(pwd == null || isNaN(size) || pwd.length < size);
21755     },
21756     // private
21757     SpansEnoughCharacterSets: function (word, nb)
21758     {
21759         if (!this.IsLongEnough(word, nb))
21760         {
21761             return false;
21762         }
21763
21764         var characterSetChecks = new Array(
21765             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21766             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21767         );
21768         
21769         for (var index = 0; index < word.length; ++index) {
21770             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21771                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21772                     characterSetChecks[nCharSet].fResult = true;
21773                     break;
21774                 }
21775             }
21776         }
21777
21778         var nCharSets = 0;
21779         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21780             if (characterSetChecks[nCharSet].fResult) {
21781                 ++nCharSets;
21782             }
21783         }
21784
21785         if (nCharSets < nb) {
21786             return false;
21787         }
21788         return true;
21789     },
21790     // private
21791     ClientSideStrongPassword: function (pwd)
21792     {
21793         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21794     },
21795     // private
21796     ClientSideMediumPassword: function (pwd)
21797     {
21798         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21799     },
21800     // private
21801     ClientSideWeakPassword: function (pwd)
21802     {
21803         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21804     }
21805           
21806 })//<script type="text/javascript">
21807
21808 /*
21809  * Based  Ext JS Library 1.1.1
21810  * Copyright(c) 2006-2007, Ext JS, LLC.
21811  * LGPL
21812  *
21813  */
21814  
21815 /**
21816  * @class Roo.HtmlEditorCore
21817  * @extends Roo.Component
21818  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21819  *
21820  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21821  */
21822
21823 Roo.HtmlEditorCore = function(config){
21824     
21825     
21826     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21827     
21828     
21829     this.addEvents({
21830         /**
21831          * @event initialize
21832          * Fires when the editor is fully initialized (including the iframe)
21833          * @param {Roo.HtmlEditorCore} this
21834          */
21835         initialize: true,
21836         /**
21837          * @event activate
21838          * Fires when the editor is first receives the focus. Any insertion must wait
21839          * until after this event.
21840          * @param {Roo.HtmlEditorCore} this
21841          */
21842         activate: true,
21843          /**
21844          * @event beforesync
21845          * Fires before the textarea is updated with content from the editor iframe. Return false
21846          * to cancel the sync.
21847          * @param {Roo.HtmlEditorCore} this
21848          * @param {String} html
21849          */
21850         beforesync: true,
21851          /**
21852          * @event beforepush
21853          * Fires before the iframe editor is updated with content from the textarea. Return false
21854          * to cancel the push.
21855          * @param {Roo.HtmlEditorCore} this
21856          * @param {String} html
21857          */
21858         beforepush: true,
21859          /**
21860          * @event sync
21861          * Fires when the textarea is updated with content from the editor iframe.
21862          * @param {Roo.HtmlEditorCore} this
21863          * @param {String} html
21864          */
21865         sync: true,
21866          /**
21867          * @event push
21868          * Fires when the iframe editor is updated with content from the textarea.
21869          * @param {Roo.HtmlEditorCore} this
21870          * @param {String} html
21871          */
21872         push: true,
21873         
21874         /**
21875          * @event editorevent
21876          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21877          * @param {Roo.HtmlEditorCore} this
21878          */
21879         editorevent: true
21880         
21881     });
21882     
21883     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21884     
21885     // defaults : white / black...
21886     this.applyBlacklists();
21887     
21888     
21889     
21890 };
21891
21892
21893 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21894
21895
21896      /**
21897      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21898      */
21899     
21900     owner : false,
21901     
21902      /**
21903      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21904      *                        Roo.resizable.
21905      */
21906     resizable : false,
21907      /**
21908      * @cfg {Number} height (in pixels)
21909      */   
21910     height: 300,
21911    /**
21912      * @cfg {Number} width (in pixels)
21913      */   
21914     width: 500,
21915     
21916     /**
21917      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21918      * 
21919      */
21920     stylesheets: false,
21921     
21922     // id of frame..
21923     frameId: false,
21924     
21925     // private properties
21926     validationEvent : false,
21927     deferHeight: true,
21928     initialized : false,
21929     activated : false,
21930     sourceEditMode : false,
21931     onFocus : Roo.emptyFn,
21932     iframePad:3,
21933     hideMode:'offsets',
21934     
21935     clearUp: true,
21936     
21937     // blacklist + whitelisted elements..
21938     black: false,
21939     white: false,
21940      
21941     bodyCls : '',
21942
21943     /**
21944      * Protected method that will not generally be called directly. It
21945      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21946      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21947      */
21948     getDocMarkup : function(){
21949         // body styles..
21950         var st = '';
21951         
21952         // inherit styels from page...?? 
21953         if (this.stylesheets === false) {
21954             
21955             Roo.get(document.head).select('style').each(function(node) {
21956                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21957             });
21958             
21959             Roo.get(document.head).select('link').each(function(node) { 
21960                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21961             });
21962             
21963         } else if (!this.stylesheets.length) {
21964                 // simple..
21965                 st = '<style type="text/css">' +
21966                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21967                    '</style>';
21968         } else { 
21969             st = '<style type="text/css">' +
21970                     this.stylesheets +
21971                 '</style>';
21972         }
21973         
21974         st +=  '<style type="text/css">' +
21975             'IMG { cursor: pointer } ' +
21976         '</style>';
21977
21978         var cls = 'roo-htmleditor-body';
21979         
21980         if(this.bodyCls.length){
21981             cls += ' ' + this.bodyCls;
21982         }
21983         
21984         return '<html><head>' + st  +
21985             //<style type="text/css">' +
21986             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21987             //'</style>' +
21988             ' </head><body class="' +  cls + '"></body></html>';
21989     },
21990
21991     // private
21992     onRender : function(ct, position)
21993     {
21994         var _t = this;
21995         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21996         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21997         
21998         
21999         this.el.dom.style.border = '0 none';
22000         this.el.dom.setAttribute('tabIndex', -1);
22001         this.el.addClass('x-hidden hide');
22002         
22003         
22004         
22005         if(Roo.isIE){ // fix IE 1px bogus margin
22006             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22007         }
22008        
22009         
22010         this.frameId = Roo.id();
22011         
22012          
22013         
22014         var iframe = this.owner.wrap.createChild({
22015             tag: 'iframe',
22016             cls: 'form-control', // bootstrap..
22017             id: this.frameId,
22018             name: this.frameId,
22019             frameBorder : 'no',
22020             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22021         }, this.el
22022         );
22023         
22024         
22025         this.iframe = iframe.dom;
22026
22027          this.assignDocWin();
22028         
22029         this.doc.designMode = 'on';
22030        
22031         this.doc.open();
22032         this.doc.write(this.getDocMarkup());
22033         this.doc.close();
22034
22035         
22036         var task = { // must defer to wait for browser to be ready
22037             run : function(){
22038                 //console.log("run task?" + this.doc.readyState);
22039                 this.assignDocWin();
22040                 if(this.doc.body || this.doc.readyState == 'complete'){
22041                     try {
22042                         this.doc.designMode="on";
22043                     } catch (e) {
22044                         return;
22045                     }
22046                     Roo.TaskMgr.stop(task);
22047                     this.initEditor.defer(10, this);
22048                 }
22049             },
22050             interval : 10,
22051             duration: 10000,
22052             scope: this
22053         };
22054         Roo.TaskMgr.start(task);
22055
22056     },
22057
22058     // private
22059     onResize : function(w, h)
22060     {
22061          Roo.log('resize: ' +w + ',' + h );
22062         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22063         if(!this.iframe){
22064             return;
22065         }
22066         if(typeof w == 'number'){
22067             
22068             this.iframe.style.width = w + 'px';
22069         }
22070         if(typeof h == 'number'){
22071             
22072             this.iframe.style.height = h + 'px';
22073             if(this.doc){
22074                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22075             }
22076         }
22077         
22078     },
22079
22080     /**
22081      * Toggles the editor between standard and source edit mode.
22082      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22083      */
22084     toggleSourceEdit : function(sourceEditMode){
22085         
22086         this.sourceEditMode = sourceEditMode === true;
22087         
22088         if(this.sourceEditMode){
22089  
22090             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22091             
22092         }else{
22093             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22094             //this.iframe.className = '';
22095             this.deferFocus();
22096         }
22097         //this.setSize(this.owner.wrap.getSize());
22098         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22099     },
22100
22101     
22102   
22103
22104     /**
22105      * Protected method that will not generally be called directly. If you need/want
22106      * custom HTML cleanup, this is the method you should override.
22107      * @param {String} html The HTML to be cleaned
22108      * return {String} The cleaned HTML
22109      */
22110     cleanHtml : function(html){
22111         html = String(html);
22112         if(html.length > 5){
22113             if(Roo.isSafari){ // strip safari nonsense
22114                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22115             }
22116         }
22117         if(html == '&nbsp;'){
22118             html = '';
22119         }
22120         return html;
22121     },
22122
22123     /**
22124      * HTML Editor -> Textarea
22125      * Protected method that will not generally be called directly. Syncs the contents
22126      * of the editor iframe with the textarea.
22127      */
22128     syncValue : function(){
22129         if(this.initialized){
22130             var bd = (this.doc.body || this.doc.documentElement);
22131             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22132             var html = bd.innerHTML;
22133             if(Roo.isSafari){
22134                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22135                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22136                 if(m && m[1]){
22137                     html = '<div style="'+m[0]+'">' + html + '</div>';
22138                 }
22139             }
22140             html = this.cleanHtml(html);
22141             // fix up the special chars.. normaly like back quotes in word...
22142             // however we do not want to do this with chinese..
22143             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22144                 var cc = b.charCodeAt();
22145                 if (
22146                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22147                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22148                     (cc >= 0xf900 && cc < 0xfb00 )
22149                 ) {
22150                         return b;
22151                 }
22152                 return "&#"+cc+";" 
22153             });
22154             if(this.owner.fireEvent('beforesync', this, html) !== false){
22155                 this.el.dom.value = html;
22156                 this.owner.fireEvent('sync', this, html);
22157             }
22158         }
22159     },
22160
22161     /**
22162      * Protected method that will not generally be called directly. Pushes the value of the textarea
22163      * into the iframe editor.
22164      */
22165     pushValue : function(){
22166         if(this.initialized){
22167             var v = this.el.dom.value.trim();
22168             
22169 //            if(v.length < 1){
22170 //                v = '&#160;';
22171 //            }
22172             
22173             if(this.owner.fireEvent('beforepush', this, v) !== false){
22174                 var d = (this.doc.body || this.doc.documentElement);
22175                 d.innerHTML = v;
22176                 this.cleanUpPaste();
22177                 this.el.dom.value = d.innerHTML;
22178                 this.owner.fireEvent('push', this, v);
22179             }
22180         }
22181     },
22182
22183     // private
22184     deferFocus : function(){
22185         this.focus.defer(10, this);
22186     },
22187
22188     // doc'ed in Field
22189     focus : function(){
22190         if(this.win && !this.sourceEditMode){
22191             this.win.focus();
22192         }else{
22193             this.el.focus();
22194         }
22195     },
22196     
22197     assignDocWin: function()
22198     {
22199         var iframe = this.iframe;
22200         
22201          if(Roo.isIE){
22202             this.doc = iframe.contentWindow.document;
22203             this.win = iframe.contentWindow;
22204         } else {
22205 //            if (!Roo.get(this.frameId)) {
22206 //                return;
22207 //            }
22208 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22209 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22210             
22211             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22212                 return;
22213             }
22214             
22215             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22216             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22217         }
22218     },
22219     
22220     // private
22221     initEditor : function(){
22222         //console.log("INIT EDITOR");
22223         this.assignDocWin();
22224         
22225         
22226         
22227         this.doc.designMode="on";
22228         this.doc.open();
22229         this.doc.write(this.getDocMarkup());
22230         this.doc.close();
22231         
22232         var dbody = (this.doc.body || this.doc.documentElement);
22233         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22234         // this copies styles from the containing element into thsi one..
22235         // not sure why we need all of this..
22236         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22237         
22238         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22239         //ss['background-attachment'] = 'fixed'; // w3c
22240         dbody.bgProperties = 'fixed'; // ie
22241         //Roo.DomHelper.applyStyles(dbody, ss);
22242         Roo.EventManager.on(this.doc, {
22243             //'mousedown': this.onEditorEvent,
22244             'mouseup': this.onEditorEvent,
22245             'dblclick': this.onEditorEvent,
22246             'click': this.onEditorEvent,
22247             'keyup': this.onEditorEvent,
22248             buffer:100,
22249             scope: this
22250         });
22251         if(Roo.isGecko){
22252             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22253         }
22254         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22255             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22256         }
22257         this.initialized = true;
22258
22259         this.owner.fireEvent('initialize', this);
22260         this.pushValue();
22261     },
22262
22263     // private
22264     onDestroy : function(){
22265         
22266         
22267         
22268         if(this.rendered){
22269             
22270             //for (var i =0; i < this.toolbars.length;i++) {
22271             //    // fixme - ask toolbars for heights?
22272             //    this.toolbars[i].onDestroy();
22273            // }
22274             
22275             //this.wrap.dom.innerHTML = '';
22276             //this.wrap.remove();
22277         }
22278     },
22279
22280     // private
22281     onFirstFocus : function(){
22282         
22283         this.assignDocWin();
22284         
22285         
22286         this.activated = true;
22287          
22288     
22289         if(Roo.isGecko){ // prevent silly gecko errors
22290             this.win.focus();
22291             var s = this.win.getSelection();
22292             if(!s.focusNode || s.focusNode.nodeType != 3){
22293                 var r = s.getRangeAt(0);
22294                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22295                 r.collapse(true);
22296                 this.deferFocus();
22297             }
22298             try{
22299                 this.execCmd('useCSS', true);
22300                 this.execCmd('styleWithCSS', false);
22301             }catch(e){}
22302         }
22303         this.owner.fireEvent('activate', this);
22304     },
22305
22306     // private
22307     adjustFont: function(btn){
22308         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22309         //if(Roo.isSafari){ // safari
22310         //    adjust *= 2;
22311        // }
22312         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22313         if(Roo.isSafari){ // safari
22314             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22315             v =  (v < 10) ? 10 : v;
22316             v =  (v > 48) ? 48 : v;
22317             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22318             
22319         }
22320         
22321         
22322         v = Math.max(1, v+adjust);
22323         
22324         this.execCmd('FontSize', v  );
22325     },
22326
22327     onEditorEvent : function(e)
22328     {
22329         this.owner.fireEvent('editorevent', this, e);
22330       //  this.updateToolbar();
22331         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22332     },
22333
22334     insertTag : function(tg)
22335     {
22336         // could be a bit smarter... -> wrap the current selected tRoo..
22337         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22338             
22339             range = this.createRange(this.getSelection());
22340             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22341             wrappingNode.appendChild(range.extractContents());
22342             range.insertNode(wrappingNode);
22343
22344             return;
22345             
22346             
22347             
22348         }
22349         this.execCmd("formatblock",   tg);
22350         
22351     },
22352     
22353     insertText : function(txt)
22354     {
22355         
22356         
22357         var range = this.createRange();
22358         range.deleteContents();
22359                //alert(Sender.getAttribute('label'));
22360                
22361         range.insertNode(this.doc.createTextNode(txt));
22362     } ,
22363     
22364      
22365
22366     /**
22367      * Executes a Midas editor command on the editor document and performs necessary focus and
22368      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22369      * @param {String} cmd The Midas command
22370      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22371      */
22372     relayCmd : function(cmd, value){
22373         this.win.focus();
22374         this.execCmd(cmd, value);
22375         this.owner.fireEvent('editorevent', this);
22376         //this.updateToolbar();
22377         this.owner.deferFocus();
22378     },
22379
22380     /**
22381      * Executes a Midas editor command directly on the editor document.
22382      * For visual commands, you should use {@link #relayCmd} instead.
22383      * <b>This should only be called after the editor is initialized.</b>
22384      * @param {String} cmd The Midas command
22385      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22386      */
22387     execCmd : function(cmd, value){
22388         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22389         this.syncValue();
22390     },
22391  
22392  
22393    
22394     /**
22395      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22396      * to insert tRoo.
22397      * @param {String} text | dom node.. 
22398      */
22399     insertAtCursor : function(text)
22400     {
22401         
22402         if(!this.activated){
22403             return;
22404         }
22405         /*
22406         if(Roo.isIE){
22407             this.win.focus();
22408             var r = this.doc.selection.createRange();
22409             if(r){
22410                 r.collapse(true);
22411                 r.pasteHTML(text);
22412                 this.syncValue();
22413                 this.deferFocus();
22414             
22415             }
22416             return;
22417         }
22418         */
22419         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22420             this.win.focus();
22421             
22422             
22423             // from jquery ui (MIT licenced)
22424             var range, node;
22425             var win = this.win;
22426             
22427             if (win.getSelection && win.getSelection().getRangeAt) {
22428                 range = win.getSelection().getRangeAt(0);
22429                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22430                 range.insertNode(node);
22431             } else if (win.document.selection && win.document.selection.createRange) {
22432                 // no firefox support
22433                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22434                 win.document.selection.createRange().pasteHTML(txt);
22435             } else {
22436                 // no firefox support
22437                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22438                 this.execCmd('InsertHTML', txt);
22439             } 
22440             
22441             this.syncValue();
22442             
22443             this.deferFocus();
22444         }
22445     },
22446  // private
22447     mozKeyPress : function(e){
22448         if(e.ctrlKey){
22449             var c = e.getCharCode(), cmd;
22450           
22451             if(c > 0){
22452                 c = String.fromCharCode(c).toLowerCase();
22453                 switch(c){
22454                     case 'b':
22455                         cmd = 'bold';
22456                         break;
22457                     case 'i':
22458                         cmd = 'italic';
22459                         break;
22460                     
22461                     case 'u':
22462                         cmd = 'underline';
22463                         break;
22464                     
22465                     case 'v':
22466                         this.cleanUpPaste.defer(100, this);
22467                         return;
22468                         
22469                 }
22470                 if(cmd){
22471                     this.win.focus();
22472                     this.execCmd(cmd);
22473                     this.deferFocus();
22474                     e.preventDefault();
22475                 }
22476                 
22477             }
22478         }
22479     },
22480
22481     // private
22482     fixKeys : function(){ // load time branching for fastest keydown performance
22483         if(Roo.isIE){
22484             return function(e){
22485                 var k = e.getKey(), r;
22486                 if(k == e.TAB){
22487                     e.stopEvent();
22488                     r = this.doc.selection.createRange();
22489                     if(r){
22490                         r.collapse(true);
22491                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22492                         this.deferFocus();
22493                     }
22494                     return;
22495                 }
22496                 
22497                 if(k == e.ENTER){
22498                     r = this.doc.selection.createRange();
22499                     if(r){
22500                         var target = r.parentElement();
22501                         if(!target || target.tagName.toLowerCase() != 'li'){
22502                             e.stopEvent();
22503                             r.pasteHTML('<br />');
22504                             r.collapse(false);
22505                             r.select();
22506                         }
22507                     }
22508                 }
22509                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22510                     this.cleanUpPaste.defer(100, this);
22511                     return;
22512                 }
22513                 
22514                 
22515             };
22516         }else if(Roo.isOpera){
22517             return function(e){
22518                 var k = e.getKey();
22519                 if(k == e.TAB){
22520                     e.stopEvent();
22521                     this.win.focus();
22522                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22523                     this.deferFocus();
22524                 }
22525                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22526                     this.cleanUpPaste.defer(100, this);
22527                     return;
22528                 }
22529                 
22530             };
22531         }else if(Roo.isSafari){
22532             return function(e){
22533                 var k = e.getKey();
22534                 
22535                 if(k == e.TAB){
22536                     e.stopEvent();
22537                     this.execCmd('InsertText','\t');
22538                     this.deferFocus();
22539                     return;
22540                 }
22541                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22542                     this.cleanUpPaste.defer(100, this);
22543                     return;
22544                 }
22545                 
22546              };
22547         }
22548     }(),
22549     
22550     getAllAncestors: function()
22551     {
22552         var p = this.getSelectedNode();
22553         var a = [];
22554         if (!p) {
22555             a.push(p); // push blank onto stack..
22556             p = this.getParentElement();
22557         }
22558         
22559         
22560         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22561             a.push(p);
22562             p = p.parentNode;
22563         }
22564         a.push(this.doc.body);
22565         return a;
22566     },
22567     lastSel : false,
22568     lastSelNode : false,
22569     
22570     
22571     getSelection : function() 
22572     {
22573         this.assignDocWin();
22574         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22575     },
22576     
22577     getSelectedNode: function() 
22578     {
22579         // this may only work on Gecko!!!
22580         
22581         // should we cache this!!!!
22582         
22583         
22584         
22585          
22586         var range = this.createRange(this.getSelection()).cloneRange();
22587         
22588         if (Roo.isIE) {
22589             var parent = range.parentElement();
22590             while (true) {
22591                 var testRange = range.duplicate();
22592                 testRange.moveToElementText(parent);
22593                 if (testRange.inRange(range)) {
22594                     break;
22595                 }
22596                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22597                     break;
22598                 }
22599                 parent = parent.parentElement;
22600             }
22601             return parent;
22602         }
22603         
22604         // is ancestor a text element.
22605         var ac =  range.commonAncestorContainer;
22606         if (ac.nodeType == 3) {
22607             ac = ac.parentNode;
22608         }
22609         
22610         var ar = ac.childNodes;
22611          
22612         var nodes = [];
22613         var other_nodes = [];
22614         var has_other_nodes = false;
22615         for (var i=0;i<ar.length;i++) {
22616             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22617                 continue;
22618             }
22619             // fullly contained node.
22620             
22621             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22622                 nodes.push(ar[i]);
22623                 continue;
22624             }
22625             
22626             // probably selected..
22627             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22628                 other_nodes.push(ar[i]);
22629                 continue;
22630             }
22631             // outer..
22632             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22633                 continue;
22634             }
22635             
22636             
22637             has_other_nodes = true;
22638         }
22639         if (!nodes.length && other_nodes.length) {
22640             nodes= other_nodes;
22641         }
22642         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22643             return false;
22644         }
22645         
22646         return nodes[0];
22647     },
22648     createRange: function(sel)
22649     {
22650         // this has strange effects when using with 
22651         // top toolbar - not sure if it's a great idea.
22652         //this.editor.contentWindow.focus();
22653         if (typeof sel != "undefined") {
22654             try {
22655                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22656             } catch(e) {
22657                 return this.doc.createRange();
22658             }
22659         } else {
22660             return this.doc.createRange();
22661         }
22662     },
22663     getParentElement: function()
22664     {
22665         
22666         this.assignDocWin();
22667         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22668         
22669         var range = this.createRange(sel);
22670          
22671         try {
22672             var p = range.commonAncestorContainer;
22673             while (p.nodeType == 3) { // text node
22674                 p = p.parentNode;
22675             }
22676             return p;
22677         } catch (e) {
22678             return null;
22679         }
22680     
22681     },
22682     /***
22683      *
22684      * Range intersection.. the hard stuff...
22685      *  '-1' = before
22686      *  '0' = hits..
22687      *  '1' = after.
22688      *         [ -- selected range --- ]
22689      *   [fail]                        [fail]
22690      *
22691      *    basically..
22692      *      if end is before start or  hits it. fail.
22693      *      if start is after end or hits it fail.
22694      *
22695      *   if either hits (but other is outside. - then it's not 
22696      *   
22697      *    
22698      **/
22699     
22700     
22701     // @see http://www.thismuchiknow.co.uk/?p=64.
22702     rangeIntersectsNode : function(range, node)
22703     {
22704         var nodeRange = node.ownerDocument.createRange();
22705         try {
22706             nodeRange.selectNode(node);
22707         } catch (e) {
22708             nodeRange.selectNodeContents(node);
22709         }
22710     
22711         var rangeStartRange = range.cloneRange();
22712         rangeStartRange.collapse(true);
22713     
22714         var rangeEndRange = range.cloneRange();
22715         rangeEndRange.collapse(false);
22716     
22717         var nodeStartRange = nodeRange.cloneRange();
22718         nodeStartRange.collapse(true);
22719     
22720         var nodeEndRange = nodeRange.cloneRange();
22721         nodeEndRange.collapse(false);
22722     
22723         return rangeStartRange.compareBoundaryPoints(
22724                  Range.START_TO_START, nodeEndRange) == -1 &&
22725                rangeEndRange.compareBoundaryPoints(
22726                  Range.START_TO_START, nodeStartRange) == 1;
22727         
22728          
22729     },
22730     rangeCompareNode : function(range, node)
22731     {
22732         var nodeRange = node.ownerDocument.createRange();
22733         try {
22734             nodeRange.selectNode(node);
22735         } catch (e) {
22736             nodeRange.selectNodeContents(node);
22737         }
22738         
22739         
22740         range.collapse(true);
22741     
22742         nodeRange.collapse(true);
22743      
22744         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22745         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22746          
22747         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22748         
22749         var nodeIsBefore   =  ss == 1;
22750         var nodeIsAfter    = ee == -1;
22751         
22752         if (nodeIsBefore && nodeIsAfter) {
22753             return 0; // outer
22754         }
22755         if (!nodeIsBefore && nodeIsAfter) {
22756             return 1; //right trailed.
22757         }
22758         
22759         if (nodeIsBefore && !nodeIsAfter) {
22760             return 2;  // left trailed.
22761         }
22762         // fully contined.
22763         return 3;
22764     },
22765
22766     // private? - in a new class?
22767     cleanUpPaste :  function()
22768     {
22769         // cleans up the whole document..
22770         Roo.log('cleanuppaste');
22771         
22772         this.cleanUpChildren(this.doc.body);
22773         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22774         if (clean != this.doc.body.innerHTML) {
22775             this.doc.body.innerHTML = clean;
22776         }
22777         
22778     },
22779     
22780     cleanWordChars : function(input) {// change the chars to hex code
22781         var he = Roo.HtmlEditorCore;
22782         
22783         var output = input;
22784         Roo.each(he.swapCodes, function(sw) { 
22785             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22786             
22787             output = output.replace(swapper, sw[1]);
22788         });
22789         
22790         return output;
22791     },
22792     
22793     
22794     cleanUpChildren : function (n)
22795     {
22796         if (!n.childNodes.length) {
22797             return;
22798         }
22799         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22800            this.cleanUpChild(n.childNodes[i]);
22801         }
22802     },
22803     
22804     
22805         
22806     
22807     cleanUpChild : function (node)
22808     {
22809         var ed = this;
22810         //console.log(node);
22811         if (node.nodeName == "#text") {
22812             // clean up silly Windows -- stuff?
22813             return; 
22814         }
22815         if (node.nodeName == "#comment") {
22816             node.parentNode.removeChild(node);
22817             // clean up silly Windows -- stuff?
22818             return; 
22819         }
22820         var lcname = node.tagName.toLowerCase();
22821         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22822         // whitelist of tags..
22823         
22824         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22825             // remove node.
22826             node.parentNode.removeChild(node);
22827             return;
22828             
22829         }
22830         
22831         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22832         
22833         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22834         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22835         
22836         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22837         //    remove_keep_children = true;
22838         //}
22839         
22840         if (remove_keep_children) {
22841             this.cleanUpChildren(node);
22842             // inserts everything just before this node...
22843             while (node.childNodes.length) {
22844                 var cn = node.childNodes[0];
22845                 node.removeChild(cn);
22846                 node.parentNode.insertBefore(cn, node);
22847             }
22848             node.parentNode.removeChild(node);
22849             return;
22850         }
22851         
22852         if (!node.attributes || !node.attributes.length) {
22853             this.cleanUpChildren(node);
22854             return;
22855         }
22856         
22857         function cleanAttr(n,v)
22858         {
22859             
22860             if (v.match(/^\./) || v.match(/^\//)) {
22861                 return;
22862             }
22863             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22864                 return;
22865             }
22866             if (v.match(/^#/)) {
22867                 return;
22868             }
22869 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22870             node.removeAttribute(n);
22871             
22872         }
22873         
22874         var cwhite = this.cwhite;
22875         var cblack = this.cblack;
22876             
22877         function cleanStyle(n,v)
22878         {
22879             if (v.match(/expression/)) { //XSS?? should we even bother..
22880                 node.removeAttribute(n);
22881                 return;
22882             }
22883             
22884             var parts = v.split(/;/);
22885             var clean = [];
22886             
22887             Roo.each(parts, function(p) {
22888                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22889                 if (!p.length) {
22890                     return true;
22891                 }
22892                 var l = p.split(':').shift().replace(/\s+/g,'');
22893                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22894                 
22895                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22896 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22897                     //node.removeAttribute(n);
22898                     return true;
22899                 }
22900                 //Roo.log()
22901                 // only allow 'c whitelisted system attributes'
22902                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22903 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22904                     //node.removeAttribute(n);
22905                     return true;
22906                 }
22907                 
22908                 
22909                  
22910                 
22911                 clean.push(p);
22912                 return true;
22913             });
22914             if (clean.length) { 
22915                 node.setAttribute(n, clean.join(';'));
22916             } else {
22917                 node.removeAttribute(n);
22918             }
22919             
22920         }
22921         
22922         
22923         for (var i = node.attributes.length-1; i > -1 ; i--) {
22924             var a = node.attributes[i];
22925             //console.log(a);
22926             
22927             if (a.name.toLowerCase().substr(0,2)=='on')  {
22928                 node.removeAttribute(a.name);
22929                 continue;
22930             }
22931             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22932                 node.removeAttribute(a.name);
22933                 continue;
22934             }
22935             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22936                 cleanAttr(a.name,a.value); // fixme..
22937                 continue;
22938             }
22939             if (a.name == 'style') {
22940                 cleanStyle(a.name,a.value);
22941                 continue;
22942             }
22943             /// clean up MS crap..
22944             // tecnically this should be a list of valid class'es..
22945             
22946             
22947             if (a.name == 'class') {
22948                 if (a.value.match(/^Mso/)) {
22949                     node.className = '';
22950                 }
22951                 
22952                 if (a.value.match(/^body$/)) {
22953                     node.className = '';
22954                 }
22955                 continue;
22956             }
22957             
22958             // style cleanup!?
22959             // class cleanup?
22960             
22961         }
22962         
22963         
22964         this.cleanUpChildren(node);
22965         
22966         
22967     },
22968     
22969     /**
22970      * Clean up MS wordisms...
22971      */
22972     cleanWord : function(node)
22973     {
22974         
22975         
22976         if (!node) {
22977             this.cleanWord(this.doc.body);
22978             return;
22979         }
22980         if (node.nodeName == "#text") {
22981             // clean up silly Windows -- stuff?
22982             return; 
22983         }
22984         if (node.nodeName == "#comment") {
22985             node.parentNode.removeChild(node);
22986             // clean up silly Windows -- stuff?
22987             return; 
22988         }
22989         
22990         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22991             node.parentNode.removeChild(node);
22992             return;
22993         }
22994         
22995         // remove - but keep children..
22996         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22997             while (node.childNodes.length) {
22998                 var cn = node.childNodes[0];
22999                 node.removeChild(cn);
23000                 node.parentNode.insertBefore(cn, node);
23001             }
23002             node.parentNode.removeChild(node);
23003             this.iterateChildren(node, this.cleanWord);
23004             return;
23005         }
23006         // clean styles
23007         if (node.className.length) {
23008             
23009             var cn = node.className.split(/\W+/);
23010             var cna = [];
23011             Roo.each(cn, function(cls) {
23012                 if (cls.match(/Mso[a-zA-Z]+/)) {
23013                     return;
23014                 }
23015                 cna.push(cls);
23016             });
23017             node.className = cna.length ? cna.join(' ') : '';
23018             if (!cna.length) {
23019                 node.removeAttribute("class");
23020             }
23021         }
23022         
23023         if (node.hasAttribute("lang")) {
23024             node.removeAttribute("lang");
23025         }
23026         
23027         if (node.hasAttribute("style")) {
23028             
23029             var styles = node.getAttribute("style").split(";");
23030             var nstyle = [];
23031             Roo.each(styles, function(s) {
23032                 if (!s.match(/:/)) {
23033                     return;
23034                 }
23035                 var kv = s.split(":");
23036                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23037                     return;
23038                 }
23039                 // what ever is left... we allow.
23040                 nstyle.push(s);
23041             });
23042             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23043             if (!nstyle.length) {
23044                 node.removeAttribute('style');
23045             }
23046         }
23047         this.iterateChildren(node, this.cleanWord);
23048         
23049         
23050         
23051     },
23052     /**
23053      * iterateChildren of a Node, calling fn each time, using this as the scole..
23054      * @param {DomNode} node node to iterate children of.
23055      * @param {Function} fn method of this class to call on each item.
23056      */
23057     iterateChildren : function(node, fn)
23058     {
23059         if (!node.childNodes.length) {
23060                 return;
23061         }
23062         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23063            fn.call(this, node.childNodes[i])
23064         }
23065     },
23066     
23067     
23068     /**
23069      * cleanTableWidths.
23070      *
23071      * Quite often pasting from word etc.. results in tables with column and widths.
23072      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23073      *
23074      */
23075     cleanTableWidths : function(node)
23076     {
23077          
23078          
23079         if (!node) {
23080             this.cleanTableWidths(this.doc.body);
23081             return;
23082         }
23083         
23084         // ignore list...
23085         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23086             return; 
23087         }
23088         Roo.log(node.tagName);
23089         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23090             this.iterateChildren(node, this.cleanTableWidths);
23091             return;
23092         }
23093         if (node.hasAttribute('width')) {
23094             node.removeAttribute('width');
23095         }
23096         
23097          
23098         if (node.hasAttribute("style")) {
23099             // pretty basic...
23100             
23101             var styles = node.getAttribute("style").split(";");
23102             var nstyle = [];
23103             Roo.each(styles, function(s) {
23104                 if (!s.match(/:/)) {
23105                     return;
23106                 }
23107                 var kv = s.split(":");
23108                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23109                     return;
23110                 }
23111                 // what ever is left... we allow.
23112                 nstyle.push(s);
23113             });
23114             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23115             if (!nstyle.length) {
23116                 node.removeAttribute('style');
23117             }
23118         }
23119         
23120         this.iterateChildren(node, this.cleanTableWidths);
23121         
23122         
23123     },
23124     
23125     
23126     
23127     
23128     domToHTML : function(currentElement, depth, nopadtext) {
23129         
23130         depth = depth || 0;
23131         nopadtext = nopadtext || false;
23132     
23133         if (!currentElement) {
23134             return this.domToHTML(this.doc.body);
23135         }
23136         
23137         //Roo.log(currentElement);
23138         var j;
23139         var allText = false;
23140         var nodeName = currentElement.nodeName;
23141         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23142         
23143         if  (nodeName == '#text') {
23144             
23145             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23146         }
23147         
23148         
23149         var ret = '';
23150         if (nodeName != 'BODY') {
23151              
23152             var i = 0;
23153             // Prints the node tagName, such as <A>, <IMG>, etc
23154             if (tagName) {
23155                 var attr = [];
23156                 for(i = 0; i < currentElement.attributes.length;i++) {
23157                     // quoting?
23158                     var aname = currentElement.attributes.item(i).name;
23159                     if (!currentElement.attributes.item(i).value.length) {
23160                         continue;
23161                     }
23162                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23163                 }
23164                 
23165                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23166             } 
23167             else {
23168                 
23169                 // eack
23170             }
23171         } else {
23172             tagName = false;
23173         }
23174         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23175             return ret;
23176         }
23177         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23178             nopadtext = true;
23179         }
23180         
23181         
23182         // Traverse the tree
23183         i = 0;
23184         var currentElementChild = currentElement.childNodes.item(i);
23185         var allText = true;
23186         var innerHTML  = '';
23187         lastnode = '';
23188         while (currentElementChild) {
23189             // Formatting code (indent the tree so it looks nice on the screen)
23190             var nopad = nopadtext;
23191             if (lastnode == 'SPAN') {
23192                 nopad  = true;
23193             }
23194             // text
23195             if  (currentElementChild.nodeName == '#text') {
23196                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23197                 toadd = nopadtext ? toadd : toadd.trim();
23198                 if (!nopad && toadd.length > 80) {
23199                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23200                 }
23201                 innerHTML  += toadd;
23202                 
23203                 i++;
23204                 currentElementChild = currentElement.childNodes.item(i);
23205                 lastNode = '';
23206                 continue;
23207             }
23208             allText = false;
23209             
23210             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23211                 
23212             // Recursively traverse the tree structure of the child node
23213             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23214             lastnode = currentElementChild.nodeName;
23215             i++;
23216             currentElementChild=currentElement.childNodes.item(i);
23217         }
23218         
23219         ret += innerHTML;
23220         
23221         if (!allText) {
23222                 // The remaining code is mostly for formatting the tree
23223             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23224         }
23225         
23226         
23227         if (tagName) {
23228             ret+= "</"+tagName+">";
23229         }
23230         return ret;
23231         
23232     },
23233         
23234     applyBlacklists : function()
23235     {
23236         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23237         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23238         
23239         this.white = [];
23240         this.black = [];
23241         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23242             if (b.indexOf(tag) > -1) {
23243                 return;
23244             }
23245             this.white.push(tag);
23246             
23247         }, this);
23248         
23249         Roo.each(w, function(tag) {
23250             if (b.indexOf(tag) > -1) {
23251                 return;
23252             }
23253             if (this.white.indexOf(tag) > -1) {
23254                 return;
23255             }
23256             this.white.push(tag);
23257             
23258         }, this);
23259         
23260         
23261         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23262             if (w.indexOf(tag) > -1) {
23263                 return;
23264             }
23265             this.black.push(tag);
23266             
23267         }, this);
23268         
23269         Roo.each(b, function(tag) {
23270             if (w.indexOf(tag) > -1) {
23271                 return;
23272             }
23273             if (this.black.indexOf(tag) > -1) {
23274                 return;
23275             }
23276             this.black.push(tag);
23277             
23278         }, this);
23279         
23280         
23281         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23282         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23283         
23284         this.cwhite = [];
23285         this.cblack = [];
23286         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23287             if (b.indexOf(tag) > -1) {
23288                 return;
23289             }
23290             this.cwhite.push(tag);
23291             
23292         }, this);
23293         
23294         Roo.each(w, function(tag) {
23295             if (b.indexOf(tag) > -1) {
23296                 return;
23297             }
23298             if (this.cwhite.indexOf(tag) > -1) {
23299                 return;
23300             }
23301             this.cwhite.push(tag);
23302             
23303         }, this);
23304         
23305         
23306         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23307             if (w.indexOf(tag) > -1) {
23308                 return;
23309             }
23310             this.cblack.push(tag);
23311             
23312         }, this);
23313         
23314         Roo.each(b, function(tag) {
23315             if (w.indexOf(tag) > -1) {
23316                 return;
23317             }
23318             if (this.cblack.indexOf(tag) > -1) {
23319                 return;
23320             }
23321             this.cblack.push(tag);
23322             
23323         }, this);
23324     },
23325     
23326     setStylesheets : function(stylesheets)
23327     {
23328         if(typeof(stylesheets) == 'string'){
23329             Roo.get(this.iframe.contentDocument.head).createChild({
23330                 tag : 'link',
23331                 rel : 'stylesheet',
23332                 type : 'text/css',
23333                 href : stylesheets
23334             });
23335             
23336             return;
23337         }
23338         var _this = this;
23339      
23340         Roo.each(stylesheets, function(s) {
23341             if(!s.length){
23342                 return;
23343             }
23344             
23345             Roo.get(_this.iframe.contentDocument.head).createChild({
23346                 tag : 'link',
23347                 rel : 'stylesheet',
23348                 type : 'text/css',
23349                 href : s
23350             });
23351         });
23352
23353         
23354     },
23355     
23356     removeStylesheets : function()
23357     {
23358         var _this = this;
23359         
23360         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23361             s.remove();
23362         });
23363     },
23364     
23365     setStyle : function(style)
23366     {
23367         Roo.get(this.iframe.contentDocument.head).createChild({
23368             tag : 'style',
23369             type : 'text/css',
23370             html : style
23371         });
23372
23373         return;
23374     }
23375     
23376     // hide stuff that is not compatible
23377     /**
23378      * @event blur
23379      * @hide
23380      */
23381     /**
23382      * @event change
23383      * @hide
23384      */
23385     /**
23386      * @event focus
23387      * @hide
23388      */
23389     /**
23390      * @event specialkey
23391      * @hide
23392      */
23393     /**
23394      * @cfg {String} fieldClass @hide
23395      */
23396     /**
23397      * @cfg {String} focusClass @hide
23398      */
23399     /**
23400      * @cfg {String} autoCreate @hide
23401      */
23402     /**
23403      * @cfg {String} inputType @hide
23404      */
23405     /**
23406      * @cfg {String} invalidClass @hide
23407      */
23408     /**
23409      * @cfg {String} invalidText @hide
23410      */
23411     /**
23412      * @cfg {String} msgFx @hide
23413      */
23414     /**
23415      * @cfg {String} validateOnBlur @hide
23416      */
23417 });
23418
23419 Roo.HtmlEditorCore.white = [
23420         'area', 'br', 'img', 'input', 'hr', 'wbr',
23421         
23422        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23423        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23424        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23425        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23426        'table',   'ul',         'xmp', 
23427        
23428        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23429       'thead',   'tr', 
23430      
23431       'dir', 'menu', 'ol', 'ul', 'dl',
23432        
23433       'embed',  'object'
23434 ];
23435
23436
23437 Roo.HtmlEditorCore.black = [
23438     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23439         'applet', // 
23440         'base',   'basefont', 'bgsound', 'blink',  'body', 
23441         'frame',  'frameset', 'head',    'html',   'ilayer', 
23442         'iframe', 'layer',  'link',     'meta',    'object',   
23443         'script', 'style' ,'title',  'xml' // clean later..
23444 ];
23445 Roo.HtmlEditorCore.clean = [
23446     'script', 'style', 'title', 'xml'
23447 ];
23448 Roo.HtmlEditorCore.remove = [
23449     'font'
23450 ];
23451 // attributes..
23452
23453 Roo.HtmlEditorCore.ablack = [
23454     'on'
23455 ];
23456     
23457 Roo.HtmlEditorCore.aclean = [ 
23458     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23459 ];
23460
23461 // protocols..
23462 Roo.HtmlEditorCore.pwhite= [
23463         'http',  'https',  'mailto'
23464 ];
23465
23466 // white listed style attributes.
23467 Roo.HtmlEditorCore.cwhite= [
23468       //  'text-align', /// default is to allow most things..
23469       
23470          
23471 //        'font-size'//??
23472 ];
23473
23474 // black listed style attributes.
23475 Roo.HtmlEditorCore.cblack= [
23476       //  'font-size' -- this can be set by the project 
23477 ];
23478
23479
23480 Roo.HtmlEditorCore.swapCodes   =[ 
23481     [    8211, "--" ], 
23482     [    8212, "--" ], 
23483     [    8216,  "'" ],  
23484     [    8217, "'" ],  
23485     [    8220, '"' ],  
23486     [    8221, '"' ],  
23487     [    8226, "*" ],  
23488     [    8230, "..." ]
23489 ]; 
23490
23491     /*
23492  * - LGPL
23493  *
23494  * HtmlEditor
23495  * 
23496  */
23497
23498 /**
23499  * @class Roo.bootstrap.HtmlEditor
23500  * @extends Roo.bootstrap.TextArea
23501  * Bootstrap HtmlEditor class
23502
23503  * @constructor
23504  * Create a new HtmlEditor
23505  * @param {Object} config The config object
23506  */
23507
23508 Roo.bootstrap.HtmlEditor = function(config){
23509     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23510     if (!this.toolbars) {
23511         this.toolbars = [];
23512     }
23513     
23514     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23515     this.addEvents({
23516             /**
23517              * @event initialize
23518              * Fires when the editor is fully initialized (including the iframe)
23519              * @param {HtmlEditor} this
23520              */
23521             initialize: true,
23522             /**
23523              * @event activate
23524              * Fires when the editor is first receives the focus. Any insertion must wait
23525              * until after this event.
23526              * @param {HtmlEditor} this
23527              */
23528             activate: true,
23529              /**
23530              * @event beforesync
23531              * Fires before the textarea is updated with content from the editor iframe. Return false
23532              * to cancel the sync.
23533              * @param {HtmlEditor} this
23534              * @param {String} html
23535              */
23536             beforesync: true,
23537              /**
23538              * @event beforepush
23539              * Fires before the iframe editor is updated with content from the textarea. Return false
23540              * to cancel the push.
23541              * @param {HtmlEditor} this
23542              * @param {String} html
23543              */
23544             beforepush: true,
23545              /**
23546              * @event sync
23547              * Fires when the textarea is updated with content from the editor iframe.
23548              * @param {HtmlEditor} this
23549              * @param {String} html
23550              */
23551             sync: true,
23552              /**
23553              * @event push
23554              * Fires when the iframe editor is updated with content from the textarea.
23555              * @param {HtmlEditor} this
23556              * @param {String} html
23557              */
23558             push: true,
23559              /**
23560              * @event editmodechange
23561              * Fires when the editor switches edit modes
23562              * @param {HtmlEditor} this
23563              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23564              */
23565             editmodechange: true,
23566             /**
23567              * @event editorevent
23568              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23569              * @param {HtmlEditor} this
23570              */
23571             editorevent: true,
23572             /**
23573              * @event firstfocus
23574              * Fires when on first focus - needed by toolbars..
23575              * @param {HtmlEditor} this
23576              */
23577             firstfocus: true,
23578             /**
23579              * @event autosave
23580              * Auto save the htmlEditor value as a file into Events
23581              * @param {HtmlEditor} this
23582              */
23583             autosave: true,
23584             /**
23585              * @event savedpreview
23586              * preview the saved version of htmlEditor
23587              * @param {HtmlEditor} this
23588              */
23589             savedpreview: true
23590         });
23591 };
23592
23593
23594 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23595     
23596     
23597       /**
23598      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23599      */
23600     toolbars : false,
23601     
23602      /**
23603     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23604     */
23605     btns : [],
23606    
23607      /**
23608      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23609      *                        Roo.resizable.
23610      */
23611     resizable : false,
23612      /**
23613      * @cfg {Number} height (in pixels)
23614      */   
23615     height: 300,
23616    /**
23617      * @cfg {Number} width (in pixels)
23618      */   
23619     width: false,
23620     
23621     /**
23622      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23623      * 
23624      */
23625     stylesheets: false,
23626     
23627     // id of frame..
23628     frameId: false,
23629     
23630     // private properties
23631     validationEvent : false,
23632     deferHeight: true,
23633     initialized : false,
23634     activated : false,
23635     
23636     onFocus : Roo.emptyFn,
23637     iframePad:3,
23638     hideMode:'offsets',
23639     
23640     tbContainer : false,
23641     
23642     bodyCls : '',
23643     
23644     toolbarContainer :function() {
23645         return this.wrap.select('.x-html-editor-tb',true).first();
23646     },
23647
23648     /**
23649      * Protected method that will not generally be called directly. It
23650      * is called when the editor creates its toolbar. Override this method if you need to
23651      * add custom toolbar buttons.
23652      * @param {HtmlEditor} editor
23653      */
23654     createToolbar : function(){
23655         Roo.log('renewing');
23656         Roo.log("create toolbars");
23657         
23658         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23659         this.toolbars[0].render(this.toolbarContainer());
23660         
23661         return;
23662         
23663 //        if (!editor.toolbars || !editor.toolbars.length) {
23664 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23665 //        }
23666 //        
23667 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23668 //            editor.toolbars[i] = Roo.factory(
23669 //                    typeof(editor.toolbars[i]) == 'string' ?
23670 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23671 //                Roo.bootstrap.HtmlEditor);
23672 //            editor.toolbars[i].init(editor);
23673 //        }
23674     },
23675
23676      
23677     // private
23678     onRender : function(ct, position)
23679     {
23680        // Roo.log("Call onRender: " + this.xtype);
23681         var _t = this;
23682         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23683       
23684         this.wrap = this.inputEl().wrap({
23685             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23686         });
23687         
23688         this.editorcore.onRender(ct, position);
23689          
23690         if (this.resizable) {
23691             this.resizeEl = new Roo.Resizable(this.wrap, {
23692                 pinned : true,
23693                 wrap: true,
23694                 dynamic : true,
23695                 minHeight : this.height,
23696                 height: this.height,
23697                 handles : this.resizable,
23698                 width: this.width,
23699                 listeners : {
23700                     resize : function(r, w, h) {
23701                         _t.onResize(w,h); // -something
23702                     }
23703                 }
23704             });
23705             
23706         }
23707         this.createToolbar(this);
23708        
23709         
23710         if(!this.width && this.resizable){
23711             this.setSize(this.wrap.getSize());
23712         }
23713         if (this.resizeEl) {
23714             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23715             // should trigger onReize..
23716         }
23717         
23718     },
23719
23720     // private
23721     onResize : function(w, h)
23722     {
23723         Roo.log('resize: ' +w + ',' + h );
23724         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23725         var ew = false;
23726         var eh = false;
23727         
23728         if(this.inputEl() ){
23729             if(typeof w == 'number'){
23730                 var aw = w - this.wrap.getFrameWidth('lr');
23731                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23732                 ew = aw;
23733             }
23734             if(typeof h == 'number'){
23735                  var tbh = -11;  // fixme it needs to tool bar size!
23736                 for (var i =0; i < this.toolbars.length;i++) {
23737                     // fixme - ask toolbars for heights?
23738                     tbh += this.toolbars[i].el.getHeight();
23739                     //if (this.toolbars[i].footer) {
23740                     //    tbh += this.toolbars[i].footer.el.getHeight();
23741                     //}
23742                 }
23743               
23744                 
23745                 
23746                 
23747                 
23748                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23749                 ah -= 5; // knock a few pixes off for look..
23750                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23751                 var eh = ah;
23752             }
23753         }
23754         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23755         this.editorcore.onResize(ew,eh);
23756         
23757     },
23758
23759     /**
23760      * Toggles the editor between standard and source edit mode.
23761      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23762      */
23763     toggleSourceEdit : function(sourceEditMode)
23764     {
23765         this.editorcore.toggleSourceEdit(sourceEditMode);
23766         
23767         if(this.editorcore.sourceEditMode){
23768             Roo.log('editor - showing textarea');
23769             
23770 //            Roo.log('in');
23771 //            Roo.log(this.syncValue());
23772             this.syncValue();
23773             this.inputEl().removeClass(['hide', 'x-hidden']);
23774             this.inputEl().dom.removeAttribute('tabIndex');
23775             this.inputEl().focus();
23776         }else{
23777             Roo.log('editor - hiding textarea');
23778 //            Roo.log('out')
23779 //            Roo.log(this.pushValue()); 
23780             this.pushValue();
23781             
23782             this.inputEl().addClass(['hide', 'x-hidden']);
23783             this.inputEl().dom.setAttribute('tabIndex', -1);
23784             //this.deferFocus();
23785         }
23786          
23787         if(this.resizable){
23788             this.setSize(this.wrap.getSize());
23789         }
23790         
23791         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23792     },
23793  
23794     // private (for BoxComponent)
23795     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23796
23797     // private (for BoxComponent)
23798     getResizeEl : function(){
23799         return this.wrap;
23800     },
23801
23802     // private (for BoxComponent)
23803     getPositionEl : function(){
23804         return this.wrap;
23805     },
23806
23807     // private
23808     initEvents : function(){
23809         this.originalValue = this.getValue();
23810     },
23811
23812 //    /**
23813 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23814 //     * @method
23815 //     */
23816 //    markInvalid : Roo.emptyFn,
23817 //    /**
23818 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23819 //     * @method
23820 //     */
23821 //    clearInvalid : Roo.emptyFn,
23822
23823     setValue : function(v){
23824         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23825         this.editorcore.pushValue();
23826     },
23827
23828      
23829     // private
23830     deferFocus : function(){
23831         this.focus.defer(10, this);
23832     },
23833
23834     // doc'ed in Field
23835     focus : function(){
23836         this.editorcore.focus();
23837         
23838     },
23839       
23840
23841     // private
23842     onDestroy : function(){
23843         
23844         
23845         
23846         if(this.rendered){
23847             
23848             for (var i =0; i < this.toolbars.length;i++) {
23849                 // fixme - ask toolbars for heights?
23850                 this.toolbars[i].onDestroy();
23851             }
23852             
23853             this.wrap.dom.innerHTML = '';
23854             this.wrap.remove();
23855         }
23856     },
23857
23858     // private
23859     onFirstFocus : function(){
23860         //Roo.log("onFirstFocus");
23861         this.editorcore.onFirstFocus();
23862          for (var i =0; i < this.toolbars.length;i++) {
23863             this.toolbars[i].onFirstFocus();
23864         }
23865         
23866     },
23867     
23868     // private
23869     syncValue : function()
23870     {   
23871         this.editorcore.syncValue();
23872     },
23873     
23874     pushValue : function()
23875     {   
23876         this.editorcore.pushValue();
23877     }
23878      
23879     
23880     // hide stuff that is not compatible
23881     /**
23882      * @event blur
23883      * @hide
23884      */
23885     /**
23886      * @event change
23887      * @hide
23888      */
23889     /**
23890      * @event focus
23891      * @hide
23892      */
23893     /**
23894      * @event specialkey
23895      * @hide
23896      */
23897     /**
23898      * @cfg {String} fieldClass @hide
23899      */
23900     /**
23901      * @cfg {String} focusClass @hide
23902      */
23903     /**
23904      * @cfg {String} autoCreate @hide
23905      */
23906     /**
23907      * @cfg {String} inputType @hide
23908      */
23909     /**
23910      * @cfg {String} invalidClass @hide
23911      */
23912     /**
23913      * @cfg {String} invalidText @hide
23914      */
23915     /**
23916      * @cfg {String} msgFx @hide
23917      */
23918     /**
23919      * @cfg {String} validateOnBlur @hide
23920      */
23921 });
23922  
23923     
23924    
23925    
23926    
23927       
23928 Roo.namespace('Roo.bootstrap.htmleditor');
23929 /**
23930  * @class Roo.bootstrap.HtmlEditorToolbar1
23931  * Basic Toolbar
23932  * 
23933  * Usage:
23934  *
23935  new Roo.bootstrap.HtmlEditor({
23936     ....
23937     toolbars : [
23938         new Roo.bootstrap.HtmlEditorToolbar1({
23939             disable : { fonts: 1 , format: 1, ..., ... , ...],
23940             btns : [ .... ]
23941         })
23942     }
23943      
23944  * 
23945  * @cfg {Object} disable List of elements to disable..
23946  * @cfg {Array} btns List of additional buttons.
23947  * 
23948  * 
23949  * NEEDS Extra CSS? 
23950  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23951  */
23952  
23953 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23954 {
23955     
23956     Roo.apply(this, config);
23957     
23958     // default disabled, based on 'good practice'..
23959     this.disable = this.disable || {};
23960     Roo.applyIf(this.disable, {
23961         fontSize : true,
23962         colors : true,
23963         specialElements : true
23964     });
23965     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23966     
23967     this.editor = config.editor;
23968     this.editorcore = config.editor.editorcore;
23969     
23970     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23971     
23972     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23973     // dont call parent... till later.
23974 }
23975 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23976      
23977     bar : true,
23978     
23979     editor : false,
23980     editorcore : false,
23981     
23982     
23983     formats : [
23984         "p" ,  
23985         "h1","h2","h3","h4","h5","h6", 
23986         "pre", "code", 
23987         "abbr", "acronym", "address", "cite", "samp", "var",
23988         'div','span'
23989     ],
23990     
23991     onRender : function(ct, position)
23992     {
23993        // Roo.log("Call onRender: " + this.xtype);
23994         
23995        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23996        Roo.log(this.el);
23997        this.el.dom.style.marginBottom = '0';
23998        var _this = this;
23999        var editorcore = this.editorcore;
24000        var editor= this.editor;
24001        
24002        var children = [];
24003        var btn = function(id,cmd , toggle, handler, html){
24004        
24005             var  event = toggle ? 'toggle' : 'click';
24006        
24007             var a = {
24008                 size : 'sm',
24009                 xtype: 'Button',
24010                 xns: Roo.bootstrap,
24011                 //glyphicon : id,
24012                 fa: id,
24013                 cmd : id || cmd,
24014                 enableToggle:toggle !== false,
24015                 html : html || '',
24016                 pressed : toggle ? false : null,
24017                 listeners : {}
24018             };
24019             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24020                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24021             };
24022             children.push(a);
24023             return a;
24024        }
24025        
24026     //    var cb_box = function...
24027         
24028         var style = {
24029                 xtype: 'Button',
24030                 size : 'sm',
24031                 xns: Roo.bootstrap,
24032                 fa : 'font',
24033                 //html : 'submit'
24034                 menu : {
24035                     xtype: 'Menu',
24036                     xns: Roo.bootstrap,
24037                     items:  []
24038                 }
24039         };
24040         Roo.each(this.formats, function(f) {
24041             style.menu.items.push({
24042                 xtype :'MenuItem',
24043                 xns: Roo.bootstrap,
24044                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24045                 tagname : f,
24046                 listeners : {
24047                     click : function()
24048                     {
24049                         editorcore.insertTag(this.tagname);
24050                         editor.focus();
24051                     }
24052                 }
24053                 
24054             });
24055         });
24056         children.push(style);   
24057         
24058         btn('bold',false,true);
24059         btn('italic',false,true);
24060         btn('align-left', 'justifyleft',true);
24061         btn('align-center', 'justifycenter',true);
24062         btn('align-right' , 'justifyright',true);
24063         btn('link', false, false, function(btn) {
24064             //Roo.log("create link?");
24065             var url = prompt(this.createLinkText, this.defaultLinkValue);
24066             if(url && url != 'http:/'+'/'){
24067                 this.editorcore.relayCmd('createlink', url);
24068             }
24069         }),
24070         btn('list','insertunorderedlist',true);
24071         btn('pencil', false,true, function(btn){
24072                 Roo.log(this);
24073                 this.toggleSourceEdit(btn.pressed);
24074         });
24075         
24076         if (this.editor.btns.length > 0) {
24077             for (var i = 0; i<this.editor.btns.length; i++) {
24078                 children.push(this.editor.btns[i]);
24079             }
24080         }
24081         
24082         /*
24083         var cog = {
24084                 xtype: 'Button',
24085                 size : 'sm',
24086                 xns: Roo.bootstrap,
24087                 glyphicon : 'cog',
24088                 //html : 'submit'
24089                 menu : {
24090                     xtype: 'Menu',
24091                     xns: Roo.bootstrap,
24092                     items:  []
24093                 }
24094         };
24095         
24096         cog.menu.items.push({
24097             xtype :'MenuItem',
24098             xns: Roo.bootstrap,
24099             html : Clean styles,
24100             tagname : f,
24101             listeners : {
24102                 click : function()
24103                 {
24104                     editorcore.insertTag(this.tagname);
24105                     editor.focus();
24106                 }
24107             }
24108             
24109         });
24110        */
24111         
24112          
24113        this.xtype = 'NavSimplebar';
24114         
24115         for(var i=0;i< children.length;i++) {
24116             
24117             this.buttons.add(this.addxtypeChild(children[i]));
24118             
24119         }
24120         
24121         editor.on('editorevent', this.updateToolbar, this);
24122     },
24123     onBtnClick : function(id)
24124     {
24125        this.editorcore.relayCmd(id);
24126        this.editorcore.focus();
24127     },
24128     
24129     /**
24130      * Protected method that will not generally be called directly. It triggers
24131      * a toolbar update by reading the markup state of the current selection in the editor.
24132      */
24133     updateToolbar: function(){
24134
24135         if(!this.editorcore.activated){
24136             this.editor.onFirstFocus(); // is this neeed?
24137             return;
24138         }
24139
24140         var btns = this.buttons; 
24141         var doc = this.editorcore.doc;
24142         btns.get('bold').setActive(doc.queryCommandState('bold'));
24143         btns.get('italic').setActive(doc.queryCommandState('italic'));
24144         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24145         
24146         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24147         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24148         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24149         
24150         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24151         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24152          /*
24153         
24154         var ans = this.editorcore.getAllAncestors();
24155         if (this.formatCombo) {
24156             
24157             
24158             var store = this.formatCombo.store;
24159             this.formatCombo.setValue("");
24160             for (var i =0; i < ans.length;i++) {
24161                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24162                     // select it..
24163                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24164                     break;
24165                 }
24166             }
24167         }
24168         
24169         
24170         
24171         // hides menus... - so this cant be on a menu...
24172         Roo.bootstrap.MenuMgr.hideAll();
24173         */
24174         Roo.bootstrap.MenuMgr.hideAll();
24175         //this.editorsyncValue();
24176     },
24177     onFirstFocus: function() {
24178         this.buttons.each(function(item){
24179            item.enable();
24180         });
24181     },
24182     toggleSourceEdit : function(sourceEditMode){
24183         
24184           
24185         if(sourceEditMode){
24186             Roo.log("disabling buttons");
24187            this.buttons.each( function(item){
24188                 if(item.cmd != 'pencil'){
24189                     item.disable();
24190                 }
24191             });
24192           
24193         }else{
24194             Roo.log("enabling buttons");
24195             if(this.editorcore.initialized){
24196                 this.buttons.each( function(item){
24197                     item.enable();
24198                 });
24199             }
24200             
24201         }
24202         Roo.log("calling toggole on editor");
24203         // tell the editor that it's been pressed..
24204         this.editor.toggleSourceEdit(sourceEditMode);
24205        
24206     }
24207 });
24208
24209
24210
24211
24212
24213 /**
24214  * @class Roo.bootstrap.Table.AbstractSelectionModel
24215  * @extends Roo.util.Observable
24216  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24217  * implemented by descendant classes.  This class should not be directly instantiated.
24218  * @constructor
24219  */
24220 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24221     this.locked = false;
24222     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24223 };
24224
24225
24226 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24227     /** @ignore Called by the grid automatically. Do not call directly. */
24228     init : function(grid){
24229         this.grid = grid;
24230         this.initEvents();
24231     },
24232
24233     /**
24234      * Locks the selections.
24235      */
24236     lock : function(){
24237         this.locked = true;
24238     },
24239
24240     /**
24241      * Unlocks the selections.
24242      */
24243     unlock : function(){
24244         this.locked = false;
24245     },
24246
24247     /**
24248      * Returns true if the selections are locked.
24249      * @return {Boolean}
24250      */
24251     isLocked : function(){
24252         return this.locked;
24253     }
24254 });
24255 /**
24256  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24257  * @class Roo.bootstrap.Table.RowSelectionModel
24258  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24259  * It supports multiple selections and keyboard selection/navigation. 
24260  * @constructor
24261  * @param {Object} config
24262  */
24263
24264 Roo.bootstrap.Table.RowSelectionModel = function(config){
24265     Roo.apply(this, config);
24266     this.selections = new Roo.util.MixedCollection(false, function(o){
24267         return o.id;
24268     });
24269
24270     this.last = false;
24271     this.lastActive = false;
24272
24273     this.addEvents({
24274         /**
24275              * @event selectionchange
24276              * Fires when the selection changes
24277              * @param {SelectionModel} this
24278              */
24279             "selectionchange" : true,
24280         /**
24281              * @event afterselectionchange
24282              * Fires after the selection changes (eg. by key press or clicking)
24283              * @param {SelectionModel} this
24284              */
24285             "afterselectionchange" : true,
24286         /**
24287              * @event beforerowselect
24288              * Fires when a row is selected being selected, return false to cancel.
24289              * @param {SelectionModel} this
24290              * @param {Number} rowIndex The selected index
24291              * @param {Boolean} keepExisting False if other selections will be cleared
24292              */
24293             "beforerowselect" : true,
24294         /**
24295              * @event rowselect
24296              * Fires when a row is selected.
24297              * @param {SelectionModel} this
24298              * @param {Number} rowIndex The selected index
24299              * @param {Roo.data.Record} r The record
24300              */
24301             "rowselect" : true,
24302         /**
24303              * @event rowdeselect
24304              * Fires when a row is deselected.
24305              * @param {SelectionModel} this
24306              * @param {Number} rowIndex The selected index
24307              */
24308         "rowdeselect" : true
24309     });
24310     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24311     this.locked = false;
24312  };
24313
24314 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24315     /**
24316      * @cfg {Boolean} singleSelect
24317      * True to allow selection of only one row at a time (defaults to false)
24318      */
24319     singleSelect : false,
24320
24321     // private
24322     initEvents : function()
24323     {
24324
24325         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24326         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24327         //}else{ // allow click to work like normal
24328          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24329         //}
24330         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24331         this.grid.on("rowclick", this.handleMouseDown, this);
24332         
24333         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24334             "up" : function(e){
24335                 if(!e.shiftKey){
24336                     this.selectPrevious(e.shiftKey);
24337                 }else if(this.last !== false && this.lastActive !== false){
24338                     var last = this.last;
24339                     this.selectRange(this.last,  this.lastActive-1);
24340                     this.grid.getView().focusRow(this.lastActive);
24341                     if(last !== false){
24342                         this.last = last;
24343                     }
24344                 }else{
24345                     this.selectFirstRow();
24346                 }
24347                 this.fireEvent("afterselectionchange", this);
24348             },
24349             "down" : function(e){
24350                 if(!e.shiftKey){
24351                     this.selectNext(e.shiftKey);
24352                 }else if(this.last !== false && this.lastActive !== false){
24353                     var last = this.last;
24354                     this.selectRange(this.last,  this.lastActive+1);
24355                     this.grid.getView().focusRow(this.lastActive);
24356                     if(last !== false){
24357                         this.last = last;
24358                     }
24359                 }else{
24360                     this.selectFirstRow();
24361                 }
24362                 this.fireEvent("afterselectionchange", this);
24363             },
24364             scope: this
24365         });
24366         this.grid.store.on('load', function(){
24367             this.selections.clear();
24368         },this);
24369         /*
24370         var view = this.grid.view;
24371         view.on("refresh", this.onRefresh, this);
24372         view.on("rowupdated", this.onRowUpdated, this);
24373         view.on("rowremoved", this.onRemove, this);
24374         */
24375     },
24376
24377     // private
24378     onRefresh : function()
24379     {
24380         var ds = this.grid.store, i, v = this.grid.view;
24381         var s = this.selections;
24382         s.each(function(r){
24383             if((i = ds.indexOfId(r.id)) != -1){
24384                 v.onRowSelect(i);
24385             }else{
24386                 s.remove(r);
24387             }
24388         });
24389     },
24390
24391     // private
24392     onRemove : function(v, index, r){
24393         this.selections.remove(r);
24394     },
24395
24396     // private
24397     onRowUpdated : function(v, index, r){
24398         if(this.isSelected(r)){
24399             v.onRowSelect(index);
24400         }
24401     },
24402
24403     /**
24404      * Select records.
24405      * @param {Array} records The records to select
24406      * @param {Boolean} keepExisting (optional) True to keep existing selections
24407      */
24408     selectRecords : function(records, keepExisting)
24409     {
24410         if(!keepExisting){
24411             this.clearSelections();
24412         }
24413             var ds = this.grid.store;
24414         for(var i = 0, len = records.length; i < len; i++){
24415             this.selectRow(ds.indexOf(records[i]), true);
24416         }
24417     },
24418
24419     /**
24420      * Gets the number of selected rows.
24421      * @return {Number}
24422      */
24423     getCount : function(){
24424         return this.selections.length;
24425     },
24426
24427     /**
24428      * Selects the first row in the grid.
24429      */
24430     selectFirstRow : function(){
24431         this.selectRow(0);
24432     },
24433
24434     /**
24435      * Select the last row.
24436      * @param {Boolean} keepExisting (optional) True to keep existing selections
24437      */
24438     selectLastRow : function(keepExisting){
24439         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24440         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24441     },
24442
24443     /**
24444      * Selects the row immediately following the last selected row.
24445      * @param {Boolean} keepExisting (optional) True to keep existing selections
24446      */
24447     selectNext : function(keepExisting)
24448     {
24449             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24450             this.selectRow(this.last+1, keepExisting);
24451             this.grid.getView().focusRow(this.last);
24452         }
24453     },
24454
24455     /**
24456      * Selects the row that precedes the last selected row.
24457      * @param {Boolean} keepExisting (optional) True to keep existing selections
24458      */
24459     selectPrevious : function(keepExisting){
24460         if(this.last){
24461             this.selectRow(this.last-1, keepExisting);
24462             this.grid.getView().focusRow(this.last);
24463         }
24464     },
24465
24466     /**
24467      * Returns the selected records
24468      * @return {Array} Array of selected records
24469      */
24470     getSelections : function(){
24471         return [].concat(this.selections.items);
24472     },
24473
24474     /**
24475      * Returns the first selected record.
24476      * @return {Record}
24477      */
24478     getSelected : function(){
24479         return this.selections.itemAt(0);
24480     },
24481
24482
24483     /**
24484      * Clears all selections.
24485      */
24486     clearSelections : function(fast)
24487     {
24488         if(this.locked) {
24489             return;
24490         }
24491         if(fast !== true){
24492                 var ds = this.grid.store;
24493             var s = this.selections;
24494             s.each(function(r){
24495                 this.deselectRow(ds.indexOfId(r.id));
24496             }, this);
24497             s.clear();
24498         }else{
24499             this.selections.clear();
24500         }
24501         this.last = false;
24502     },
24503
24504
24505     /**
24506      * Selects all rows.
24507      */
24508     selectAll : function(){
24509         if(this.locked) {
24510             return;
24511         }
24512         this.selections.clear();
24513         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24514             this.selectRow(i, true);
24515         }
24516     },
24517
24518     /**
24519      * Returns True if there is a selection.
24520      * @return {Boolean}
24521      */
24522     hasSelection : function(){
24523         return this.selections.length > 0;
24524     },
24525
24526     /**
24527      * Returns True if the specified row is selected.
24528      * @param {Number/Record} record The record or index of the record to check
24529      * @return {Boolean}
24530      */
24531     isSelected : function(index){
24532             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24533         return (r && this.selections.key(r.id) ? true : false);
24534     },
24535
24536     /**
24537      * Returns True if the specified record id is selected.
24538      * @param {String} id The id of record to check
24539      * @return {Boolean}
24540      */
24541     isIdSelected : function(id){
24542         return (this.selections.key(id) ? true : false);
24543     },
24544
24545
24546     // private
24547     handleMouseDBClick : function(e, t){
24548         
24549     },
24550     // private
24551     handleMouseDown : function(e, t)
24552     {
24553             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24554         if(this.isLocked() || rowIndex < 0 ){
24555             return;
24556         };
24557         if(e.shiftKey && this.last !== false){
24558             var last = this.last;
24559             this.selectRange(last, rowIndex, e.ctrlKey);
24560             this.last = last; // reset the last
24561             t.focus();
24562     
24563         }else{
24564             var isSelected = this.isSelected(rowIndex);
24565             //Roo.log("select row:" + rowIndex);
24566             if(isSelected){
24567                 this.deselectRow(rowIndex);
24568             } else {
24569                         this.selectRow(rowIndex, true);
24570             }
24571     
24572             /*
24573                 if(e.button !== 0 && isSelected){
24574                 alert('rowIndex 2: ' + rowIndex);
24575                     view.focusRow(rowIndex);
24576                 }else if(e.ctrlKey && isSelected){
24577                     this.deselectRow(rowIndex);
24578                 }else if(!isSelected){
24579                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24580                     view.focusRow(rowIndex);
24581                 }
24582             */
24583         }
24584         this.fireEvent("afterselectionchange", this);
24585     },
24586     // private
24587     handleDragableRowClick :  function(grid, rowIndex, e) 
24588     {
24589         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24590             this.selectRow(rowIndex, false);
24591             grid.view.focusRow(rowIndex);
24592              this.fireEvent("afterselectionchange", this);
24593         }
24594     },
24595     
24596     /**
24597      * Selects multiple rows.
24598      * @param {Array} rows Array of the indexes of the row to select
24599      * @param {Boolean} keepExisting (optional) True to keep existing selections
24600      */
24601     selectRows : function(rows, keepExisting){
24602         if(!keepExisting){
24603             this.clearSelections();
24604         }
24605         for(var i = 0, len = rows.length; i < len; i++){
24606             this.selectRow(rows[i], true);
24607         }
24608     },
24609
24610     /**
24611      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24612      * @param {Number} startRow The index of the first row in the range
24613      * @param {Number} endRow The index of the last row in the range
24614      * @param {Boolean} keepExisting (optional) True to retain existing selections
24615      */
24616     selectRange : function(startRow, endRow, keepExisting){
24617         if(this.locked) {
24618             return;
24619         }
24620         if(!keepExisting){
24621             this.clearSelections();
24622         }
24623         if(startRow <= endRow){
24624             for(var i = startRow; i <= endRow; i++){
24625                 this.selectRow(i, true);
24626             }
24627         }else{
24628             for(var i = startRow; i >= endRow; i--){
24629                 this.selectRow(i, true);
24630             }
24631         }
24632     },
24633
24634     /**
24635      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24636      * @param {Number} startRow The index of the first row in the range
24637      * @param {Number} endRow The index of the last row in the range
24638      */
24639     deselectRange : function(startRow, endRow, preventViewNotify){
24640         if(this.locked) {
24641             return;
24642         }
24643         for(var i = startRow; i <= endRow; i++){
24644             this.deselectRow(i, preventViewNotify);
24645         }
24646     },
24647
24648     /**
24649      * Selects a row.
24650      * @param {Number} row The index of the row to select
24651      * @param {Boolean} keepExisting (optional) True to keep existing selections
24652      */
24653     selectRow : function(index, keepExisting, preventViewNotify)
24654     {
24655             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24656             return;
24657         }
24658         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24659             if(!keepExisting || this.singleSelect){
24660                 this.clearSelections();
24661             }
24662             
24663             var r = this.grid.store.getAt(index);
24664             //console.log('selectRow - record id :' + r.id);
24665             
24666             this.selections.add(r);
24667             this.last = this.lastActive = index;
24668             if(!preventViewNotify){
24669                 var proxy = new Roo.Element(
24670                                 this.grid.getRowDom(index)
24671                 );
24672                 proxy.addClass('bg-info info');
24673             }
24674             this.fireEvent("rowselect", this, index, r);
24675             this.fireEvent("selectionchange", this);
24676         }
24677     },
24678
24679     /**
24680      * Deselects a row.
24681      * @param {Number} row The index of the row to deselect
24682      */
24683     deselectRow : function(index, preventViewNotify)
24684     {
24685         if(this.locked) {
24686             return;
24687         }
24688         if(this.last == index){
24689             this.last = false;
24690         }
24691         if(this.lastActive == index){
24692             this.lastActive = false;
24693         }
24694         
24695         var r = this.grid.store.getAt(index);
24696         if (!r) {
24697             return;
24698         }
24699         
24700         this.selections.remove(r);
24701         //.console.log('deselectRow - record id :' + r.id);
24702         if(!preventViewNotify){
24703         
24704             var proxy = new Roo.Element(
24705                 this.grid.getRowDom(index)
24706             );
24707             proxy.removeClass('bg-info info');
24708         }
24709         this.fireEvent("rowdeselect", this, index);
24710         this.fireEvent("selectionchange", this);
24711     },
24712
24713     // private
24714     restoreLast : function(){
24715         if(this._last){
24716             this.last = this._last;
24717         }
24718     },
24719
24720     // private
24721     acceptsNav : function(row, col, cm){
24722         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24723     },
24724
24725     // private
24726     onEditorKey : function(field, e){
24727         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24728         if(k == e.TAB){
24729             e.stopEvent();
24730             ed.completeEdit();
24731             if(e.shiftKey){
24732                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24733             }else{
24734                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24735             }
24736         }else if(k == e.ENTER && !e.ctrlKey){
24737             e.stopEvent();
24738             ed.completeEdit();
24739             if(e.shiftKey){
24740                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24741             }else{
24742                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24743             }
24744         }else if(k == e.ESC){
24745             ed.cancelEdit();
24746         }
24747         if(newCell){
24748             g.startEditing(newCell[0], newCell[1]);
24749         }
24750     }
24751 });
24752 /*
24753  * Based on:
24754  * Ext JS Library 1.1.1
24755  * Copyright(c) 2006-2007, Ext JS, LLC.
24756  *
24757  * Originally Released Under LGPL - original licence link has changed is not relivant.
24758  *
24759  * Fork - LGPL
24760  * <script type="text/javascript">
24761  */
24762  
24763 /**
24764  * @class Roo.bootstrap.PagingToolbar
24765  * @extends Roo.bootstrap.NavSimplebar
24766  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24767  * @constructor
24768  * Create a new PagingToolbar
24769  * @param {Object} config The config object
24770  * @param {Roo.data.Store} store
24771  */
24772 Roo.bootstrap.PagingToolbar = function(config)
24773 {
24774     // old args format still supported... - xtype is prefered..
24775         // created from xtype...
24776     
24777     this.ds = config.dataSource;
24778     
24779     if (config.store && !this.ds) {
24780         this.store= Roo.factory(config.store, Roo.data);
24781         this.ds = this.store;
24782         this.ds.xmodule = this.xmodule || false;
24783     }
24784     
24785     this.toolbarItems = [];
24786     if (config.items) {
24787         this.toolbarItems = config.items;
24788     }
24789     
24790     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24791     
24792     this.cursor = 0;
24793     
24794     if (this.ds) { 
24795         this.bind(this.ds);
24796     }
24797     
24798     if (Roo.bootstrap.version == 4) {
24799         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24800     } else {
24801         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24802     }
24803     
24804 };
24805
24806 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24807     /**
24808      * @cfg {Roo.data.Store} dataSource
24809      * The underlying data store providing the paged data
24810      */
24811     /**
24812      * @cfg {String/HTMLElement/Element} container
24813      * container The id or element that will contain the toolbar
24814      */
24815     /**
24816      * @cfg {Boolean} displayInfo
24817      * True to display the displayMsg (defaults to false)
24818      */
24819     /**
24820      * @cfg {Number} pageSize
24821      * The number of records to display per page (defaults to 20)
24822      */
24823     pageSize: 20,
24824     /**
24825      * @cfg {String} displayMsg
24826      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24827      */
24828     displayMsg : 'Displaying {0} - {1} of {2}',
24829     /**
24830      * @cfg {String} emptyMsg
24831      * The message to display when no records are found (defaults to "No data to display")
24832      */
24833     emptyMsg : 'No data to display',
24834     /**
24835      * Customizable piece of the default paging text (defaults to "Page")
24836      * @type String
24837      */
24838     beforePageText : "Page",
24839     /**
24840      * Customizable piece of the default paging text (defaults to "of %0")
24841      * @type String
24842      */
24843     afterPageText : "of {0}",
24844     /**
24845      * Customizable piece of the default paging text (defaults to "First Page")
24846      * @type String
24847      */
24848     firstText : "First Page",
24849     /**
24850      * Customizable piece of the default paging text (defaults to "Previous Page")
24851      * @type String
24852      */
24853     prevText : "Previous Page",
24854     /**
24855      * Customizable piece of the default paging text (defaults to "Next Page")
24856      * @type String
24857      */
24858     nextText : "Next Page",
24859     /**
24860      * Customizable piece of the default paging text (defaults to "Last Page")
24861      * @type String
24862      */
24863     lastText : "Last Page",
24864     /**
24865      * Customizable piece of the default paging text (defaults to "Refresh")
24866      * @type String
24867      */
24868     refreshText : "Refresh",
24869
24870     buttons : false,
24871     // private
24872     onRender : function(ct, position) 
24873     {
24874         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24875         this.navgroup.parentId = this.id;
24876         this.navgroup.onRender(this.el, null);
24877         // add the buttons to the navgroup
24878         
24879         if(this.displayInfo){
24880             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24881             this.displayEl = this.el.select('.x-paging-info', true).first();
24882 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24883 //            this.displayEl = navel.el.select('span',true).first();
24884         }
24885         
24886         var _this = this;
24887         
24888         if(this.buttons){
24889             Roo.each(_this.buttons, function(e){ // this might need to use render????
24890                Roo.factory(e).render(_this.el);
24891             });
24892         }
24893             
24894         Roo.each(_this.toolbarItems, function(e) {
24895             _this.navgroup.addItem(e);
24896         });
24897         
24898         
24899         this.first = this.navgroup.addItem({
24900             tooltip: this.firstText,
24901             cls: "prev btn-outline-secondary",
24902             html : ' <i class="fa fa-step-backward"></i>',
24903             disabled: true,
24904             preventDefault: true,
24905             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24906         });
24907         
24908         this.prev =  this.navgroup.addItem({
24909             tooltip: this.prevText,
24910             cls: "prev btn-outline-secondary",
24911             html : ' <i class="fa fa-backward"></i>',
24912             disabled: true,
24913             preventDefault: true,
24914             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24915         });
24916     //this.addSeparator();
24917         
24918         
24919         var field = this.navgroup.addItem( {
24920             tagtype : 'span',
24921             cls : 'x-paging-position  btn-outline-secondary',
24922              disabled: true,
24923             html : this.beforePageText  +
24924                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24925                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24926          } ); //?? escaped?
24927         
24928         this.field = field.el.select('input', true).first();
24929         this.field.on("keydown", this.onPagingKeydown, this);
24930         this.field.on("focus", function(){this.dom.select();});
24931     
24932     
24933         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24934         //this.field.setHeight(18);
24935         //this.addSeparator();
24936         this.next = this.navgroup.addItem({
24937             tooltip: this.nextText,
24938             cls: "next btn-outline-secondary",
24939             html : ' <i class="fa fa-forward"></i>',
24940             disabled: true,
24941             preventDefault: true,
24942             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24943         });
24944         this.last = this.navgroup.addItem({
24945             tooltip: this.lastText,
24946             html : ' <i class="fa fa-step-forward"></i>',
24947             cls: "next btn-outline-secondary",
24948             disabled: true,
24949             preventDefault: true,
24950             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24951         });
24952     //this.addSeparator();
24953         this.loading = this.navgroup.addItem({
24954             tooltip: this.refreshText,
24955             cls: "btn-outline-secondary",
24956             html : ' <i class="fa fa-refresh"></i>',
24957             preventDefault: true,
24958             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24959         });
24960         
24961     },
24962
24963     // private
24964     updateInfo : function(){
24965         if(this.displayEl){
24966             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24967             var msg = count == 0 ?
24968                 this.emptyMsg :
24969                 String.format(
24970                     this.displayMsg,
24971                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24972                 );
24973             this.displayEl.update(msg);
24974         }
24975     },
24976
24977     // private
24978     onLoad : function(ds, r, o)
24979     {
24980         this.cursor = o.params.start ? o.params.start : 0;
24981         
24982         var d = this.getPageData(),
24983             ap = d.activePage,
24984             ps = d.pages;
24985         
24986         
24987         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24988         this.field.dom.value = ap;
24989         this.first.setDisabled(ap == 1);
24990         this.prev.setDisabled(ap == 1);
24991         this.next.setDisabled(ap == ps);
24992         this.last.setDisabled(ap == ps);
24993         this.loading.enable();
24994         this.updateInfo();
24995     },
24996
24997     // private
24998     getPageData : function(){
24999         var total = this.ds.getTotalCount();
25000         return {
25001             total : total,
25002             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25003             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25004         };
25005     },
25006
25007     // private
25008     onLoadError : function(){
25009         this.loading.enable();
25010     },
25011
25012     // private
25013     onPagingKeydown : function(e){
25014         var k = e.getKey();
25015         var d = this.getPageData();
25016         if(k == e.RETURN){
25017             var v = this.field.dom.value, pageNum;
25018             if(!v || isNaN(pageNum = parseInt(v, 10))){
25019                 this.field.dom.value = d.activePage;
25020                 return;
25021             }
25022             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25023             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25024             e.stopEvent();
25025         }
25026         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))
25027         {
25028           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25029           this.field.dom.value = pageNum;
25030           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25031           e.stopEvent();
25032         }
25033         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25034         {
25035           var v = this.field.dom.value, pageNum; 
25036           var increment = (e.shiftKey) ? 10 : 1;
25037           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25038                 increment *= -1;
25039           }
25040           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25041             this.field.dom.value = d.activePage;
25042             return;
25043           }
25044           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25045           {
25046             this.field.dom.value = parseInt(v, 10) + increment;
25047             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25048             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25049           }
25050           e.stopEvent();
25051         }
25052     },
25053
25054     // private
25055     beforeLoad : function(){
25056         if(this.loading){
25057             this.loading.disable();
25058         }
25059     },
25060
25061     // private
25062     onClick : function(which){
25063         
25064         var ds = this.ds;
25065         if (!ds) {
25066             return;
25067         }
25068         
25069         switch(which){
25070             case "first":
25071                 ds.load({params:{start: 0, limit: this.pageSize}});
25072             break;
25073             case "prev":
25074                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25075             break;
25076             case "next":
25077                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25078             break;
25079             case "last":
25080                 var total = ds.getTotalCount();
25081                 var extra = total % this.pageSize;
25082                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25083                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25084             break;
25085             case "refresh":
25086                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25087             break;
25088         }
25089     },
25090
25091     /**
25092      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25093      * @param {Roo.data.Store} store The data store to unbind
25094      */
25095     unbind : function(ds){
25096         ds.un("beforeload", this.beforeLoad, this);
25097         ds.un("load", this.onLoad, this);
25098         ds.un("loadexception", this.onLoadError, this);
25099         ds.un("remove", this.updateInfo, this);
25100         ds.un("add", this.updateInfo, this);
25101         this.ds = undefined;
25102     },
25103
25104     /**
25105      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25106      * @param {Roo.data.Store} store The data store to bind
25107      */
25108     bind : function(ds){
25109         ds.on("beforeload", this.beforeLoad, this);
25110         ds.on("load", this.onLoad, this);
25111         ds.on("loadexception", this.onLoadError, this);
25112         ds.on("remove", this.updateInfo, this);
25113         ds.on("add", this.updateInfo, this);
25114         this.ds = ds;
25115     }
25116 });/*
25117  * - LGPL
25118  *
25119  * element
25120  * 
25121  */
25122
25123 /**
25124  * @class Roo.bootstrap.MessageBar
25125  * @extends Roo.bootstrap.Component
25126  * Bootstrap MessageBar class
25127  * @cfg {String} html contents of the MessageBar
25128  * @cfg {String} weight (info | success | warning | danger) default info
25129  * @cfg {String} beforeClass insert the bar before the given class
25130  * @cfg {Boolean} closable (true | false) default false
25131  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25132  * 
25133  * @constructor
25134  * Create a new Element
25135  * @param {Object} config The config object
25136  */
25137
25138 Roo.bootstrap.MessageBar = function(config){
25139     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25140 };
25141
25142 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25143     
25144     html: '',
25145     weight: 'info',
25146     closable: false,
25147     fixed: false,
25148     beforeClass: 'bootstrap-sticky-wrap',
25149     
25150     getAutoCreate : function(){
25151         
25152         var cfg = {
25153             tag: 'div',
25154             cls: 'alert alert-dismissable alert-' + this.weight,
25155             cn: [
25156                 {
25157                     tag: 'span',
25158                     cls: 'message',
25159                     html: this.html || ''
25160                 }
25161             ]
25162         };
25163         
25164         if(this.fixed){
25165             cfg.cls += ' alert-messages-fixed';
25166         }
25167         
25168         if(this.closable){
25169             cfg.cn.push({
25170                 tag: 'button',
25171                 cls: 'close',
25172                 html: 'x'
25173             });
25174         }
25175         
25176         return cfg;
25177     },
25178     
25179     onRender : function(ct, position)
25180     {
25181         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25182         
25183         if(!this.el){
25184             var cfg = Roo.apply({},  this.getAutoCreate());
25185             cfg.id = Roo.id();
25186             
25187             if (this.cls) {
25188                 cfg.cls += ' ' + this.cls;
25189             }
25190             if (this.style) {
25191                 cfg.style = this.style;
25192             }
25193             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25194             
25195             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25196         }
25197         
25198         this.el.select('>button.close').on('click', this.hide, this);
25199         
25200     },
25201     
25202     show : function()
25203     {
25204         if (!this.rendered) {
25205             this.render();
25206         }
25207         
25208         this.el.show();
25209         
25210         this.fireEvent('show', this);
25211         
25212     },
25213     
25214     hide : function()
25215     {
25216         if (!this.rendered) {
25217             this.render();
25218         }
25219         
25220         this.el.hide();
25221         
25222         this.fireEvent('hide', this);
25223     },
25224     
25225     update : function()
25226     {
25227 //        var e = this.el.dom.firstChild;
25228 //        
25229 //        if(this.closable){
25230 //            e = e.nextSibling;
25231 //        }
25232 //        
25233 //        e.data = this.html || '';
25234
25235         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25236     }
25237    
25238 });
25239
25240  
25241
25242      /*
25243  * - LGPL
25244  *
25245  * Graph
25246  * 
25247  */
25248
25249
25250 /**
25251  * @class Roo.bootstrap.Graph
25252  * @extends Roo.bootstrap.Component
25253  * Bootstrap Graph class
25254 > Prameters
25255  -sm {number} sm 4
25256  -md {number} md 5
25257  @cfg {String} graphtype  bar | vbar | pie
25258  @cfg {number} g_x coodinator | centre x (pie)
25259  @cfg {number} g_y coodinator | centre y (pie)
25260  @cfg {number} g_r radius (pie)
25261  @cfg {number} g_height height of the chart (respected by all elements in the set)
25262  @cfg {number} g_width width of the chart (respected by all elements in the set)
25263  @cfg {Object} title The title of the chart
25264     
25265  -{Array}  values
25266  -opts (object) options for the chart 
25267      o {
25268      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25269      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25270      o vgutter (number)
25271      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.
25272      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25273      o to
25274      o stretch (boolean)
25275      o }
25276  -opts (object) options for the pie
25277      o{
25278      o cut
25279      o startAngle (number)
25280      o endAngle (number)
25281      } 
25282  *
25283  * @constructor
25284  * Create a new Input
25285  * @param {Object} config The config object
25286  */
25287
25288 Roo.bootstrap.Graph = function(config){
25289     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25290     
25291     this.addEvents({
25292         // img events
25293         /**
25294          * @event click
25295          * The img click event for the img.
25296          * @param {Roo.EventObject} e
25297          */
25298         "click" : true
25299     });
25300 };
25301
25302 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25303     
25304     sm: 4,
25305     md: 5,
25306     graphtype: 'bar',
25307     g_height: 250,
25308     g_width: 400,
25309     g_x: 50,
25310     g_y: 50,
25311     g_r: 30,
25312     opts:{
25313         //g_colors: this.colors,
25314         g_type: 'soft',
25315         g_gutter: '20%'
25316
25317     },
25318     title : false,
25319
25320     getAutoCreate : function(){
25321         
25322         var cfg = {
25323             tag: 'div',
25324             html : null
25325         };
25326         
25327         
25328         return  cfg;
25329     },
25330
25331     onRender : function(ct,position){
25332         
25333         
25334         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25335         
25336         if (typeof(Raphael) == 'undefined') {
25337             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25338             return;
25339         }
25340         
25341         this.raphael = Raphael(this.el.dom);
25342         
25343                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25344                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25345                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25346                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25347                 /*
25348                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25349                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25350                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25351                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25352                 
25353                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25354                 r.barchart(330, 10, 300, 220, data1);
25355                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25356                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25357                 */
25358                 
25359                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25360                 // r.barchart(30, 30, 560, 250,  xdata, {
25361                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25362                 //     axis : "0 0 1 1",
25363                 //     axisxlabels :  xdata
25364                 //     //yvalues : cols,
25365                    
25366                 // });
25367 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25368 //        
25369 //        this.load(null,xdata,{
25370 //                axis : "0 0 1 1",
25371 //                axisxlabels :  xdata
25372 //                });
25373
25374     },
25375
25376     load : function(graphtype,xdata,opts)
25377     {
25378         this.raphael.clear();
25379         if(!graphtype) {
25380             graphtype = this.graphtype;
25381         }
25382         if(!opts){
25383             opts = this.opts;
25384         }
25385         var r = this.raphael,
25386             fin = function () {
25387                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25388             },
25389             fout = function () {
25390                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25391             },
25392             pfin = function() {
25393                 this.sector.stop();
25394                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25395
25396                 if (this.label) {
25397                     this.label[0].stop();
25398                     this.label[0].attr({ r: 7.5 });
25399                     this.label[1].attr({ "font-weight": 800 });
25400                 }
25401             },
25402             pfout = function() {
25403                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25404
25405                 if (this.label) {
25406                     this.label[0].animate({ r: 5 }, 500, "bounce");
25407                     this.label[1].attr({ "font-weight": 400 });
25408                 }
25409             };
25410
25411         switch(graphtype){
25412             case 'bar':
25413                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25414                 break;
25415             case 'hbar':
25416                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25417                 break;
25418             case 'pie':
25419 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25420 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25421 //            
25422                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25423                 
25424                 break;
25425
25426         }
25427         
25428         if(this.title){
25429             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25430         }
25431         
25432     },
25433     
25434     setTitle: function(o)
25435     {
25436         this.title = o;
25437     },
25438     
25439     initEvents: function() {
25440         
25441         if(!this.href){
25442             this.el.on('click', this.onClick, this);
25443         }
25444     },
25445     
25446     onClick : function(e)
25447     {
25448         Roo.log('img onclick');
25449         this.fireEvent('click', this, e);
25450     }
25451    
25452 });
25453
25454  
25455 /*
25456  * - LGPL
25457  *
25458  * numberBox
25459  * 
25460  */
25461 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25462
25463 /**
25464  * @class Roo.bootstrap.dash.NumberBox
25465  * @extends Roo.bootstrap.Component
25466  * Bootstrap NumberBox class
25467  * @cfg {String} headline Box headline
25468  * @cfg {String} content Box content
25469  * @cfg {String} icon Box icon
25470  * @cfg {String} footer Footer text
25471  * @cfg {String} fhref Footer href
25472  * 
25473  * @constructor
25474  * Create a new NumberBox
25475  * @param {Object} config The config object
25476  */
25477
25478
25479 Roo.bootstrap.dash.NumberBox = function(config){
25480     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25481     
25482 };
25483
25484 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25485     
25486     headline : '',
25487     content : '',
25488     icon : '',
25489     footer : '',
25490     fhref : '',
25491     ficon : '',
25492     
25493     getAutoCreate : function(){
25494         
25495         var cfg = {
25496             tag : 'div',
25497             cls : 'small-box ',
25498             cn : [
25499                 {
25500                     tag : 'div',
25501                     cls : 'inner',
25502                     cn :[
25503                         {
25504                             tag : 'h3',
25505                             cls : 'roo-headline',
25506                             html : this.headline
25507                         },
25508                         {
25509                             tag : 'p',
25510                             cls : 'roo-content',
25511                             html : this.content
25512                         }
25513                     ]
25514                 }
25515             ]
25516         };
25517         
25518         if(this.icon){
25519             cfg.cn.push({
25520                 tag : 'div',
25521                 cls : 'icon',
25522                 cn :[
25523                     {
25524                         tag : 'i',
25525                         cls : 'ion ' + this.icon
25526                     }
25527                 ]
25528             });
25529         }
25530         
25531         if(this.footer){
25532             var footer = {
25533                 tag : 'a',
25534                 cls : 'small-box-footer',
25535                 href : this.fhref || '#',
25536                 html : this.footer
25537             };
25538             
25539             cfg.cn.push(footer);
25540             
25541         }
25542         
25543         return  cfg;
25544     },
25545
25546     onRender : function(ct,position){
25547         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25548
25549
25550        
25551                 
25552     },
25553
25554     setHeadline: function (value)
25555     {
25556         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25557     },
25558     
25559     setFooter: function (value, href)
25560     {
25561         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25562         
25563         if(href){
25564             this.el.select('a.small-box-footer',true).first().attr('href', href);
25565         }
25566         
25567     },
25568
25569     setContent: function (value)
25570     {
25571         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25572     },
25573
25574     initEvents: function() 
25575     {   
25576         
25577     }
25578     
25579 });
25580
25581  
25582 /*
25583  * - LGPL
25584  *
25585  * TabBox
25586  * 
25587  */
25588 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25589
25590 /**
25591  * @class Roo.bootstrap.dash.TabBox
25592  * @extends Roo.bootstrap.Component
25593  * Bootstrap TabBox class
25594  * @cfg {String} title Title of the TabBox
25595  * @cfg {String} icon Icon of the TabBox
25596  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25597  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25598  * 
25599  * @constructor
25600  * Create a new TabBox
25601  * @param {Object} config The config object
25602  */
25603
25604
25605 Roo.bootstrap.dash.TabBox = function(config){
25606     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25607     this.addEvents({
25608         // raw events
25609         /**
25610          * @event addpane
25611          * When a pane is added
25612          * @param {Roo.bootstrap.dash.TabPane} pane
25613          */
25614         "addpane" : true,
25615         /**
25616          * @event activatepane
25617          * When a pane is activated
25618          * @param {Roo.bootstrap.dash.TabPane} pane
25619          */
25620         "activatepane" : true
25621         
25622          
25623     });
25624     
25625     this.panes = [];
25626 };
25627
25628 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25629
25630     title : '',
25631     icon : false,
25632     showtabs : true,
25633     tabScrollable : false,
25634     
25635     getChildContainer : function()
25636     {
25637         return this.el.select('.tab-content', true).first();
25638     },
25639     
25640     getAutoCreate : function(){
25641         
25642         var header = {
25643             tag: 'li',
25644             cls: 'pull-left header',
25645             html: this.title,
25646             cn : []
25647         };
25648         
25649         if(this.icon){
25650             header.cn.push({
25651                 tag: 'i',
25652                 cls: 'fa ' + this.icon
25653             });
25654         }
25655         
25656         var h = {
25657             tag: 'ul',
25658             cls: 'nav nav-tabs pull-right',
25659             cn: [
25660                 header
25661             ]
25662         };
25663         
25664         if(this.tabScrollable){
25665             h = {
25666                 tag: 'div',
25667                 cls: 'tab-header',
25668                 cn: [
25669                     {
25670                         tag: 'ul',
25671                         cls: 'nav nav-tabs pull-right',
25672                         cn: [
25673                             header
25674                         ]
25675                     }
25676                 ]
25677             };
25678         }
25679         
25680         var cfg = {
25681             tag: 'div',
25682             cls: 'nav-tabs-custom',
25683             cn: [
25684                 h,
25685                 {
25686                     tag: 'div',
25687                     cls: 'tab-content no-padding',
25688                     cn: []
25689                 }
25690             ]
25691         };
25692
25693         return  cfg;
25694     },
25695     initEvents : function()
25696     {
25697         //Roo.log('add add pane handler');
25698         this.on('addpane', this.onAddPane, this);
25699     },
25700      /**
25701      * Updates the box title
25702      * @param {String} html to set the title to.
25703      */
25704     setTitle : function(value)
25705     {
25706         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25707     },
25708     onAddPane : function(pane)
25709     {
25710         this.panes.push(pane);
25711         //Roo.log('addpane');
25712         //Roo.log(pane);
25713         // tabs are rendere left to right..
25714         if(!this.showtabs){
25715             return;
25716         }
25717         
25718         var ctr = this.el.select('.nav-tabs', true).first();
25719          
25720          
25721         var existing = ctr.select('.nav-tab',true);
25722         var qty = existing.getCount();;
25723         
25724         
25725         var tab = ctr.createChild({
25726             tag : 'li',
25727             cls : 'nav-tab' + (qty ? '' : ' active'),
25728             cn : [
25729                 {
25730                     tag : 'a',
25731                     href:'#',
25732                     html : pane.title
25733                 }
25734             ]
25735         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25736         pane.tab = tab;
25737         
25738         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25739         if (!qty) {
25740             pane.el.addClass('active');
25741         }
25742         
25743                 
25744     },
25745     onTabClick : function(ev,un,ob,pane)
25746     {
25747         //Roo.log('tab - prev default');
25748         ev.preventDefault();
25749         
25750         
25751         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25752         pane.tab.addClass('active');
25753         //Roo.log(pane.title);
25754         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25755         // technically we should have a deactivate event.. but maybe add later.
25756         // and it should not de-activate the selected tab...
25757         this.fireEvent('activatepane', pane);
25758         pane.el.addClass('active');
25759         pane.fireEvent('activate');
25760         
25761         
25762     },
25763     
25764     getActivePane : function()
25765     {
25766         var r = false;
25767         Roo.each(this.panes, function(p) {
25768             if(p.el.hasClass('active')){
25769                 r = p;
25770                 return false;
25771             }
25772             
25773             return;
25774         });
25775         
25776         return r;
25777     }
25778     
25779     
25780 });
25781
25782  
25783 /*
25784  * - LGPL
25785  *
25786  * Tab pane
25787  * 
25788  */
25789 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25790 /**
25791  * @class Roo.bootstrap.TabPane
25792  * @extends Roo.bootstrap.Component
25793  * Bootstrap TabPane class
25794  * @cfg {Boolean} active (false | true) Default false
25795  * @cfg {String} title title of panel
25796
25797  * 
25798  * @constructor
25799  * Create a new TabPane
25800  * @param {Object} config The config object
25801  */
25802
25803 Roo.bootstrap.dash.TabPane = function(config){
25804     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25805     
25806     this.addEvents({
25807         // raw events
25808         /**
25809          * @event activate
25810          * When a pane is activated
25811          * @param {Roo.bootstrap.dash.TabPane} pane
25812          */
25813         "activate" : true
25814          
25815     });
25816 };
25817
25818 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25819     
25820     active : false,
25821     title : '',
25822     
25823     // the tabBox that this is attached to.
25824     tab : false,
25825      
25826     getAutoCreate : function() 
25827     {
25828         var cfg = {
25829             tag: 'div',
25830             cls: 'tab-pane'
25831         };
25832         
25833         if(this.active){
25834             cfg.cls += ' active';
25835         }
25836         
25837         return cfg;
25838     },
25839     initEvents  : function()
25840     {
25841         //Roo.log('trigger add pane handler');
25842         this.parent().fireEvent('addpane', this)
25843     },
25844     
25845      /**
25846      * Updates the tab title 
25847      * @param {String} html to set the title to.
25848      */
25849     setTitle: function(str)
25850     {
25851         if (!this.tab) {
25852             return;
25853         }
25854         this.title = str;
25855         this.tab.select('a', true).first().dom.innerHTML = str;
25856         
25857     }
25858     
25859     
25860     
25861 });
25862
25863  
25864
25865
25866  /*
25867  * - LGPL
25868  *
25869  * menu
25870  * 
25871  */
25872 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25873
25874 /**
25875  * @class Roo.bootstrap.menu.Menu
25876  * @extends Roo.bootstrap.Component
25877  * Bootstrap Menu class - container for Menu
25878  * @cfg {String} html Text of the menu
25879  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25880  * @cfg {String} icon Font awesome icon
25881  * @cfg {String} pos Menu align to (top | bottom) default bottom
25882  * 
25883  * 
25884  * @constructor
25885  * Create a new Menu
25886  * @param {Object} config The config object
25887  */
25888
25889
25890 Roo.bootstrap.menu.Menu = function(config){
25891     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25892     
25893     this.addEvents({
25894         /**
25895          * @event beforeshow
25896          * Fires before this menu is displayed
25897          * @param {Roo.bootstrap.menu.Menu} this
25898          */
25899         beforeshow : true,
25900         /**
25901          * @event beforehide
25902          * Fires before this menu is hidden
25903          * @param {Roo.bootstrap.menu.Menu} this
25904          */
25905         beforehide : true,
25906         /**
25907          * @event show
25908          * Fires after this menu is displayed
25909          * @param {Roo.bootstrap.menu.Menu} this
25910          */
25911         show : true,
25912         /**
25913          * @event hide
25914          * Fires after this menu is hidden
25915          * @param {Roo.bootstrap.menu.Menu} this
25916          */
25917         hide : true,
25918         /**
25919          * @event click
25920          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25921          * @param {Roo.bootstrap.menu.Menu} this
25922          * @param {Roo.EventObject} e
25923          */
25924         click : true
25925     });
25926     
25927 };
25928
25929 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25930     
25931     submenu : false,
25932     html : '',
25933     weight : 'default',
25934     icon : false,
25935     pos : 'bottom',
25936     
25937     
25938     getChildContainer : function() {
25939         if(this.isSubMenu){
25940             return this.el;
25941         }
25942         
25943         return this.el.select('ul.dropdown-menu', true).first();  
25944     },
25945     
25946     getAutoCreate : function()
25947     {
25948         var text = [
25949             {
25950                 tag : 'span',
25951                 cls : 'roo-menu-text',
25952                 html : this.html
25953             }
25954         ];
25955         
25956         if(this.icon){
25957             text.unshift({
25958                 tag : 'i',
25959                 cls : 'fa ' + this.icon
25960             })
25961         }
25962         
25963         
25964         var cfg = {
25965             tag : 'div',
25966             cls : 'btn-group',
25967             cn : [
25968                 {
25969                     tag : 'button',
25970                     cls : 'dropdown-button btn btn-' + this.weight,
25971                     cn : text
25972                 },
25973                 {
25974                     tag : 'button',
25975                     cls : 'dropdown-toggle btn btn-' + this.weight,
25976                     cn : [
25977                         {
25978                             tag : 'span',
25979                             cls : 'caret'
25980                         }
25981                     ]
25982                 },
25983                 {
25984                     tag : 'ul',
25985                     cls : 'dropdown-menu'
25986                 }
25987             ]
25988             
25989         };
25990         
25991         if(this.pos == 'top'){
25992             cfg.cls += ' dropup';
25993         }
25994         
25995         if(this.isSubMenu){
25996             cfg = {
25997                 tag : 'ul',
25998                 cls : 'dropdown-menu'
25999             }
26000         }
26001         
26002         return cfg;
26003     },
26004     
26005     onRender : function(ct, position)
26006     {
26007         this.isSubMenu = ct.hasClass('dropdown-submenu');
26008         
26009         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26010     },
26011     
26012     initEvents : function() 
26013     {
26014         if(this.isSubMenu){
26015             return;
26016         }
26017         
26018         this.hidden = true;
26019         
26020         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26021         this.triggerEl.on('click', this.onTriggerPress, this);
26022         
26023         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26024         this.buttonEl.on('click', this.onClick, this);
26025         
26026     },
26027     
26028     list : function()
26029     {
26030         if(this.isSubMenu){
26031             return this.el;
26032         }
26033         
26034         return this.el.select('ul.dropdown-menu', true).first();
26035     },
26036     
26037     onClick : function(e)
26038     {
26039         this.fireEvent("click", this, e);
26040     },
26041     
26042     onTriggerPress  : function(e)
26043     {   
26044         if (this.isVisible()) {
26045             this.hide();
26046         } else {
26047             this.show();
26048         }
26049     },
26050     
26051     isVisible : function(){
26052         return !this.hidden;
26053     },
26054     
26055     show : function()
26056     {
26057         this.fireEvent("beforeshow", this);
26058         
26059         this.hidden = false;
26060         this.el.addClass('open');
26061         
26062         Roo.get(document).on("mouseup", this.onMouseUp, this);
26063         
26064         this.fireEvent("show", this);
26065         
26066         
26067     },
26068     
26069     hide : function()
26070     {
26071         this.fireEvent("beforehide", this);
26072         
26073         this.hidden = true;
26074         this.el.removeClass('open');
26075         
26076         Roo.get(document).un("mouseup", this.onMouseUp);
26077         
26078         this.fireEvent("hide", this);
26079     },
26080     
26081     onMouseUp : function()
26082     {
26083         this.hide();
26084     }
26085     
26086 });
26087
26088  
26089  /*
26090  * - LGPL
26091  *
26092  * menu item
26093  * 
26094  */
26095 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26096
26097 /**
26098  * @class Roo.bootstrap.menu.Item
26099  * @extends Roo.bootstrap.Component
26100  * Bootstrap MenuItem class
26101  * @cfg {Boolean} submenu (true | false) default false
26102  * @cfg {String} html text of the item
26103  * @cfg {String} href the link
26104  * @cfg {Boolean} disable (true | false) default false
26105  * @cfg {Boolean} preventDefault (true | false) default true
26106  * @cfg {String} icon Font awesome icon
26107  * @cfg {String} pos Submenu align to (left | right) default right 
26108  * 
26109  * 
26110  * @constructor
26111  * Create a new Item
26112  * @param {Object} config The config object
26113  */
26114
26115
26116 Roo.bootstrap.menu.Item = function(config){
26117     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26118     this.addEvents({
26119         /**
26120          * @event mouseover
26121          * Fires when the mouse is hovering over this menu
26122          * @param {Roo.bootstrap.menu.Item} this
26123          * @param {Roo.EventObject} e
26124          */
26125         mouseover : true,
26126         /**
26127          * @event mouseout
26128          * Fires when the mouse exits this menu
26129          * @param {Roo.bootstrap.menu.Item} this
26130          * @param {Roo.EventObject} e
26131          */
26132         mouseout : true,
26133         // raw events
26134         /**
26135          * @event click
26136          * The raw click event for the entire grid.
26137          * @param {Roo.EventObject} e
26138          */
26139         click : true
26140     });
26141 };
26142
26143 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26144     
26145     submenu : false,
26146     href : '',
26147     html : '',
26148     preventDefault: true,
26149     disable : false,
26150     icon : false,
26151     pos : 'right',
26152     
26153     getAutoCreate : function()
26154     {
26155         var text = [
26156             {
26157                 tag : 'span',
26158                 cls : 'roo-menu-item-text',
26159                 html : this.html
26160             }
26161         ];
26162         
26163         if(this.icon){
26164             text.unshift({
26165                 tag : 'i',
26166                 cls : 'fa ' + this.icon
26167             })
26168         }
26169         
26170         var cfg = {
26171             tag : 'li',
26172             cn : [
26173                 {
26174                     tag : 'a',
26175                     href : this.href || '#',
26176                     cn : text
26177                 }
26178             ]
26179         };
26180         
26181         if(this.disable){
26182             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26183         }
26184         
26185         if(this.submenu){
26186             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26187             
26188             if(this.pos == 'left'){
26189                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26190             }
26191         }
26192         
26193         return cfg;
26194     },
26195     
26196     initEvents : function() 
26197     {
26198         this.el.on('mouseover', this.onMouseOver, this);
26199         this.el.on('mouseout', this.onMouseOut, this);
26200         
26201         this.el.select('a', true).first().on('click', this.onClick, this);
26202         
26203     },
26204     
26205     onClick : function(e)
26206     {
26207         if(this.preventDefault){
26208             e.preventDefault();
26209         }
26210         
26211         this.fireEvent("click", this, e);
26212     },
26213     
26214     onMouseOver : function(e)
26215     {
26216         if(this.submenu && this.pos == 'left'){
26217             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26218         }
26219         
26220         this.fireEvent("mouseover", this, e);
26221     },
26222     
26223     onMouseOut : function(e)
26224     {
26225         this.fireEvent("mouseout", this, e);
26226     }
26227 });
26228
26229  
26230
26231  /*
26232  * - LGPL
26233  *
26234  * menu separator
26235  * 
26236  */
26237 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26238
26239 /**
26240  * @class Roo.bootstrap.menu.Separator
26241  * @extends Roo.bootstrap.Component
26242  * Bootstrap Separator class
26243  * 
26244  * @constructor
26245  * Create a new Separator
26246  * @param {Object} config The config object
26247  */
26248
26249
26250 Roo.bootstrap.menu.Separator = function(config){
26251     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26252 };
26253
26254 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26255     
26256     getAutoCreate : function(){
26257         var cfg = {
26258             tag : 'li',
26259             cls: 'divider'
26260         };
26261         
26262         return cfg;
26263     }
26264    
26265 });
26266
26267  
26268
26269  /*
26270  * - LGPL
26271  *
26272  * Tooltip
26273  * 
26274  */
26275
26276 /**
26277  * @class Roo.bootstrap.Tooltip
26278  * Bootstrap Tooltip class
26279  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26280  * to determine which dom element triggers the tooltip.
26281  * 
26282  * It needs to add support for additional attributes like tooltip-position
26283  * 
26284  * @constructor
26285  * Create a new Toolti
26286  * @param {Object} config The config object
26287  */
26288
26289 Roo.bootstrap.Tooltip = function(config){
26290     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26291     
26292     this.alignment = Roo.bootstrap.Tooltip.alignment;
26293     
26294     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26295         this.alignment = config.alignment;
26296     }
26297     
26298 };
26299
26300 Roo.apply(Roo.bootstrap.Tooltip, {
26301     /**
26302      * @function init initialize tooltip monitoring.
26303      * @static
26304      */
26305     currentEl : false,
26306     currentTip : false,
26307     currentRegion : false,
26308     
26309     //  init : delay?
26310     
26311     init : function()
26312     {
26313         Roo.get(document).on('mouseover', this.enter ,this);
26314         Roo.get(document).on('mouseout', this.leave, this);
26315          
26316         
26317         this.currentTip = new Roo.bootstrap.Tooltip();
26318     },
26319     
26320     enter : function(ev)
26321     {
26322         var dom = ev.getTarget();
26323         
26324         //Roo.log(['enter',dom]);
26325         var el = Roo.fly(dom);
26326         if (this.currentEl) {
26327             //Roo.log(dom);
26328             //Roo.log(this.currentEl);
26329             //Roo.log(this.currentEl.contains(dom));
26330             if (this.currentEl == el) {
26331                 return;
26332             }
26333             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26334                 return;
26335             }
26336
26337         }
26338         
26339         if (this.currentTip.el) {
26340             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26341         }    
26342         //Roo.log(ev);
26343         
26344         if(!el || el.dom == document){
26345             return;
26346         }
26347         
26348         var bindEl = el;
26349         
26350         // you can not look for children, as if el is the body.. then everythign is the child..
26351         if (!el.attr('tooltip')) { //
26352             if (!el.select("[tooltip]").elements.length) {
26353                 return;
26354             }
26355             // is the mouse over this child...?
26356             bindEl = el.select("[tooltip]").first();
26357             var xy = ev.getXY();
26358             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26359                 //Roo.log("not in region.");
26360                 return;
26361             }
26362             //Roo.log("child element over..");
26363             
26364         }
26365         this.currentEl = bindEl;
26366         this.currentTip.bind(bindEl);
26367         this.currentRegion = Roo.lib.Region.getRegion(dom);
26368         this.currentTip.enter();
26369         
26370     },
26371     leave : function(ev)
26372     {
26373         var dom = ev.getTarget();
26374         //Roo.log(['leave',dom]);
26375         if (!this.currentEl) {
26376             return;
26377         }
26378         
26379         
26380         if (dom != this.currentEl.dom) {
26381             return;
26382         }
26383         var xy = ev.getXY();
26384         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26385             return;
26386         }
26387         // only activate leave if mouse cursor is outside... bounding box..
26388         
26389         
26390         
26391         
26392         if (this.currentTip) {
26393             this.currentTip.leave();
26394         }
26395         //Roo.log('clear currentEl');
26396         this.currentEl = false;
26397         
26398         
26399     },
26400     alignment : {
26401         'left' : ['r-l', [-2,0], 'right'],
26402         'right' : ['l-r', [2,0], 'left'],
26403         'bottom' : ['t-b', [0,2], 'top'],
26404         'top' : [ 'b-t', [0,-2], 'bottom']
26405     }
26406     
26407 });
26408
26409
26410 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26411     
26412     
26413     bindEl : false,
26414     
26415     delay : null, // can be { show : 300 , hide: 500}
26416     
26417     timeout : null,
26418     
26419     hoverState : null, //???
26420     
26421     placement : 'bottom', 
26422     
26423     alignment : false,
26424     
26425     getAutoCreate : function(){
26426     
26427         var cfg = {
26428            cls : 'tooltip',
26429            role : 'tooltip',
26430            cn : [
26431                 {
26432                     cls : 'tooltip-arrow'
26433                 },
26434                 {
26435                     cls : 'tooltip-inner'
26436                 }
26437            ]
26438         };
26439         
26440         return cfg;
26441     },
26442     bind : function(el)
26443     {
26444         this.bindEl = el;
26445     },
26446       
26447     
26448     enter : function () {
26449        
26450         if (this.timeout != null) {
26451             clearTimeout(this.timeout);
26452         }
26453         
26454         this.hoverState = 'in';
26455          //Roo.log("enter - show");
26456         if (!this.delay || !this.delay.show) {
26457             this.show();
26458             return;
26459         }
26460         var _t = this;
26461         this.timeout = setTimeout(function () {
26462             if (_t.hoverState == 'in') {
26463                 _t.show();
26464             }
26465         }, this.delay.show);
26466     },
26467     leave : function()
26468     {
26469         clearTimeout(this.timeout);
26470     
26471         this.hoverState = 'out';
26472          if (!this.delay || !this.delay.hide) {
26473             this.hide();
26474             return;
26475         }
26476        
26477         var _t = this;
26478         this.timeout = setTimeout(function () {
26479             //Roo.log("leave - timeout");
26480             
26481             if (_t.hoverState == 'out') {
26482                 _t.hide();
26483                 Roo.bootstrap.Tooltip.currentEl = false;
26484             }
26485         }, delay);
26486     },
26487     
26488     show : function (msg)
26489     {
26490         if (!this.el) {
26491             this.render(document.body);
26492         }
26493         // set content.
26494         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26495         
26496         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26497         
26498         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26499         
26500         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26501         
26502         var placement = typeof this.placement == 'function' ?
26503             this.placement.call(this, this.el, on_el) :
26504             this.placement;
26505             
26506         var autoToken = /\s?auto?\s?/i;
26507         var autoPlace = autoToken.test(placement);
26508         if (autoPlace) {
26509             placement = placement.replace(autoToken, '') || 'top';
26510         }
26511         
26512         //this.el.detach()
26513         //this.el.setXY([0,0]);
26514         this.el.show();
26515         //this.el.dom.style.display='block';
26516         
26517         //this.el.appendTo(on_el);
26518         
26519         var p = this.getPosition();
26520         var box = this.el.getBox();
26521         
26522         if (autoPlace) {
26523             // fixme..
26524         }
26525         
26526         var align = this.alignment[placement];
26527         
26528         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26529         
26530         if(placement == 'top' || placement == 'bottom'){
26531             if(xy[0] < 0){
26532                 placement = 'right';
26533             }
26534             
26535             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26536                 placement = 'left';
26537             }
26538             
26539             var scroll = Roo.select('body', true).first().getScroll();
26540             
26541             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26542                 placement = 'top';
26543             }
26544             
26545             align = this.alignment[placement];
26546         }
26547         
26548         this.el.alignTo(this.bindEl, align[0],align[1]);
26549         //var arrow = this.el.select('.arrow',true).first();
26550         //arrow.set(align[2], 
26551         
26552         this.el.addClass(placement);
26553         
26554         this.el.addClass('in fade');
26555         
26556         this.hoverState = null;
26557         
26558         if (this.el.hasClass('fade')) {
26559             // fade it?
26560         }
26561         
26562     },
26563     hide : function()
26564     {
26565          
26566         if (!this.el) {
26567             return;
26568         }
26569         //this.el.setXY([0,0]);
26570         this.el.removeClass('in');
26571         //this.el.hide();
26572         
26573     }
26574     
26575 });
26576  
26577
26578  /*
26579  * - LGPL
26580  *
26581  * Location Picker
26582  * 
26583  */
26584
26585 /**
26586  * @class Roo.bootstrap.LocationPicker
26587  * @extends Roo.bootstrap.Component
26588  * Bootstrap LocationPicker class
26589  * @cfg {Number} latitude Position when init default 0
26590  * @cfg {Number} longitude Position when init default 0
26591  * @cfg {Number} zoom default 15
26592  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26593  * @cfg {Boolean} mapTypeControl default false
26594  * @cfg {Boolean} disableDoubleClickZoom default false
26595  * @cfg {Boolean} scrollwheel default true
26596  * @cfg {Boolean} streetViewControl default false
26597  * @cfg {Number} radius default 0
26598  * @cfg {String} locationName
26599  * @cfg {Boolean} draggable default true
26600  * @cfg {Boolean} enableAutocomplete default false
26601  * @cfg {Boolean} enableReverseGeocode default true
26602  * @cfg {String} markerTitle
26603  * 
26604  * @constructor
26605  * Create a new LocationPicker
26606  * @param {Object} config The config object
26607  */
26608
26609
26610 Roo.bootstrap.LocationPicker = function(config){
26611     
26612     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26613     
26614     this.addEvents({
26615         /**
26616          * @event initial
26617          * Fires when the picker initialized.
26618          * @param {Roo.bootstrap.LocationPicker} this
26619          * @param {Google Location} location
26620          */
26621         initial : true,
26622         /**
26623          * @event positionchanged
26624          * Fires when the picker position changed.
26625          * @param {Roo.bootstrap.LocationPicker} this
26626          * @param {Google Location} location
26627          */
26628         positionchanged : true,
26629         /**
26630          * @event resize
26631          * Fires when the map resize.
26632          * @param {Roo.bootstrap.LocationPicker} this
26633          */
26634         resize : true,
26635         /**
26636          * @event show
26637          * Fires when the map show.
26638          * @param {Roo.bootstrap.LocationPicker} this
26639          */
26640         show : true,
26641         /**
26642          * @event hide
26643          * Fires when the map hide.
26644          * @param {Roo.bootstrap.LocationPicker} this
26645          */
26646         hide : true,
26647         /**
26648          * @event mapClick
26649          * Fires when click the map.
26650          * @param {Roo.bootstrap.LocationPicker} this
26651          * @param {Map event} e
26652          */
26653         mapClick : true,
26654         /**
26655          * @event mapRightClick
26656          * Fires when right click the map.
26657          * @param {Roo.bootstrap.LocationPicker} this
26658          * @param {Map event} e
26659          */
26660         mapRightClick : true,
26661         /**
26662          * @event markerClick
26663          * Fires when click the marker.
26664          * @param {Roo.bootstrap.LocationPicker} this
26665          * @param {Map event} e
26666          */
26667         markerClick : true,
26668         /**
26669          * @event markerRightClick
26670          * Fires when right click the marker.
26671          * @param {Roo.bootstrap.LocationPicker} this
26672          * @param {Map event} e
26673          */
26674         markerRightClick : true,
26675         /**
26676          * @event OverlayViewDraw
26677          * Fires when OverlayView Draw
26678          * @param {Roo.bootstrap.LocationPicker} this
26679          */
26680         OverlayViewDraw : true,
26681         /**
26682          * @event OverlayViewOnAdd
26683          * Fires when OverlayView Draw
26684          * @param {Roo.bootstrap.LocationPicker} this
26685          */
26686         OverlayViewOnAdd : true,
26687         /**
26688          * @event OverlayViewOnRemove
26689          * Fires when OverlayView Draw
26690          * @param {Roo.bootstrap.LocationPicker} this
26691          */
26692         OverlayViewOnRemove : true,
26693         /**
26694          * @event OverlayViewShow
26695          * Fires when OverlayView Draw
26696          * @param {Roo.bootstrap.LocationPicker} this
26697          * @param {Pixel} cpx
26698          */
26699         OverlayViewShow : true,
26700         /**
26701          * @event OverlayViewHide
26702          * Fires when OverlayView Draw
26703          * @param {Roo.bootstrap.LocationPicker} this
26704          */
26705         OverlayViewHide : true,
26706         /**
26707          * @event loadexception
26708          * Fires when load google lib failed.
26709          * @param {Roo.bootstrap.LocationPicker} this
26710          */
26711         loadexception : true
26712     });
26713         
26714 };
26715
26716 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26717     
26718     gMapContext: false,
26719     
26720     latitude: 0,
26721     longitude: 0,
26722     zoom: 15,
26723     mapTypeId: false,
26724     mapTypeControl: false,
26725     disableDoubleClickZoom: false,
26726     scrollwheel: true,
26727     streetViewControl: false,
26728     radius: 0,
26729     locationName: '',
26730     draggable: true,
26731     enableAutocomplete: false,
26732     enableReverseGeocode: true,
26733     markerTitle: '',
26734     
26735     getAutoCreate: function()
26736     {
26737
26738         var cfg = {
26739             tag: 'div',
26740             cls: 'roo-location-picker'
26741         };
26742         
26743         return cfg
26744     },
26745     
26746     initEvents: function(ct, position)
26747     {       
26748         if(!this.el.getWidth() || this.isApplied()){
26749             return;
26750         }
26751         
26752         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26753         
26754         this.initial();
26755     },
26756     
26757     initial: function()
26758     {
26759         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26760             this.fireEvent('loadexception', this);
26761             return;
26762         }
26763         
26764         if(!this.mapTypeId){
26765             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26766         }
26767         
26768         this.gMapContext = this.GMapContext();
26769         
26770         this.initOverlayView();
26771         
26772         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26773         
26774         var _this = this;
26775                 
26776         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26777             _this.setPosition(_this.gMapContext.marker.position);
26778         });
26779         
26780         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26781             _this.fireEvent('mapClick', this, event);
26782             
26783         });
26784
26785         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26786             _this.fireEvent('mapRightClick', this, event);
26787             
26788         });
26789         
26790         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26791             _this.fireEvent('markerClick', this, event);
26792             
26793         });
26794
26795         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26796             _this.fireEvent('markerRightClick', this, event);
26797             
26798         });
26799         
26800         this.setPosition(this.gMapContext.location);
26801         
26802         this.fireEvent('initial', this, this.gMapContext.location);
26803     },
26804     
26805     initOverlayView: function()
26806     {
26807         var _this = this;
26808         
26809         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26810             
26811             draw: function()
26812             {
26813                 _this.fireEvent('OverlayViewDraw', _this);
26814             },
26815             
26816             onAdd: function()
26817             {
26818                 _this.fireEvent('OverlayViewOnAdd', _this);
26819             },
26820             
26821             onRemove: function()
26822             {
26823                 _this.fireEvent('OverlayViewOnRemove', _this);
26824             },
26825             
26826             show: function(cpx)
26827             {
26828                 _this.fireEvent('OverlayViewShow', _this, cpx);
26829             },
26830             
26831             hide: function()
26832             {
26833                 _this.fireEvent('OverlayViewHide', _this);
26834             }
26835             
26836         });
26837     },
26838     
26839     fromLatLngToContainerPixel: function(event)
26840     {
26841         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26842     },
26843     
26844     isApplied: function() 
26845     {
26846         return this.getGmapContext() == false ? false : true;
26847     },
26848     
26849     getGmapContext: function() 
26850     {
26851         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26852     },
26853     
26854     GMapContext: function() 
26855     {
26856         var position = new google.maps.LatLng(this.latitude, this.longitude);
26857         
26858         var _map = new google.maps.Map(this.el.dom, {
26859             center: position,
26860             zoom: this.zoom,
26861             mapTypeId: this.mapTypeId,
26862             mapTypeControl: this.mapTypeControl,
26863             disableDoubleClickZoom: this.disableDoubleClickZoom,
26864             scrollwheel: this.scrollwheel,
26865             streetViewControl: this.streetViewControl,
26866             locationName: this.locationName,
26867             draggable: this.draggable,
26868             enableAutocomplete: this.enableAutocomplete,
26869             enableReverseGeocode: this.enableReverseGeocode
26870         });
26871         
26872         var _marker = new google.maps.Marker({
26873             position: position,
26874             map: _map,
26875             title: this.markerTitle,
26876             draggable: this.draggable
26877         });
26878         
26879         return {
26880             map: _map,
26881             marker: _marker,
26882             circle: null,
26883             location: position,
26884             radius: this.radius,
26885             locationName: this.locationName,
26886             addressComponents: {
26887                 formatted_address: null,
26888                 addressLine1: null,
26889                 addressLine2: null,
26890                 streetName: null,
26891                 streetNumber: null,
26892                 city: null,
26893                 district: null,
26894                 state: null,
26895                 stateOrProvince: null
26896             },
26897             settings: this,
26898             domContainer: this.el.dom,
26899             geodecoder: new google.maps.Geocoder()
26900         };
26901     },
26902     
26903     drawCircle: function(center, radius, options) 
26904     {
26905         if (this.gMapContext.circle != null) {
26906             this.gMapContext.circle.setMap(null);
26907         }
26908         if (radius > 0) {
26909             radius *= 1;
26910             options = Roo.apply({}, options, {
26911                 strokeColor: "#0000FF",
26912                 strokeOpacity: .35,
26913                 strokeWeight: 2,
26914                 fillColor: "#0000FF",
26915                 fillOpacity: .2
26916             });
26917             
26918             options.map = this.gMapContext.map;
26919             options.radius = radius;
26920             options.center = center;
26921             this.gMapContext.circle = new google.maps.Circle(options);
26922             return this.gMapContext.circle;
26923         }
26924         
26925         return null;
26926     },
26927     
26928     setPosition: function(location) 
26929     {
26930         this.gMapContext.location = location;
26931         this.gMapContext.marker.setPosition(location);
26932         this.gMapContext.map.panTo(location);
26933         this.drawCircle(location, this.gMapContext.radius, {});
26934         
26935         var _this = this;
26936         
26937         if (this.gMapContext.settings.enableReverseGeocode) {
26938             this.gMapContext.geodecoder.geocode({
26939                 latLng: this.gMapContext.location
26940             }, function(results, status) {
26941                 
26942                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26943                     _this.gMapContext.locationName = results[0].formatted_address;
26944                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26945                     
26946                     _this.fireEvent('positionchanged', this, location);
26947                 }
26948             });
26949             
26950             return;
26951         }
26952         
26953         this.fireEvent('positionchanged', this, location);
26954     },
26955     
26956     resize: function()
26957     {
26958         google.maps.event.trigger(this.gMapContext.map, "resize");
26959         
26960         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26961         
26962         this.fireEvent('resize', this);
26963     },
26964     
26965     setPositionByLatLng: function(latitude, longitude)
26966     {
26967         this.setPosition(new google.maps.LatLng(latitude, longitude));
26968     },
26969     
26970     getCurrentPosition: function() 
26971     {
26972         return {
26973             latitude: this.gMapContext.location.lat(),
26974             longitude: this.gMapContext.location.lng()
26975         };
26976     },
26977     
26978     getAddressName: function() 
26979     {
26980         return this.gMapContext.locationName;
26981     },
26982     
26983     getAddressComponents: function() 
26984     {
26985         return this.gMapContext.addressComponents;
26986     },
26987     
26988     address_component_from_google_geocode: function(address_components) 
26989     {
26990         var result = {};
26991         
26992         for (var i = 0; i < address_components.length; i++) {
26993             var component = address_components[i];
26994             if (component.types.indexOf("postal_code") >= 0) {
26995                 result.postalCode = component.short_name;
26996             } else if (component.types.indexOf("street_number") >= 0) {
26997                 result.streetNumber = component.short_name;
26998             } else if (component.types.indexOf("route") >= 0) {
26999                 result.streetName = component.short_name;
27000             } else if (component.types.indexOf("neighborhood") >= 0) {
27001                 result.city = component.short_name;
27002             } else if (component.types.indexOf("locality") >= 0) {
27003                 result.city = component.short_name;
27004             } else if (component.types.indexOf("sublocality") >= 0) {
27005                 result.district = component.short_name;
27006             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27007                 result.stateOrProvince = component.short_name;
27008             } else if (component.types.indexOf("country") >= 0) {
27009                 result.country = component.short_name;
27010             }
27011         }
27012         
27013         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27014         result.addressLine2 = "";
27015         return result;
27016     },
27017     
27018     setZoomLevel: function(zoom)
27019     {
27020         this.gMapContext.map.setZoom(zoom);
27021     },
27022     
27023     show: function()
27024     {
27025         if(!this.el){
27026             return;
27027         }
27028         
27029         this.el.show();
27030         
27031         this.resize();
27032         
27033         this.fireEvent('show', this);
27034     },
27035     
27036     hide: function()
27037     {
27038         if(!this.el){
27039             return;
27040         }
27041         
27042         this.el.hide();
27043         
27044         this.fireEvent('hide', this);
27045     }
27046     
27047 });
27048
27049 Roo.apply(Roo.bootstrap.LocationPicker, {
27050     
27051     OverlayView : function(map, options)
27052     {
27053         options = options || {};
27054         
27055         this.setMap(map);
27056     }
27057     
27058     
27059 });/*
27060  * - LGPL
27061  *
27062  * Alert
27063  * 
27064  */
27065
27066 /**
27067  * @class Roo.bootstrap.Alert
27068  * @extends Roo.bootstrap.Component
27069  * Bootstrap Alert class
27070  * @cfg {String} title The title of alert
27071  * @cfg {String} html The content of alert
27072  * @cfg {String} weight (  success | info | warning | danger )
27073  * @cfg {String} faicon font-awesomeicon
27074  * 
27075  * @constructor
27076  * Create a new alert
27077  * @param {Object} config The config object
27078  */
27079
27080
27081 Roo.bootstrap.Alert = function(config){
27082     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27083     
27084 };
27085
27086 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27087     
27088     title: '',
27089     html: '',
27090     weight: false,
27091     faicon: false,
27092     
27093     getAutoCreate : function()
27094     {
27095         
27096         var cfg = {
27097             tag : 'div',
27098             cls : 'alert',
27099             cn : [
27100                 {
27101                     tag : 'i',
27102                     cls : 'roo-alert-icon'
27103                     
27104                 },
27105                 {
27106                     tag : 'b',
27107                     cls : 'roo-alert-title',
27108                     html : this.title
27109                 },
27110                 {
27111                     tag : 'span',
27112                     cls : 'roo-alert-text',
27113                     html : this.html
27114                 }
27115             ]
27116         };
27117         
27118         if(this.faicon){
27119             cfg.cn[0].cls += ' fa ' + this.faicon;
27120         }
27121         
27122         if(this.weight){
27123             cfg.cls += ' alert-' + this.weight;
27124         }
27125         
27126         return cfg;
27127     },
27128     
27129     initEvents: function() 
27130     {
27131         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27132     },
27133     
27134     setTitle : function(str)
27135     {
27136         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27137     },
27138     
27139     setText : function(str)
27140     {
27141         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27142     },
27143     
27144     setWeight : function(weight)
27145     {
27146         if(this.weight){
27147             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27148         }
27149         
27150         this.weight = weight;
27151         
27152         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27153     },
27154     
27155     setIcon : function(icon)
27156     {
27157         if(this.faicon){
27158             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27159         }
27160         
27161         this.faicon = icon;
27162         
27163         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27164     },
27165     
27166     hide: function() 
27167     {
27168         this.el.hide();   
27169     },
27170     
27171     show: function() 
27172     {  
27173         this.el.show();   
27174     }
27175     
27176 });
27177
27178  
27179 /*
27180 * Licence: LGPL
27181 */
27182
27183 /**
27184  * @class Roo.bootstrap.UploadCropbox
27185  * @extends Roo.bootstrap.Component
27186  * Bootstrap UploadCropbox class
27187  * @cfg {String} emptyText show when image has been loaded
27188  * @cfg {String} rotateNotify show when image too small to rotate
27189  * @cfg {Number} errorTimeout default 3000
27190  * @cfg {Number} minWidth default 300
27191  * @cfg {Number} minHeight default 300
27192  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27193  * @cfg {Boolean} isDocument (true|false) default false
27194  * @cfg {String} url action url
27195  * @cfg {String} paramName default 'imageUpload'
27196  * @cfg {String} method default POST
27197  * @cfg {Boolean} loadMask (true|false) default true
27198  * @cfg {Boolean} loadingText default 'Loading...'
27199  * 
27200  * @constructor
27201  * Create a new UploadCropbox
27202  * @param {Object} config The config object
27203  */
27204
27205 Roo.bootstrap.UploadCropbox = function(config){
27206     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27207     
27208     this.addEvents({
27209         /**
27210          * @event beforeselectfile
27211          * Fire before select file
27212          * @param {Roo.bootstrap.UploadCropbox} this
27213          */
27214         "beforeselectfile" : true,
27215         /**
27216          * @event initial
27217          * Fire after initEvent
27218          * @param {Roo.bootstrap.UploadCropbox} this
27219          */
27220         "initial" : true,
27221         /**
27222          * @event crop
27223          * Fire after initEvent
27224          * @param {Roo.bootstrap.UploadCropbox} this
27225          * @param {String} data
27226          */
27227         "crop" : true,
27228         /**
27229          * @event prepare
27230          * Fire when preparing the file data
27231          * @param {Roo.bootstrap.UploadCropbox} this
27232          * @param {Object} file
27233          */
27234         "prepare" : true,
27235         /**
27236          * @event exception
27237          * Fire when get exception
27238          * @param {Roo.bootstrap.UploadCropbox} this
27239          * @param {XMLHttpRequest} xhr
27240          */
27241         "exception" : true,
27242         /**
27243          * @event beforeloadcanvas
27244          * Fire before load the canvas
27245          * @param {Roo.bootstrap.UploadCropbox} this
27246          * @param {String} src
27247          */
27248         "beforeloadcanvas" : true,
27249         /**
27250          * @event trash
27251          * Fire when trash image
27252          * @param {Roo.bootstrap.UploadCropbox} this
27253          */
27254         "trash" : true,
27255         /**
27256          * @event download
27257          * Fire when download the image
27258          * @param {Roo.bootstrap.UploadCropbox} this
27259          */
27260         "download" : true,
27261         /**
27262          * @event footerbuttonclick
27263          * Fire when footerbuttonclick
27264          * @param {Roo.bootstrap.UploadCropbox} this
27265          * @param {String} type
27266          */
27267         "footerbuttonclick" : true,
27268         /**
27269          * @event resize
27270          * Fire when resize
27271          * @param {Roo.bootstrap.UploadCropbox} this
27272          */
27273         "resize" : true,
27274         /**
27275          * @event rotate
27276          * Fire when rotate the image
27277          * @param {Roo.bootstrap.UploadCropbox} this
27278          * @param {String} pos
27279          */
27280         "rotate" : true,
27281         /**
27282          * @event inspect
27283          * Fire when inspect the file
27284          * @param {Roo.bootstrap.UploadCropbox} this
27285          * @param {Object} file
27286          */
27287         "inspect" : true,
27288         /**
27289          * @event upload
27290          * Fire when xhr upload the file
27291          * @param {Roo.bootstrap.UploadCropbox} this
27292          * @param {Object} data
27293          */
27294         "upload" : true,
27295         /**
27296          * @event arrange
27297          * Fire when arrange the file data
27298          * @param {Roo.bootstrap.UploadCropbox} this
27299          * @param {Object} formData
27300          */
27301         "arrange" : true
27302     });
27303     
27304     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27305 };
27306
27307 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27308     
27309     emptyText : 'Click to upload image',
27310     rotateNotify : 'Image is too small to rotate',
27311     errorTimeout : 3000,
27312     scale : 0,
27313     baseScale : 1,
27314     rotate : 0,
27315     dragable : false,
27316     pinching : false,
27317     mouseX : 0,
27318     mouseY : 0,
27319     cropData : false,
27320     minWidth : 300,
27321     minHeight : 300,
27322     file : false,
27323     exif : {},
27324     baseRotate : 1,
27325     cropType : 'image/jpeg',
27326     buttons : false,
27327     canvasLoaded : false,
27328     isDocument : false,
27329     method : 'POST',
27330     paramName : 'imageUpload',
27331     loadMask : true,
27332     loadingText : 'Loading...',
27333     maskEl : false,
27334     
27335     getAutoCreate : function()
27336     {
27337         var cfg = {
27338             tag : 'div',
27339             cls : 'roo-upload-cropbox',
27340             cn : [
27341                 {
27342                     tag : 'input',
27343                     cls : 'roo-upload-cropbox-selector',
27344                     type : 'file'
27345                 },
27346                 {
27347                     tag : 'div',
27348                     cls : 'roo-upload-cropbox-body',
27349                     style : 'cursor:pointer',
27350                     cn : [
27351                         {
27352                             tag : 'div',
27353                             cls : 'roo-upload-cropbox-preview'
27354                         },
27355                         {
27356                             tag : 'div',
27357                             cls : 'roo-upload-cropbox-thumb'
27358                         },
27359                         {
27360                             tag : 'div',
27361                             cls : 'roo-upload-cropbox-empty-notify',
27362                             html : this.emptyText
27363                         },
27364                         {
27365                             tag : 'div',
27366                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27367                             html : this.rotateNotify
27368                         }
27369                     ]
27370                 },
27371                 {
27372                     tag : 'div',
27373                     cls : 'roo-upload-cropbox-footer',
27374                     cn : {
27375                         tag : 'div',
27376                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27377                         cn : []
27378                     }
27379                 }
27380             ]
27381         };
27382         
27383         return cfg;
27384     },
27385     
27386     onRender : function(ct, position)
27387     {
27388         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27389         
27390         if (this.buttons.length) {
27391             
27392             Roo.each(this.buttons, function(bb) {
27393                 
27394                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27395                 
27396                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27397                 
27398             }, this);
27399         }
27400         
27401         if(this.loadMask){
27402             this.maskEl = this.el;
27403         }
27404     },
27405     
27406     initEvents : function()
27407     {
27408         this.urlAPI = (window.createObjectURL && window) || 
27409                                 (window.URL && URL.revokeObjectURL && URL) || 
27410                                 (window.webkitURL && webkitURL);
27411                         
27412         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27413         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27414         
27415         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27416         this.selectorEl.hide();
27417         
27418         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27419         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27420         
27421         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27422         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27423         this.thumbEl.hide();
27424         
27425         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27426         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27427         
27428         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27429         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27430         this.errorEl.hide();
27431         
27432         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27433         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27434         this.footerEl.hide();
27435         
27436         this.setThumbBoxSize();
27437         
27438         this.bind();
27439         
27440         this.resize();
27441         
27442         this.fireEvent('initial', this);
27443     },
27444
27445     bind : function()
27446     {
27447         var _this = this;
27448         
27449         window.addEventListener("resize", function() { _this.resize(); } );
27450         
27451         this.bodyEl.on('click', this.beforeSelectFile, this);
27452         
27453         if(Roo.isTouch){
27454             this.bodyEl.on('touchstart', this.onTouchStart, this);
27455             this.bodyEl.on('touchmove', this.onTouchMove, this);
27456             this.bodyEl.on('touchend', this.onTouchEnd, this);
27457         }
27458         
27459         if(!Roo.isTouch){
27460             this.bodyEl.on('mousedown', this.onMouseDown, this);
27461             this.bodyEl.on('mousemove', this.onMouseMove, this);
27462             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27463             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27464             Roo.get(document).on('mouseup', this.onMouseUp, this);
27465         }
27466         
27467         this.selectorEl.on('change', this.onFileSelected, this);
27468     },
27469     
27470     reset : function()
27471     {    
27472         this.scale = 0;
27473         this.baseScale = 1;
27474         this.rotate = 0;
27475         this.baseRotate = 1;
27476         this.dragable = false;
27477         this.pinching = false;
27478         this.mouseX = 0;
27479         this.mouseY = 0;
27480         this.cropData = false;
27481         this.notifyEl.dom.innerHTML = this.emptyText;
27482         
27483         this.selectorEl.dom.value = '';
27484         
27485     },
27486     
27487     resize : function()
27488     {
27489         if(this.fireEvent('resize', this) != false){
27490             this.setThumbBoxPosition();
27491             this.setCanvasPosition();
27492         }
27493     },
27494     
27495     onFooterButtonClick : function(e, el, o, type)
27496     {
27497         switch (type) {
27498             case 'rotate-left' :
27499                 this.onRotateLeft(e);
27500                 break;
27501             case 'rotate-right' :
27502                 this.onRotateRight(e);
27503                 break;
27504             case 'picture' :
27505                 this.beforeSelectFile(e);
27506                 break;
27507             case 'trash' :
27508                 this.trash(e);
27509                 break;
27510             case 'crop' :
27511                 this.crop(e);
27512                 break;
27513             case 'download' :
27514                 this.download(e);
27515                 break;
27516             default :
27517                 break;
27518         }
27519         
27520         this.fireEvent('footerbuttonclick', this, type);
27521     },
27522     
27523     beforeSelectFile : function(e)
27524     {
27525         e.preventDefault();
27526         
27527         if(this.fireEvent('beforeselectfile', this) != false){
27528             this.selectorEl.dom.click();
27529         }
27530     },
27531     
27532     onFileSelected : function(e)
27533     {
27534         e.preventDefault();
27535         
27536         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27537             return;
27538         }
27539         
27540         var file = this.selectorEl.dom.files[0];
27541         
27542         if(this.fireEvent('inspect', this, file) != false){
27543             this.prepare(file);
27544         }
27545         
27546     },
27547     
27548     trash : function(e)
27549     {
27550         this.fireEvent('trash', this);
27551     },
27552     
27553     download : function(e)
27554     {
27555         this.fireEvent('download', this);
27556     },
27557     
27558     loadCanvas : function(src)
27559     {   
27560         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27561             
27562             this.reset();
27563             
27564             this.imageEl = document.createElement('img');
27565             
27566             var _this = this;
27567             
27568             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27569             
27570             this.imageEl.src = src;
27571         }
27572     },
27573     
27574     onLoadCanvas : function()
27575     {   
27576         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27577         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27578         
27579         this.bodyEl.un('click', this.beforeSelectFile, this);
27580         
27581         this.notifyEl.hide();
27582         this.thumbEl.show();
27583         this.footerEl.show();
27584         
27585         this.baseRotateLevel();
27586         
27587         if(this.isDocument){
27588             this.setThumbBoxSize();
27589         }
27590         
27591         this.setThumbBoxPosition();
27592         
27593         this.baseScaleLevel();
27594         
27595         this.draw();
27596         
27597         this.resize();
27598         
27599         this.canvasLoaded = true;
27600         
27601         if(this.loadMask){
27602             this.maskEl.unmask();
27603         }
27604         
27605     },
27606     
27607     setCanvasPosition : function()
27608     {   
27609         if(!this.canvasEl){
27610             return;
27611         }
27612         
27613         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27614         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27615         
27616         this.previewEl.setLeft(pw);
27617         this.previewEl.setTop(ph);
27618         
27619     },
27620     
27621     onMouseDown : function(e)
27622     {   
27623         e.stopEvent();
27624         
27625         this.dragable = true;
27626         this.pinching = false;
27627         
27628         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27629             this.dragable = false;
27630             return;
27631         }
27632         
27633         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27634         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27635         
27636     },
27637     
27638     onMouseMove : function(e)
27639     {   
27640         e.stopEvent();
27641         
27642         if(!this.canvasLoaded){
27643             return;
27644         }
27645         
27646         if (!this.dragable){
27647             return;
27648         }
27649         
27650         var minX = Math.ceil(this.thumbEl.getLeft(true));
27651         var minY = Math.ceil(this.thumbEl.getTop(true));
27652         
27653         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27654         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27655         
27656         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27657         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27658         
27659         x = x - this.mouseX;
27660         y = y - this.mouseY;
27661         
27662         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27663         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27664         
27665         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27666         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27667         
27668         this.previewEl.setLeft(bgX);
27669         this.previewEl.setTop(bgY);
27670         
27671         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27672         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27673     },
27674     
27675     onMouseUp : function(e)
27676     {   
27677         e.stopEvent();
27678         
27679         this.dragable = false;
27680     },
27681     
27682     onMouseWheel : function(e)
27683     {   
27684         e.stopEvent();
27685         
27686         this.startScale = this.scale;
27687         
27688         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27689         
27690         if(!this.zoomable()){
27691             this.scale = this.startScale;
27692             return;
27693         }
27694         
27695         this.draw();
27696         
27697         return;
27698     },
27699     
27700     zoomable : function()
27701     {
27702         var minScale = this.thumbEl.getWidth() / this.minWidth;
27703         
27704         if(this.minWidth < this.minHeight){
27705             minScale = this.thumbEl.getHeight() / this.minHeight;
27706         }
27707         
27708         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27709         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27710         
27711         if(
27712                 this.isDocument &&
27713                 (this.rotate == 0 || this.rotate == 180) && 
27714                 (
27715                     width > this.imageEl.OriginWidth || 
27716                     height > this.imageEl.OriginHeight ||
27717                     (width < this.minWidth && height < this.minHeight)
27718                 )
27719         ){
27720             return false;
27721         }
27722         
27723         if(
27724                 this.isDocument &&
27725                 (this.rotate == 90 || this.rotate == 270) && 
27726                 (
27727                     width > this.imageEl.OriginWidth || 
27728                     height > this.imageEl.OriginHeight ||
27729                     (width < this.minHeight && height < this.minWidth)
27730                 )
27731         ){
27732             return false;
27733         }
27734         
27735         if(
27736                 !this.isDocument &&
27737                 (this.rotate == 0 || this.rotate == 180) && 
27738                 (
27739                     width < this.minWidth || 
27740                     width > this.imageEl.OriginWidth || 
27741                     height < this.minHeight || 
27742                     height > this.imageEl.OriginHeight
27743                 )
27744         ){
27745             return false;
27746         }
27747         
27748         if(
27749                 !this.isDocument &&
27750                 (this.rotate == 90 || this.rotate == 270) && 
27751                 (
27752                     width < this.minHeight || 
27753                     width > this.imageEl.OriginWidth || 
27754                     height < this.minWidth || 
27755                     height > this.imageEl.OriginHeight
27756                 )
27757         ){
27758             return false;
27759         }
27760         
27761         return true;
27762         
27763     },
27764     
27765     onRotateLeft : function(e)
27766     {   
27767         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27768             
27769             var minScale = this.thumbEl.getWidth() / this.minWidth;
27770             
27771             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27772             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27773             
27774             this.startScale = this.scale;
27775             
27776             while (this.getScaleLevel() < minScale){
27777             
27778                 this.scale = this.scale + 1;
27779                 
27780                 if(!this.zoomable()){
27781                     break;
27782                 }
27783                 
27784                 if(
27785                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27786                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27787                 ){
27788                     continue;
27789                 }
27790                 
27791                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27792
27793                 this.draw();
27794                 
27795                 return;
27796             }
27797             
27798             this.scale = this.startScale;
27799             
27800             this.onRotateFail();
27801             
27802             return false;
27803         }
27804         
27805         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27806
27807         if(this.isDocument){
27808             this.setThumbBoxSize();
27809             this.setThumbBoxPosition();
27810             this.setCanvasPosition();
27811         }
27812         
27813         this.draw();
27814         
27815         this.fireEvent('rotate', this, 'left');
27816         
27817     },
27818     
27819     onRotateRight : function(e)
27820     {
27821         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27822             
27823             var minScale = this.thumbEl.getWidth() / this.minWidth;
27824         
27825             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27826             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27827             
27828             this.startScale = this.scale;
27829             
27830             while (this.getScaleLevel() < minScale){
27831             
27832                 this.scale = this.scale + 1;
27833                 
27834                 if(!this.zoomable()){
27835                     break;
27836                 }
27837                 
27838                 if(
27839                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27840                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27841                 ){
27842                     continue;
27843                 }
27844                 
27845                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27846
27847                 this.draw();
27848                 
27849                 return;
27850             }
27851             
27852             this.scale = this.startScale;
27853             
27854             this.onRotateFail();
27855             
27856             return false;
27857         }
27858         
27859         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27860
27861         if(this.isDocument){
27862             this.setThumbBoxSize();
27863             this.setThumbBoxPosition();
27864             this.setCanvasPosition();
27865         }
27866         
27867         this.draw();
27868         
27869         this.fireEvent('rotate', this, 'right');
27870     },
27871     
27872     onRotateFail : function()
27873     {
27874         this.errorEl.show(true);
27875         
27876         var _this = this;
27877         
27878         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27879     },
27880     
27881     draw : function()
27882     {
27883         this.previewEl.dom.innerHTML = '';
27884         
27885         var canvasEl = document.createElement("canvas");
27886         
27887         var contextEl = canvasEl.getContext("2d");
27888         
27889         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27890         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27891         var center = this.imageEl.OriginWidth / 2;
27892         
27893         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27894             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27895             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27896             center = this.imageEl.OriginHeight / 2;
27897         }
27898         
27899         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27900         
27901         contextEl.translate(center, center);
27902         contextEl.rotate(this.rotate * Math.PI / 180);
27903
27904         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27905         
27906         this.canvasEl = document.createElement("canvas");
27907         
27908         this.contextEl = this.canvasEl.getContext("2d");
27909         
27910         switch (this.rotate) {
27911             case 0 :
27912                 
27913                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27914                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27915                 
27916                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27917                 
27918                 break;
27919             case 90 : 
27920                 
27921                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27922                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27923                 
27924                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27925                     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);
27926                     break;
27927                 }
27928                 
27929                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27930                 
27931                 break;
27932             case 180 :
27933                 
27934                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27935                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27936                 
27937                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27938                     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);
27939                     break;
27940                 }
27941                 
27942                 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);
27943                 
27944                 break;
27945             case 270 :
27946                 
27947                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27948                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27949         
27950                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27951                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27952                     break;
27953                 }
27954                 
27955                 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);
27956                 
27957                 break;
27958             default : 
27959                 break;
27960         }
27961         
27962         this.previewEl.appendChild(this.canvasEl);
27963         
27964         this.setCanvasPosition();
27965     },
27966     
27967     crop : function()
27968     {
27969         if(!this.canvasLoaded){
27970             return;
27971         }
27972         
27973         var imageCanvas = document.createElement("canvas");
27974         
27975         var imageContext = imageCanvas.getContext("2d");
27976         
27977         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27978         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27979         
27980         var center = imageCanvas.width / 2;
27981         
27982         imageContext.translate(center, center);
27983         
27984         imageContext.rotate(this.rotate * Math.PI / 180);
27985         
27986         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27987         
27988         var canvas = document.createElement("canvas");
27989         
27990         var context = canvas.getContext("2d");
27991                 
27992         canvas.width = this.minWidth;
27993         canvas.height = this.minHeight;
27994
27995         switch (this.rotate) {
27996             case 0 :
27997                 
27998                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27999                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28000                 
28001                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28002                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28003                 
28004                 var targetWidth = this.minWidth - 2 * x;
28005                 var targetHeight = this.minHeight - 2 * y;
28006                 
28007                 var scale = 1;
28008                 
28009                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28010                     scale = targetWidth / width;
28011                 }
28012                 
28013                 if(x > 0 && y == 0){
28014                     scale = targetHeight / height;
28015                 }
28016                 
28017                 if(x > 0 && y > 0){
28018                     scale = targetWidth / width;
28019                     
28020                     if(width < height){
28021                         scale = targetHeight / height;
28022                     }
28023                 }
28024                 
28025                 context.scale(scale, scale);
28026                 
28027                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28028                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28029
28030                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28031                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28032
28033                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28034                 
28035                 break;
28036             case 90 : 
28037                 
28038                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28039                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28040                 
28041                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28042                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28043                 
28044                 var targetWidth = this.minWidth - 2 * x;
28045                 var targetHeight = this.minHeight - 2 * y;
28046                 
28047                 var scale = 1;
28048                 
28049                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28050                     scale = targetWidth / width;
28051                 }
28052                 
28053                 if(x > 0 && y == 0){
28054                     scale = targetHeight / height;
28055                 }
28056                 
28057                 if(x > 0 && y > 0){
28058                     scale = targetWidth / width;
28059                     
28060                     if(width < height){
28061                         scale = targetHeight / height;
28062                     }
28063                 }
28064                 
28065                 context.scale(scale, scale);
28066                 
28067                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28068                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28069
28070                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28071                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28072                 
28073                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28074                 
28075                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28076                 
28077                 break;
28078             case 180 :
28079                 
28080                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28081                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28082                 
28083                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28084                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28085                 
28086                 var targetWidth = this.minWidth - 2 * x;
28087                 var targetHeight = this.minHeight - 2 * y;
28088                 
28089                 var scale = 1;
28090                 
28091                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28092                     scale = targetWidth / width;
28093                 }
28094                 
28095                 if(x > 0 && y == 0){
28096                     scale = targetHeight / height;
28097                 }
28098                 
28099                 if(x > 0 && y > 0){
28100                     scale = targetWidth / width;
28101                     
28102                     if(width < height){
28103                         scale = targetHeight / height;
28104                     }
28105                 }
28106                 
28107                 context.scale(scale, scale);
28108                 
28109                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28110                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28111
28112                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28113                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28114
28115                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28116                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28117                 
28118                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28119                 
28120                 break;
28121             case 270 :
28122                 
28123                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28124                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28125                 
28126                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28127                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28128                 
28129                 var targetWidth = this.minWidth - 2 * x;
28130                 var targetHeight = this.minHeight - 2 * y;
28131                 
28132                 var scale = 1;
28133                 
28134                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28135                     scale = targetWidth / width;
28136                 }
28137                 
28138                 if(x > 0 && y == 0){
28139                     scale = targetHeight / height;
28140                 }
28141                 
28142                 if(x > 0 && y > 0){
28143                     scale = targetWidth / width;
28144                     
28145                     if(width < height){
28146                         scale = targetHeight / height;
28147                     }
28148                 }
28149                 
28150                 context.scale(scale, scale);
28151                 
28152                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28153                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28154
28155                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28156                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28157                 
28158                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28159                 
28160                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28161                 
28162                 break;
28163             default : 
28164                 break;
28165         }
28166         
28167         this.cropData = canvas.toDataURL(this.cropType);
28168         
28169         if(this.fireEvent('crop', this, this.cropData) !== false){
28170             this.process(this.file, this.cropData);
28171         }
28172         
28173         return;
28174         
28175     },
28176     
28177     setThumbBoxSize : function()
28178     {
28179         var width, height;
28180         
28181         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28182             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28183             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28184             
28185             this.minWidth = width;
28186             this.minHeight = height;
28187             
28188             if(this.rotate == 90 || this.rotate == 270){
28189                 this.minWidth = height;
28190                 this.minHeight = width;
28191             }
28192         }
28193         
28194         height = 300;
28195         width = Math.ceil(this.minWidth * height / this.minHeight);
28196         
28197         if(this.minWidth > this.minHeight){
28198             width = 300;
28199             height = Math.ceil(this.minHeight * width / this.minWidth);
28200         }
28201         
28202         this.thumbEl.setStyle({
28203             width : width + 'px',
28204             height : height + 'px'
28205         });
28206
28207         return;
28208             
28209     },
28210     
28211     setThumbBoxPosition : function()
28212     {
28213         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28214         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28215         
28216         this.thumbEl.setLeft(x);
28217         this.thumbEl.setTop(y);
28218         
28219     },
28220     
28221     baseRotateLevel : function()
28222     {
28223         this.baseRotate = 1;
28224         
28225         if(
28226                 typeof(this.exif) != 'undefined' &&
28227                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28228                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28229         ){
28230             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28231         }
28232         
28233         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28234         
28235     },
28236     
28237     baseScaleLevel : function()
28238     {
28239         var width, height;
28240         
28241         if(this.isDocument){
28242             
28243             if(this.baseRotate == 6 || this.baseRotate == 8){
28244             
28245                 height = this.thumbEl.getHeight();
28246                 this.baseScale = height / this.imageEl.OriginWidth;
28247
28248                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28249                     width = this.thumbEl.getWidth();
28250                     this.baseScale = width / this.imageEl.OriginHeight;
28251                 }
28252
28253                 return;
28254             }
28255
28256             height = this.thumbEl.getHeight();
28257             this.baseScale = height / this.imageEl.OriginHeight;
28258
28259             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28260                 width = this.thumbEl.getWidth();
28261                 this.baseScale = width / this.imageEl.OriginWidth;
28262             }
28263
28264             return;
28265         }
28266         
28267         if(this.baseRotate == 6 || this.baseRotate == 8){
28268             
28269             width = this.thumbEl.getHeight();
28270             this.baseScale = width / this.imageEl.OriginHeight;
28271             
28272             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28273                 height = this.thumbEl.getWidth();
28274                 this.baseScale = height / this.imageEl.OriginHeight;
28275             }
28276             
28277             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28278                 height = this.thumbEl.getWidth();
28279                 this.baseScale = height / this.imageEl.OriginHeight;
28280                 
28281                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28282                     width = this.thumbEl.getHeight();
28283                     this.baseScale = width / this.imageEl.OriginWidth;
28284                 }
28285             }
28286             
28287             return;
28288         }
28289         
28290         width = this.thumbEl.getWidth();
28291         this.baseScale = width / this.imageEl.OriginWidth;
28292         
28293         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28294             height = this.thumbEl.getHeight();
28295             this.baseScale = height / this.imageEl.OriginHeight;
28296         }
28297         
28298         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28299             
28300             height = this.thumbEl.getHeight();
28301             this.baseScale = height / this.imageEl.OriginHeight;
28302             
28303             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28304                 width = this.thumbEl.getWidth();
28305                 this.baseScale = width / this.imageEl.OriginWidth;
28306             }
28307             
28308         }
28309         
28310         return;
28311     },
28312     
28313     getScaleLevel : function()
28314     {
28315         return this.baseScale * Math.pow(1.1, this.scale);
28316     },
28317     
28318     onTouchStart : function(e)
28319     {
28320         if(!this.canvasLoaded){
28321             this.beforeSelectFile(e);
28322             return;
28323         }
28324         
28325         var touches = e.browserEvent.touches;
28326         
28327         if(!touches){
28328             return;
28329         }
28330         
28331         if(touches.length == 1){
28332             this.onMouseDown(e);
28333             return;
28334         }
28335         
28336         if(touches.length != 2){
28337             return;
28338         }
28339         
28340         var coords = [];
28341         
28342         for(var i = 0, finger; finger = touches[i]; i++){
28343             coords.push(finger.pageX, finger.pageY);
28344         }
28345         
28346         var x = Math.pow(coords[0] - coords[2], 2);
28347         var y = Math.pow(coords[1] - coords[3], 2);
28348         
28349         this.startDistance = Math.sqrt(x + y);
28350         
28351         this.startScale = this.scale;
28352         
28353         this.pinching = true;
28354         this.dragable = false;
28355         
28356     },
28357     
28358     onTouchMove : function(e)
28359     {
28360         if(!this.pinching && !this.dragable){
28361             return;
28362         }
28363         
28364         var touches = e.browserEvent.touches;
28365         
28366         if(!touches){
28367             return;
28368         }
28369         
28370         if(this.dragable){
28371             this.onMouseMove(e);
28372             return;
28373         }
28374         
28375         var coords = [];
28376         
28377         for(var i = 0, finger; finger = touches[i]; i++){
28378             coords.push(finger.pageX, finger.pageY);
28379         }
28380         
28381         var x = Math.pow(coords[0] - coords[2], 2);
28382         var y = Math.pow(coords[1] - coords[3], 2);
28383         
28384         this.endDistance = Math.sqrt(x + y);
28385         
28386         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28387         
28388         if(!this.zoomable()){
28389             this.scale = this.startScale;
28390             return;
28391         }
28392         
28393         this.draw();
28394         
28395     },
28396     
28397     onTouchEnd : function(e)
28398     {
28399         this.pinching = false;
28400         this.dragable = false;
28401         
28402     },
28403     
28404     process : function(file, crop)
28405     {
28406         if(this.loadMask){
28407             this.maskEl.mask(this.loadingText);
28408         }
28409         
28410         this.xhr = new XMLHttpRequest();
28411         
28412         file.xhr = this.xhr;
28413
28414         this.xhr.open(this.method, this.url, true);
28415         
28416         var headers = {
28417             "Accept": "application/json",
28418             "Cache-Control": "no-cache",
28419             "X-Requested-With": "XMLHttpRequest"
28420         };
28421         
28422         for (var headerName in headers) {
28423             var headerValue = headers[headerName];
28424             if (headerValue) {
28425                 this.xhr.setRequestHeader(headerName, headerValue);
28426             }
28427         }
28428         
28429         var _this = this;
28430         
28431         this.xhr.onload = function()
28432         {
28433             _this.xhrOnLoad(_this.xhr);
28434         }
28435         
28436         this.xhr.onerror = function()
28437         {
28438             _this.xhrOnError(_this.xhr);
28439         }
28440         
28441         var formData = new FormData();
28442
28443         formData.append('returnHTML', 'NO');
28444         
28445         if(crop){
28446             formData.append('crop', crop);
28447         }
28448         
28449         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28450             formData.append(this.paramName, file, file.name);
28451         }
28452         
28453         if(typeof(file.filename) != 'undefined'){
28454             formData.append('filename', file.filename);
28455         }
28456         
28457         if(typeof(file.mimetype) != 'undefined'){
28458             formData.append('mimetype', file.mimetype);
28459         }
28460         
28461         if(this.fireEvent('arrange', this, formData) != false){
28462             this.xhr.send(formData);
28463         };
28464     },
28465     
28466     xhrOnLoad : function(xhr)
28467     {
28468         if(this.loadMask){
28469             this.maskEl.unmask();
28470         }
28471         
28472         if (xhr.readyState !== 4) {
28473             this.fireEvent('exception', this, xhr);
28474             return;
28475         }
28476
28477         var response = Roo.decode(xhr.responseText);
28478         
28479         if(!response.success){
28480             this.fireEvent('exception', this, xhr);
28481             return;
28482         }
28483         
28484         var response = Roo.decode(xhr.responseText);
28485         
28486         this.fireEvent('upload', this, response);
28487         
28488     },
28489     
28490     xhrOnError : function()
28491     {
28492         if(this.loadMask){
28493             this.maskEl.unmask();
28494         }
28495         
28496         Roo.log('xhr on error');
28497         
28498         var response = Roo.decode(xhr.responseText);
28499           
28500         Roo.log(response);
28501         
28502     },
28503     
28504     prepare : function(file)
28505     {   
28506         if(this.loadMask){
28507             this.maskEl.mask(this.loadingText);
28508         }
28509         
28510         this.file = false;
28511         this.exif = {};
28512         
28513         if(typeof(file) === 'string'){
28514             this.loadCanvas(file);
28515             return;
28516         }
28517         
28518         if(!file || !this.urlAPI){
28519             return;
28520         }
28521         
28522         this.file = file;
28523         this.cropType = file.type;
28524         
28525         var _this = this;
28526         
28527         if(this.fireEvent('prepare', this, this.file) != false){
28528             
28529             var reader = new FileReader();
28530             
28531             reader.onload = function (e) {
28532                 if (e.target.error) {
28533                     Roo.log(e.target.error);
28534                     return;
28535                 }
28536                 
28537                 var buffer = e.target.result,
28538                     dataView = new DataView(buffer),
28539                     offset = 2,
28540                     maxOffset = dataView.byteLength - 4,
28541                     markerBytes,
28542                     markerLength;
28543                 
28544                 if (dataView.getUint16(0) === 0xffd8) {
28545                     while (offset < maxOffset) {
28546                         markerBytes = dataView.getUint16(offset);
28547                         
28548                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28549                             markerLength = dataView.getUint16(offset + 2) + 2;
28550                             if (offset + markerLength > dataView.byteLength) {
28551                                 Roo.log('Invalid meta data: Invalid segment size.');
28552                                 break;
28553                             }
28554                             
28555                             if(markerBytes == 0xffe1){
28556                                 _this.parseExifData(
28557                                     dataView,
28558                                     offset,
28559                                     markerLength
28560                                 );
28561                             }
28562                             
28563                             offset += markerLength;
28564                             
28565                             continue;
28566                         }
28567                         
28568                         break;
28569                     }
28570                     
28571                 }
28572                 
28573                 var url = _this.urlAPI.createObjectURL(_this.file);
28574                 
28575                 _this.loadCanvas(url);
28576                 
28577                 return;
28578             }
28579             
28580             reader.readAsArrayBuffer(this.file);
28581             
28582         }
28583         
28584     },
28585     
28586     parseExifData : function(dataView, offset, length)
28587     {
28588         var tiffOffset = offset + 10,
28589             littleEndian,
28590             dirOffset;
28591     
28592         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28593             // No Exif data, might be XMP data instead
28594             return;
28595         }
28596         
28597         // Check for the ASCII code for "Exif" (0x45786966):
28598         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28599             // No Exif data, might be XMP data instead
28600             return;
28601         }
28602         if (tiffOffset + 8 > dataView.byteLength) {
28603             Roo.log('Invalid Exif data: Invalid segment size.');
28604             return;
28605         }
28606         // Check for the two null bytes:
28607         if (dataView.getUint16(offset + 8) !== 0x0000) {
28608             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28609             return;
28610         }
28611         // Check the byte alignment:
28612         switch (dataView.getUint16(tiffOffset)) {
28613         case 0x4949:
28614             littleEndian = true;
28615             break;
28616         case 0x4D4D:
28617             littleEndian = false;
28618             break;
28619         default:
28620             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28621             return;
28622         }
28623         // Check for the TIFF tag marker (0x002A):
28624         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28625             Roo.log('Invalid Exif data: Missing TIFF marker.');
28626             return;
28627         }
28628         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28629         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28630         
28631         this.parseExifTags(
28632             dataView,
28633             tiffOffset,
28634             tiffOffset + dirOffset,
28635             littleEndian
28636         );
28637     },
28638     
28639     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28640     {
28641         var tagsNumber,
28642             dirEndOffset,
28643             i;
28644         if (dirOffset + 6 > dataView.byteLength) {
28645             Roo.log('Invalid Exif data: Invalid directory offset.');
28646             return;
28647         }
28648         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28649         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28650         if (dirEndOffset + 4 > dataView.byteLength) {
28651             Roo.log('Invalid Exif data: Invalid directory size.');
28652             return;
28653         }
28654         for (i = 0; i < tagsNumber; i += 1) {
28655             this.parseExifTag(
28656                 dataView,
28657                 tiffOffset,
28658                 dirOffset + 2 + 12 * i, // tag offset
28659                 littleEndian
28660             );
28661         }
28662         // Return the offset to the next directory:
28663         return dataView.getUint32(dirEndOffset, littleEndian);
28664     },
28665     
28666     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28667     {
28668         var tag = dataView.getUint16(offset, littleEndian);
28669         
28670         this.exif[tag] = this.getExifValue(
28671             dataView,
28672             tiffOffset,
28673             offset,
28674             dataView.getUint16(offset + 2, littleEndian), // tag type
28675             dataView.getUint32(offset + 4, littleEndian), // tag length
28676             littleEndian
28677         );
28678     },
28679     
28680     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28681     {
28682         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28683             tagSize,
28684             dataOffset,
28685             values,
28686             i,
28687             str,
28688             c;
28689     
28690         if (!tagType) {
28691             Roo.log('Invalid Exif data: Invalid tag type.');
28692             return;
28693         }
28694         
28695         tagSize = tagType.size * length;
28696         // Determine if the value is contained in the dataOffset bytes,
28697         // or if the value at the dataOffset is a pointer to the actual data:
28698         dataOffset = tagSize > 4 ?
28699                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28700         if (dataOffset + tagSize > dataView.byteLength) {
28701             Roo.log('Invalid Exif data: Invalid data offset.');
28702             return;
28703         }
28704         if (length === 1) {
28705             return tagType.getValue(dataView, dataOffset, littleEndian);
28706         }
28707         values = [];
28708         for (i = 0; i < length; i += 1) {
28709             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28710         }
28711         
28712         if (tagType.ascii) {
28713             str = '';
28714             // Concatenate the chars:
28715             for (i = 0; i < values.length; i += 1) {
28716                 c = values[i];
28717                 // Ignore the terminating NULL byte(s):
28718                 if (c === '\u0000') {
28719                     break;
28720                 }
28721                 str += c;
28722             }
28723             return str;
28724         }
28725         return values;
28726     }
28727     
28728 });
28729
28730 Roo.apply(Roo.bootstrap.UploadCropbox, {
28731     tags : {
28732         'Orientation': 0x0112
28733     },
28734     
28735     Orientation: {
28736             1: 0, //'top-left',
28737 //            2: 'top-right',
28738             3: 180, //'bottom-right',
28739 //            4: 'bottom-left',
28740 //            5: 'left-top',
28741             6: 90, //'right-top',
28742 //            7: 'right-bottom',
28743             8: 270 //'left-bottom'
28744     },
28745     
28746     exifTagTypes : {
28747         // byte, 8-bit unsigned int:
28748         1: {
28749             getValue: function (dataView, dataOffset) {
28750                 return dataView.getUint8(dataOffset);
28751             },
28752             size: 1
28753         },
28754         // ascii, 8-bit byte:
28755         2: {
28756             getValue: function (dataView, dataOffset) {
28757                 return String.fromCharCode(dataView.getUint8(dataOffset));
28758             },
28759             size: 1,
28760             ascii: true
28761         },
28762         // short, 16 bit int:
28763         3: {
28764             getValue: function (dataView, dataOffset, littleEndian) {
28765                 return dataView.getUint16(dataOffset, littleEndian);
28766             },
28767             size: 2
28768         },
28769         // long, 32 bit int:
28770         4: {
28771             getValue: function (dataView, dataOffset, littleEndian) {
28772                 return dataView.getUint32(dataOffset, littleEndian);
28773             },
28774             size: 4
28775         },
28776         // rational = two long values, first is numerator, second is denominator:
28777         5: {
28778             getValue: function (dataView, dataOffset, littleEndian) {
28779                 return dataView.getUint32(dataOffset, littleEndian) /
28780                     dataView.getUint32(dataOffset + 4, littleEndian);
28781             },
28782             size: 8
28783         },
28784         // slong, 32 bit signed int:
28785         9: {
28786             getValue: function (dataView, dataOffset, littleEndian) {
28787                 return dataView.getInt32(dataOffset, littleEndian);
28788             },
28789             size: 4
28790         },
28791         // srational, two slongs, first is numerator, second is denominator:
28792         10: {
28793             getValue: function (dataView, dataOffset, littleEndian) {
28794                 return dataView.getInt32(dataOffset, littleEndian) /
28795                     dataView.getInt32(dataOffset + 4, littleEndian);
28796             },
28797             size: 8
28798         }
28799     },
28800     
28801     footer : {
28802         STANDARD : [
28803             {
28804                 tag : 'div',
28805                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28806                 action : 'rotate-left',
28807                 cn : [
28808                     {
28809                         tag : 'button',
28810                         cls : 'btn btn-default',
28811                         html : '<i class="fa fa-undo"></i>'
28812                     }
28813                 ]
28814             },
28815             {
28816                 tag : 'div',
28817                 cls : 'btn-group roo-upload-cropbox-picture',
28818                 action : 'picture',
28819                 cn : [
28820                     {
28821                         tag : 'button',
28822                         cls : 'btn btn-default',
28823                         html : '<i class="fa fa-picture-o"></i>'
28824                     }
28825                 ]
28826             },
28827             {
28828                 tag : 'div',
28829                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28830                 action : 'rotate-right',
28831                 cn : [
28832                     {
28833                         tag : 'button',
28834                         cls : 'btn btn-default',
28835                         html : '<i class="fa fa-repeat"></i>'
28836                     }
28837                 ]
28838             }
28839         ],
28840         DOCUMENT : [
28841             {
28842                 tag : 'div',
28843                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28844                 action : 'rotate-left',
28845                 cn : [
28846                     {
28847                         tag : 'button',
28848                         cls : 'btn btn-default',
28849                         html : '<i class="fa fa-undo"></i>'
28850                     }
28851                 ]
28852             },
28853             {
28854                 tag : 'div',
28855                 cls : 'btn-group roo-upload-cropbox-download',
28856                 action : 'download',
28857                 cn : [
28858                     {
28859                         tag : 'button',
28860                         cls : 'btn btn-default',
28861                         html : '<i class="fa fa-download"></i>'
28862                     }
28863                 ]
28864             },
28865             {
28866                 tag : 'div',
28867                 cls : 'btn-group roo-upload-cropbox-crop',
28868                 action : 'crop',
28869                 cn : [
28870                     {
28871                         tag : 'button',
28872                         cls : 'btn btn-default',
28873                         html : '<i class="fa fa-crop"></i>'
28874                     }
28875                 ]
28876             },
28877             {
28878                 tag : 'div',
28879                 cls : 'btn-group roo-upload-cropbox-trash',
28880                 action : 'trash',
28881                 cn : [
28882                     {
28883                         tag : 'button',
28884                         cls : 'btn btn-default',
28885                         html : '<i class="fa fa-trash"></i>'
28886                     }
28887                 ]
28888             },
28889             {
28890                 tag : 'div',
28891                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28892                 action : 'rotate-right',
28893                 cn : [
28894                     {
28895                         tag : 'button',
28896                         cls : 'btn btn-default',
28897                         html : '<i class="fa fa-repeat"></i>'
28898                     }
28899                 ]
28900             }
28901         ],
28902         ROTATOR : [
28903             {
28904                 tag : 'div',
28905                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28906                 action : 'rotate-left',
28907                 cn : [
28908                     {
28909                         tag : 'button',
28910                         cls : 'btn btn-default',
28911                         html : '<i class="fa fa-undo"></i>'
28912                     }
28913                 ]
28914             },
28915             {
28916                 tag : 'div',
28917                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28918                 action : 'rotate-right',
28919                 cn : [
28920                     {
28921                         tag : 'button',
28922                         cls : 'btn btn-default',
28923                         html : '<i class="fa fa-repeat"></i>'
28924                     }
28925                 ]
28926             }
28927         ]
28928     }
28929 });
28930
28931 /*
28932 * Licence: LGPL
28933 */
28934
28935 /**
28936  * @class Roo.bootstrap.DocumentManager
28937  * @extends Roo.bootstrap.Component
28938  * Bootstrap DocumentManager class
28939  * @cfg {String} paramName default 'imageUpload'
28940  * @cfg {String} toolTipName default 'filename'
28941  * @cfg {String} method default POST
28942  * @cfg {String} url action url
28943  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28944  * @cfg {Boolean} multiple multiple upload default true
28945  * @cfg {Number} thumbSize default 300
28946  * @cfg {String} fieldLabel
28947  * @cfg {Number} labelWidth default 4
28948  * @cfg {String} labelAlign (left|top) default left
28949  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28950 * @cfg {Number} labellg set the width of label (1-12)
28951  * @cfg {Number} labelmd set the width of label (1-12)
28952  * @cfg {Number} labelsm set the width of label (1-12)
28953  * @cfg {Number} labelxs set the width of label (1-12)
28954  * 
28955  * @constructor
28956  * Create a new DocumentManager
28957  * @param {Object} config The config object
28958  */
28959
28960 Roo.bootstrap.DocumentManager = function(config){
28961     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28962     
28963     this.files = [];
28964     this.delegates = [];
28965     
28966     this.addEvents({
28967         /**
28968          * @event initial
28969          * Fire when initial the DocumentManager
28970          * @param {Roo.bootstrap.DocumentManager} this
28971          */
28972         "initial" : true,
28973         /**
28974          * @event inspect
28975          * inspect selected file
28976          * @param {Roo.bootstrap.DocumentManager} this
28977          * @param {File} file
28978          */
28979         "inspect" : true,
28980         /**
28981          * @event exception
28982          * Fire when xhr load exception
28983          * @param {Roo.bootstrap.DocumentManager} this
28984          * @param {XMLHttpRequest} xhr
28985          */
28986         "exception" : true,
28987         /**
28988          * @event afterupload
28989          * Fire when xhr load exception
28990          * @param {Roo.bootstrap.DocumentManager} this
28991          * @param {XMLHttpRequest} xhr
28992          */
28993         "afterupload" : true,
28994         /**
28995          * @event prepare
28996          * prepare the form data
28997          * @param {Roo.bootstrap.DocumentManager} this
28998          * @param {Object} formData
28999          */
29000         "prepare" : true,
29001         /**
29002          * @event remove
29003          * Fire when remove the file
29004          * @param {Roo.bootstrap.DocumentManager} this
29005          * @param {Object} file
29006          */
29007         "remove" : true,
29008         /**
29009          * @event refresh
29010          * Fire after refresh the file
29011          * @param {Roo.bootstrap.DocumentManager} this
29012          */
29013         "refresh" : true,
29014         /**
29015          * @event click
29016          * Fire after click the image
29017          * @param {Roo.bootstrap.DocumentManager} this
29018          * @param {Object} file
29019          */
29020         "click" : true,
29021         /**
29022          * @event edit
29023          * Fire when upload a image and editable set to true
29024          * @param {Roo.bootstrap.DocumentManager} this
29025          * @param {Object} file
29026          */
29027         "edit" : true,
29028         /**
29029          * @event beforeselectfile
29030          * Fire before select file
29031          * @param {Roo.bootstrap.DocumentManager} this
29032          */
29033         "beforeselectfile" : true,
29034         /**
29035          * @event process
29036          * Fire before process file
29037          * @param {Roo.bootstrap.DocumentManager} this
29038          * @param {Object} file
29039          */
29040         "process" : true,
29041         /**
29042          * @event previewrendered
29043          * Fire when preview rendered
29044          * @param {Roo.bootstrap.DocumentManager} this
29045          * @param {Object} file
29046          */
29047         "previewrendered" : true,
29048         /**
29049          */
29050         "previewResize" : true
29051         
29052     });
29053 };
29054
29055 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29056     
29057     boxes : 0,
29058     inputName : '',
29059     thumbSize : 300,
29060     multiple : true,
29061     files : false,
29062     method : 'POST',
29063     url : '',
29064     paramName : 'imageUpload',
29065     toolTipName : 'filename',
29066     fieldLabel : '',
29067     labelWidth : 4,
29068     labelAlign : 'left',
29069     editable : true,
29070     delegates : false,
29071     xhr : false, 
29072     
29073     labellg : 0,
29074     labelmd : 0,
29075     labelsm : 0,
29076     labelxs : 0,
29077     
29078     getAutoCreate : function()
29079     {   
29080         var managerWidget = {
29081             tag : 'div',
29082             cls : 'roo-document-manager',
29083             cn : [
29084                 {
29085                     tag : 'input',
29086                     cls : 'roo-document-manager-selector',
29087                     type : 'file'
29088                 },
29089                 {
29090                     tag : 'div',
29091                     cls : 'roo-document-manager-uploader',
29092                     cn : [
29093                         {
29094                             tag : 'div',
29095                             cls : 'roo-document-manager-upload-btn',
29096                             html : '<i class="fa fa-plus"></i>'
29097                         }
29098                     ]
29099                     
29100                 }
29101             ]
29102         };
29103         
29104         var content = [
29105             {
29106                 tag : 'div',
29107                 cls : 'column col-md-12',
29108                 cn : managerWidget
29109             }
29110         ];
29111         
29112         if(this.fieldLabel.length){
29113             
29114             content = [
29115                 {
29116                     tag : 'div',
29117                     cls : 'column col-md-12',
29118                     html : this.fieldLabel
29119                 },
29120                 {
29121                     tag : 'div',
29122                     cls : 'column col-md-12',
29123                     cn : managerWidget
29124                 }
29125             ];
29126
29127             if(this.labelAlign == 'left'){
29128                 content = [
29129                     {
29130                         tag : 'div',
29131                         cls : 'column',
29132                         html : this.fieldLabel
29133                     },
29134                     {
29135                         tag : 'div',
29136                         cls : 'column',
29137                         cn : managerWidget
29138                     }
29139                 ];
29140                 
29141                 if(this.labelWidth > 12){
29142                     content[0].style = "width: " + this.labelWidth + 'px';
29143                 }
29144
29145                 if(this.labelWidth < 13 && this.labelmd == 0){
29146                     this.labelmd = this.labelWidth;
29147                 }
29148
29149                 if(this.labellg > 0){
29150                     content[0].cls += ' col-lg-' + this.labellg;
29151                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29152                 }
29153
29154                 if(this.labelmd > 0){
29155                     content[0].cls += ' col-md-' + this.labelmd;
29156                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29157                 }
29158
29159                 if(this.labelsm > 0){
29160                     content[0].cls += ' col-sm-' + this.labelsm;
29161                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29162                 }
29163
29164                 if(this.labelxs > 0){
29165                     content[0].cls += ' col-xs-' + this.labelxs;
29166                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29167                 }
29168                 
29169             }
29170         }
29171         
29172         var cfg = {
29173             tag : 'div',
29174             cls : 'row clearfix',
29175             cn : content
29176         };
29177         
29178         return cfg;
29179         
29180     },
29181     
29182     initEvents : function()
29183     {
29184         this.managerEl = this.el.select('.roo-document-manager', true).first();
29185         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29186         
29187         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29188         this.selectorEl.hide();
29189         
29190         if(this.multiple){
29191             this.selectorEl.attr('multiple', 'multiple');
29192         }
29193         
29194         this.selectorEl.on('change', this.onFileSelected, this);
29195         
29196         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29197         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29198         
29199         this.uploader.on('click', this.onUploaderClick, this);
29200         
29201         this.renderProgressDialog();
29202         
29203         var _this = this;
29204         
29205         window.addEventListener("resize", function() { _this.refresh(); } );
29206         
29207         this.fireEvent('initial', this);
29208     },
29209     
29210     renderProgressDialog : function()
29211     {
29212         var _this = this;
29213         
29214         this.progressDialog = new Roo.bootstrap.Modal({
29215             cls : 'roo-document-manager-progress-dialog',
29216             allow_close : false,
29217             animate : false,
29218             title : '',
29219             buttons : [
29220                 {
29221                     name  :'cancel',
29222                     weight : 'danger',
29223                     html : 'Cancel'
29224                 }
29225             ], 
29226             listeners : { 
29227                 btnclick : function() {
29228                     _this.uploadCancel();
29229                     this.hide();
29230                 }
29231             }
29232         });
29233          
29234         this.progressDialog.render(Roo.get(document.body));
29235          
29236         this.progress = new Roo.bootstrap.Progress({
29237             cls : 'roo-document-manager-progress',
29238             active : true,
29239             striped : true
29240         });
29241         
29242         this.progress.render(this.progressDialog.getChildContainer());
29243         
29244         this.progressBar = new Roo.bootstrap.ProgressBar({
29245             cls : 'roo-document-manager-progress-bar',
29246             aria_valuenow : 0,
29247             aria_valuemin : 0,
29248             aria_valuemax : 12,
29249             panel : 'success'
29250         });
29251         
29252         this.progressBar.render(this.progress.getChildContainer());
29253     },
29254     
29255     onUploaderClick : function(e)
29256     {
29257         e.preventDefault();
29258      
29259         if(this.fireEvent('beforeselectfile', this) != false){
29260             this.selectorEl.dom.click();
29261         }
29262         
29263     },
29264     
29265     onFileSelected : function(e)
29266     {
29267         e.preventDefault();
29268         
29269         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29270             return;
29271         }
29272         
29273         Roo.each(this.selectorEl.dom.files, function(file){
29274             if(this.fireEvent('inspect', this, file) != false){
29275                 this.files.push(file);
29276             }
29277         }, this);
29278         
29279         this.queue();
29280         
29281     },
29282     
29283     queue : function()
29284     {
29285         this.selectorEl.dom.value = '';
29286         
29287         if(!this.files || !this.files.length){
29288             return;
29289         }
29290         
29291         if(this.boxes > 0 && this.files.length > this.boxes){
29292             this.files = this.files.slice(0, this.boxes);
29293         }
29294         
29295         this.uploader.show();
29296         
29297         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29298             this.uploader.hide();
29299         }
29300         
29301         var _this = this;
29302         
29303         var files = [];
29304         
29305         var docs = [];
29306         
29307         Roo.each(this.files, function(file){
29308             
29309             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29310                 var f = this.renderPreview(file);
29311                 files.push(f);
29312                 return;
29313             }
29314             
29315             if(file.type.indexOf('image') != -1){
29316                 this.delegates.push(
29317                     (function(){
29318                         _this.process(file);
29319                     }).createDelegate(this)
29320                 );
29321         
29322                 return;
29323             }
29324             
29325             docs.push(
29326                 (function(){
29327                     _this.process(file);
29328                 }).createDelegate(this)
29329             );
29330             
29331         }, this);
29332         
29333         this.files = files;
29334         
29335         this.delegates = this.delegates.concat(docs);
29336         
29337         if(!this.delegates.length){
29338             this.refresh();
29339             return;
29340         }
29341         
29342         this.progressBar.aria_valuemax = this.delegates.length;
29343         
29344         this.arrange();
29345         
29346         return;
29347     },
29348     
29349     arrange : function()
29350     {
29351         if(!this.delegates.length){
29352             this.progressDialog.hide();
29353             this.refresh();
29354             return;
29355         }
29356         
29357         var delegate = this.delegates.shift();
29358         
29359         this.progressDialog.show();
29360         
29361         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29362         
29363         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29364         
29365         delegate();
29366     },
29367     
29368     refresh : function()
29369     {
29370         this.uploader.show();
29371         
29372         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29373             this.uploader.hide();
29374         }
29375         
29376         Roo.isTouch ? this.closable(false) : this.closable(true);
29377         
29378         this.fireEvent('refresh', this);
29379     },
29380     
29381     onRemove : function(e, el, o)
29382     {
29383         e.preventDefault();
29384         
29385         this.fireEvent('remove', this, o);
29386         
29387     },
29388     
29389     remove : function(o)
29390     {
29391         var files = [];
29392         
29393         Roo.each(this.files, function(file){
29394             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29395                 files.push(file);
29396                 return;
29397             }
29398
29399             o.target.remove();
29400
29401         }, this);
29402         
29403         this.files = files;
29404         
29405         this.refresh();
29406     },
29407     
29408     clear : function()
29409     {
29410         Roo.each(this.files, function(file){
29411             if(!file.target){
29412                 return;
29413             }
29414             
29415             file.target.remove();
29416
29417         }, this);
29418         
29419         this.files = [];
29420         
29421         this.refresh();
29422     },
29423     
29424     onClick : function(e, el, o)
29425     {
29426         e.preventDefault();
29427         
29428         this.fireEvent('click', this, o);
29429         
29430     },
29431     
29432     closable : function(closable)
29433     {
29434         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29435             
29436             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29437             
29438             if(closable){
29439                 el.show();
29440                 return;
29441             }
29442             
29443             el.hide();
29444             
29445         }, this);
29446     },
29447     
29448     xhrOnLoad : function(xhr)
29449     {
29450         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29451             el.remove();
29452         }, this);
29453         
29454         if (xhr.readyState !== 4) {
29455             this.arrange();
29456             this.fireEvent('exception', this, xhr);
29457             return;
29458         }
29459
29460         var response = Roo.decode(xhr.responseText);
29461         
29462         if(!response.success){
29463             this.arrange();
29464             this.fireEvent('exception', this, xhr);
29465             return;
29466         }
29467         
29468         var file = this.renderPreview(response.data);
29469         
29470         this.files.push(file);
29471         
29472         this.arrange();
29473         
29474         this.fireEvent('afterupload', this, xhr);
29475         
29476     },
29477     
29478     xhrOnError : function(xhr)
29479     {
29480         Roo.log('xhr on error');
29481         
29482         var response = Roo.decode(xhr.responseText);
29483           
29484         Roo.log(response);
29485         
29486         this.arrange();
29487     },
29488     
29489     process : function(file)
29490     {
29491         if(this.fireEvent('process', this, file) !== false){
29492             if(this.editable && file.type.indexOf('image') != -1){
29493                 this.fireEvent('edit', this, file);
29494                 return;
29495             }
29496
29497             this.uploadStart(file, false);
29498
29499             return;
29500         }
29501         
29502     },
29503     
29504     uploadStart : function(file, crop)
29505     {
29506         this.xhr = new XMLHttpRequest();
29507         
29508         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29509             this.arrange();
29510             return;
29511         }
29512         
29513         file.xhr = this.xhr;
29514             
29515         this.managerEl.createChild({
29516             tag : 'div',
29517             cls : 'roo-document-manager-loading',
29518             cn : [
29519                 {
29520                     tag : 'div',
29521                     tooltip : file.name,
29522                     cls : 'roo-document-manager-thumb',
29523                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29524                 }
29525             ]
29526
29527         });
29528
29529         this.xhr.open(this.method, this.url, true);
29530         
29531         var headers = {
29532             "Accept": "application/json",
29533             "Cache-Control": "no-cache",
29534             "X-Requested-With": "XMLHttpRequest"
29535         };
29536         
29537         for (var headerName in headers) {
29538             var headerValue = headers[headerName];
29539             if (headerValue) {
29540                 this.xhr.setRequestHeader(headerName, headerValue);
29541             }
29542         }
29543         
29544         var _this = this;
29545         
29546         this.xhr.onload = function()
29547         {
29548             _this.xhrOnLoad(_this.xhr);
29549         }
29550         
29551         this.xhr.onerror = function()
29552         {
29553             _this.xhrOnError(_this.xhr);
29554         }
29555         
29556         var formData = new FormData();
29557
29558         formData.append('returnHTML', 'NO');
29559         
29560         if(crop){
29561             formData.append('crop', crop);
29562         }
29563         
29564         formData.append(this.paramName, file, file.name);
29565         
29566         var options = {
29567             file : file, 
29568             manually : false
29569         };
29570         
29571         if(this.fireEvent('prepare', this, formData, options) != false){
29572             
29573             if(options.manually){
29574                 return;
29575             }
29576             
29577             this.xhr.send(formData);
29578             return;
29579         };
29580         
29581         this.uploadCancel();
29582     },
29583     
29584     uploadCancel : function()
29585     {
29586         if (this.xhr) {
29587             this.xhr.abort();
29588         }
29589         
29590         this.delegates = [];
29591         
29592         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29593             el.remove();
29594         }, this);
29595         
29596         this.arrange();
29597     },
29598     
29599     renderPreview : function(file)
29600     {
29601         if(typeof(file.target) != 'undefined' && file.target){
29602             return file;
29603         }
29604         
29605         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29606         
29607         var previewEl = this.managerEl.createChild({
29608             tag : 'div',
29609             cls : 'roo-document-manager-preview',
29610             cn : [
29611                 {
29612                     tag : 'div',
29613                     tooltip : file[this.toolTipName],
29614                     cls : 'roo-document-manager-thumb',
29615                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29616                 },
29617                 {
29618                     tag : 'button',
29619                     cls : 'close',
29620                     html : '<i class="fa fa-times-circle"></i>'
29621                 }
29622             ]
29623         });
29624
29625         var close = previewEl.select('button.close', true).first();
29626
29627         close.on('click', this.onRemove, this, file);
29628
29629         file.target = previewEl;
29630
29631         var image = previewEl.select('img', true).first();
29632         
29633         var _this = this;
29634         
29635         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29636         
29637         image.on('click', this.onClick, this, file);
29638         
29639         this.fireEvent('previewrendered', this, file);
29640         
29641         return file;
29642         
29643     },
29644     
29645     onPreviewLoad : function(file, image)
29646     {
29647         if(typeof(file.target) == 'undefined' || !file.target){
29648             return;
29649         }
29650         
29651         var width = image.dom.naturalWidth || image.dom.width;
29652         var height = image.dom.naturalHeight || image.dom.height;
29653         
29654         if(!this.previewResize) {
29655             return;
29656         }
29657         
29658         if(width > height){
29659             file.target.addClass('wide');
29660             return;
29661         }
29662         
29663         file.target.addClass('tall');
29664         return;
29665         
29666     },
29667     
29668     uploadFromSource : function(file, crop)
29669     {
29670         this.xhr = new XMLHttpRequest();
29671         
29672         this.managerEl.createChild({
29673             tag : 'div',
29674             cls : 'roo-document-manager-loading',
29675             cn : [
29676                 {
29677                     tag : 'div',
29678                     tooltip : file.name,
29679                     cls : 'roo-document-manager-thumb',
29680                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29681                 }
29682             ]
29683
29684         });
29685
29686         this.xhr.open(this.method, this.url, true);
29687         
29688         var headers = {
29689             "Accept": "application/json",
29690             "Cache-Control": "no-cache",
29691             "X-Requested-With": "XMLHttpRequest"
29692         };
29693         
29694         for (var headerName in headers) {
29695             var headerValue = headers[headerName];
29696             if (headerValue) {
29697                 this.xhr.setRequestHeader(headerName, headerValue);
29698             }
29699         }
29700         
29701         var _this = this;
29702         
29703         this.xhr.onload = function()
29704         {
29705             _this.xhrOnLoad(_this.xhr);
29706         }
29707         
29708         this.xhr.onerror = function()
29709         {
29710             _this.xhrOnError(_this.xhr);
29711         }
29712         
29713         var formData = new FormData();
29714
29715         formData.append('returnHTML', 'NO');
29716         
29717         formData.append('crop', crop);
29718         
29719         if(typeof(file.filename) != 'undefined'){
29720             formData.append('filename', file.filename);
29721         }
29722         
29723         if(typeof(file.mimetype) != 'undefined'){
29724             formData.append('mimetype', file.mimetype);
29725         }
29726         
29727         Roo.log(formData);
29728         
29729         if(this.fireEvent('prepare', this, formData) != false){
29730             this.xhr.send(formData);
29731         };
29732     }
29733 });
29734
29735 /*
29736 * Licence: LGPL
29737 */
29738
29739 /**
29740  * @class Roo.bootstrap.DocumentViewer
29741  * @extends Roo.bootstrap.Component
29742  * Bootstrap DocumentViewer class
29743  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29744  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29745  * 
29746  * @constructor
29747  * Create a new DocumentViewer
29748  * @param {Object} config The config object
29749  */
29750
29751 Roo.bootstrap.DocumentViewer = function(config){
29752     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29753     
29754     this.addEvents({
29755         /**
29756          * @event initial
29757          * Fire after initEvent
29758          * @param {Roo.bootstrap.DocumentViewer} this
29759          */
29760         "initial" : true,
29761         /**
29762          * @event click
29763          * Fire after click
29764          * @param {Roo.bootstrap.DocumentViewer} this
29765          */
29766         "click" : true,
29767         /**
29768          * @event download
29769          * Fire after download button
29770          * @param {Roo.bootstrap.DocumentViewer} this
29771          */
29772         "download" : true,
29773         /**
29774          * @event trash
29775          * Fire after trash button
29776          * @param {Roo.bootstrap.DocumentViewer} this
29777          */
29778         "trash" : true
29779         
29780     });
29781 };
29782
29783 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29784     
29785     showDownload : true,
29786     
29787     showTrash : true,
29788     
29789     getAutoCreate : function()
29790     {
29791         var cfg = {
29792             tag : 'div',
29793             cls : 'roo-document-viewer',
29794             cn : [
29795                 {
29796                     tag : 'div',
29797                     cls : 'roo-document-viewer-body',
29798                     cn : [
29799                         {
29800                             tag : 'div',
29801                             cls : 'roo-document-viewer-thumb',
29802                             cn : [
29803                                 {
29804                                     tag : 'img',
29805                                     cls : 'roo-document-viewer-image'
29806                                 }
29807                             ]
29808                         }
29809                     ]
29810                 },
29811                 {
29812                     tag : 'div',
29813                     cls : 'roo-document-viewer-footer',
29814                     cn : {
29815                         tag : 'div',
29816                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29817                         cn : [
29818                             {
29819                                 tag : 'div',
29820                                 cls : 'btn-group roo-document-viewer-download',
29821                                 cn : [
29822                                     {
29823                                         tag : 'button',
29824                                         cls : 'btn btn-default',
29825                                         html : '<i class="fa fa-download"></i>'
29826                                     }
29827                                 ]
29828                             },
29829                             {
29830                                 tag : 'div',
29831                                 cls : 'btn-group roo-document-viewer-trash',
29832                                 cn : [
29833                                     {
29834                                         tag : 'button',
29835                                         cls : 'btn btn-default',
29836                                         html : '<i class="fa fa-trash"></i>'
29837                                     }
29838                                 ]
29839                             }
29840                         ]
29841                     }
29842                 }
29843             ]
29844         };
29845         
29846         return cfg;
29847     },
29848     
29849     initEvents : function()
29850     {
29851         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29852         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29853         
29854         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29855         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29856         
29857         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29858         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29859         
29860         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29861         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29862         
29863         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29864         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29865         
29866         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29867         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29868         
29869         this.bodyEl.on('click', this.onClick, this);
29870         this.downloadBtn.on('click', this.onDownload, this);
29871         this.trashBtn.on('click', this.onTrash, this);
29872         
29873         this.downloadBtn.hide();
29874         this.trashBtn.hide();
29875         
29876         if(this.showDownload){
29877             this.downloadBtn.show();
29878         }
29879         
29880         if(this.showTrash){
29881             this.trashBtn.show();
29882         }
29883         
29884         if(!this.showDownload && !this.showTrash) {
29885             this.footerEl.hide();
29886         }
29887         
29888     },
29889     
29890     initial : function()
29891     {
29892         this.fireEvent('initial', this);
29893         
29894     },
29895     
29896     onClick : function(e)
29897     {
29898         e.preventDefault();
29899         
29900         this.fireEvent('click', this);
29901     },
29902     
29903     onDownload : function(e)
29904     {
29905         e.preventDefault();
29906         
29907         this.fireEvent('download', this);
29908     },
29909     
29910     onTrash : function(e)
29911     {
29912         e.preventDefault();
29913         
29914         this.fireEvent('trash', this);
29915     }
29916     
29917 });
29918 /*
29919  * - LGPL
29920  *
29921  * nav progress bar
29922  * 
29923  */
29924
29925 /**
29926  * @class Roo.bootstrap.NavProgressBar
29927  * @extends Roo.bootstrap.Component
29928  * Bootstrap NavProgressBar class
29929  * 
29930  * @constructor
29931  * Create a new nav progress bar
29932  * @param {Object} config The config object
29933  */
29934
29935 Roo.bootstrap.NavProgressBar = function(config){
29936     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29937
29938     this.bullets = this.bullets || [];
29939    
29940 //    Roo.bootstrap.NavProgressBar.register(this);
29941      this.addEvents({
29942         /**
29943              * @event changed
29944              * Fires when the active item changes
29945              * @param {Roo.bootstrap.NavProgressBar} this
29946              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29947              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29948          */
29949         'changed': true
29950      });
29951     
29952 };
29953
29954 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29955     
29956     bullets : [],
29957     barItems : [],
29958     
29959     getAutoCreate : function()
29960     {
29961         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29962         
29963         cfg = {
29964             tag : 'div',
29965             cls : 'roo-navigation-bar-group',
29966             cn : [
29967                 {
29968                     tag : 'div',
29969                     cls : 'roo-navigation-top-bar'
29970                 },
29971                 {
29972                     tag : 'div',
29973                     cls : 'roo-navigation-bullets-bar',
29974                     cn : [
29975                         {
29976                             tag : 'ul',
29977                             cls : 'roo-navigation-bar'
29978                         }
29979                     ]
29980                 },
29981                 
29982                 {
29983                     tag : 'div',
29984                     cls : 'roo-navigation-bottom-bar'
29985                 }
29986             ]
29987             
29988         };
29989         
29990         return cfg;
29991         
29992     },
29993     
29994     initEvents: function() 
29995     {
29996         
29997     },
29998     
29999     onRender : function(ct, position) 
30000     {
30001         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30002         
30003         if(this.bullets.length){
30004             Roo.each(this.bullets, function(b){
30005                this.addItem(b);
30006             }, this);
30007         }
30008         
30009         this.format();
30010         
30011     },
30012     
30013     addItem : function(cfg)
30014     {
30015         var item = new Roo.bootstrap.NavProgressItem(cfg);
30016         
30017         item.parentId = this.id;
30018         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30019         
30020         if(cfg.html){
30021             var top = new Roo.bootstrap.Element({
30022                 tag : 'div',
30023                 cls : 'roo-navigation-bar-text'
30024             });
30025             
30026             var bottom = new Roo.bootstrap.Element({
30027                 tag : 'div',
30028                 cls : 'roo-navigation-bar-text'
30029             });
30030             
30031             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30032             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30033             
30034             var topText = new Roo.bootstrap.Element({
30035                 tag : 'span',
30036                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30037             });
30038             
30039             var bottomText = new Roo.bootstrap.Element({
30040                 tag : 'span',
30041                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30042             });
30043             
30044             topText.onRender(top.el, null);
30045             bottomText.onRender(bottom.el, null);
30046             
30047             item.topEl = top;
30048             item.bottomEl = bottom;
30049         }
30050         
30051         this.barItems.push(item);
30052         
30053         return item;
30054     },
30055     
30056     getActive : function()
30057     {
30058         var active = false;
30059         
30060         Roo.each(this.barItems, function(v){
30061             
30062             if (!v.isActive()) {
30063                 return;
30064             }
30065             
30066             active = v;
30067             return false;
30068             
30069         });
30070         
30071         return active;
30072     },
30073     
30074     setActiveItem : function(item)
30075     {
30076         var prev = false;
30077         
30078         Roo.each(this.barItems, function(v){
30079             if (v.rid == item.rid) {
30080                 return ;
30081             }
30082             
30083             if (v.isActive()) {
30084                 v.setActive(false);
30085                 prev = v;
30086             }
30087         });
30088
30089         item.setActive(true);
30090         
30091         this.fireEvent('changed', this, item, prev);
30092     },
30093     
30094     getBarItem: function(rid)
30095     {
30096         var ret = false;
30097         
30098         Roo.each(this.barItems, function(e) {
30099             if (e.rid != rid) {
30100                 return;
30101             }
30102             
30103             ret =  e;
30104             return false;
30105         });
30106         
30107         return ret;
30108     },
30109     
30110     indexOfItem : function(item)
30111     {
30112         var index = false;
30113         
30114         Roo.each(this.barItems, function(v, i){
30115             
30116             if (v.rid != item.rid) {
30117                 return;
30118             }
30119             
30120             index = i;
30121             return false
30122         });
30123         
30124         return index;
30125     },
30126     
30127     setActiveNext : function()
30128     {
30129         var i = this.indexOfItem(this.getActive());
30130         
30131         if (i > this.barItems.length) {
30132             return;
30133         }
30134         
30135         this.setActiveItem(this.barItems[i+1]);
30136     },
30137     
30138     setActivePrev : function()
30139     {
30140         var i = this.indexOfItem(this.getActive());
30141         
30142         if (i  < 1) {
30143             return;
30144         }
30145         
30146         this.setActiveItem(this.barItems[i-1]);
30147     },
30148     
30149     format : function()
30150     {
30151         if(!this.barItems.length){
30152             return;
30153         }
30154      
30155         var width = 100 / this.barItems.length;
30156         
30157         Roo.each(this.barItems, function(i){
30158             i.el.setStyle('width', width + '%');
30159             i.topEl.el.setStyle('width', width + '%');
30160             i.bottomEl.el.setStyle('width', width + '%');
30161         }, this);
30162         
30163     }
30164     
30165 });
30166 /*
30167  * - LGPL
30168  *
30169  * Nav Progress Item
30170  * 
30171  */
30172
30173 /**
30174  * @class Roo.bootstrap.NavProgressItem
30175  * @extends Roo.bootstrap.Component
30176  * Bootstrap NavProgressItem class
30177  * @cfg {String} rid the reference id
30178  * @cfg {Boolean} active (true|false) Is item active default false
30179  * @cfg {Boolean} disabled (true|false) Is item active default false
30180  * @cfg {String} html
30181  * @cfg {String} position (top|bottom) text position default bottom
30182  * @cfg {String} icon show icon instead of number
30183  * 
30184  * @constructor
30185  * Create a new NavProgressItem
30186  * @param {Object} config The config object
30187  */
30188 Roo.bootstrap.NavProgressItem = function(config){
30189     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30190     this.addEvents({
30191         // raw events
30192         /**
30193          * @event click
30194          * The raw click event for the entire grid.
30195          * @param {Roo.bootstrap.NavProgressItem} this
30196          * @param {Roo.EventObject} e
30197          */
30198         "click" : true
30199     });
30200    
30201 };
30202
30203 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30204     
30205     rid : '',
30206     active : false,
30207     disabled : false,
30208     html : '',
30209     position : 'bottom',
30210     icon : false,
30211     
30212     getAutoCreate : function()
30213     {
30214         var iconCls = 'roo-navigation-bar-item-icon';
30215         
30216         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30217         
30218         var cfg = {
30219             tag: 'li',
30220             cls: 'roo-navigation-bar-item',
30221             cn : [
30222                 {
30223                     tag : 'i',
30224                     cls : iconCls
30225                 }
30226             ]
30227         };
30228         
30229         if(this.active){
30230             cfg.cls += ' active';
30231         }
30232         if(this.disabled){
30233             cfg.cls += ' disabled';
30234         }
30235         
30236         return cfg;
30237     },
30238     
30239     disable : function()
30240     {
30241         this.setDisabled(true);
30242     },
30243     
30244     enable : function()
30245     {
30246         this.setDisabled(false);
30247     },
30248     
30249     initEvents: function() 
30250     {
30251         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30252         
30253         this.iconEl.on('click', this.onClick, this);
30254     },
30255     
30256     onClick : function(e)
30257     {
30258         e.preventDefault();
30259         
30260         if(this.disabled){
30261             return;
30262         }
30263         
30264         if(this.fireEvent('click', this, e) === false){
30265             return;
30266         };
30267         
30268         this.parent().setActiveItem(this);
30269     },
30270     
30271     isActive: function () 
30272     {
30273         return this.active;
30274     },
30275     
30276     setActive : function(state)
30277     {
30278         if(this.active == state){
30279             return;
30280         }
30281         
30282         this.active = state;
30283         
30284         if (state) {
30285             this.el.addClass('active');
30286             return;
30287         }
30288         
30289         this.el.removeClass('active');
30290         
30291         return;
30292     },
30293     
30294     setDisabled : function(state)
30295     {
30296         if(this.disabled == state){
30297             return;
30298         }
30299         
30300         this.disabled = state;
30301         
30302         if (state) {
30303             this.el.addClass('disabled');
30304             return;
30305         }
30306         
30307         this.el.removeClass('disabled');
30308     },
30309     
30310     tooltipEl : function()
30311     {
30312         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30313     }
30314 });
30315  
30316
30317  /*
30318  * - LGPL
30319  *
30320  * FieldLabel
30321  * 
30322  */
30323
30324 /**
30325  * @class Roo.bootstrap.FieldLabel
30326  * @extends Roo.bootstrap.Component
30327  * Bootstrap FieldLabel class
30328  * @cfg {String} html contents of the element
30329  * @cfg {String} tag tag of the element default label
30330  * @cfg {String} cls class of the element
30331  * @cfg {String} target label target 
30332  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30333  * @cfg {String} invalidClass default "text-warning"
30334  * @cfg {String} validClass default "text-success"
30335  * @cfg {String} iconTooltip default "This field is required"
30336  * @cfg {String} indicatorpos (left|right) default left
30337  * 
30338  * @constructor
30339  * Create a new FieldLabel
30340  * @param {Object} config The config object
30341  */
30342
30343 Roo.bootstrap.FieldLabel = function(config){
30344     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30345     
30346     this.addEvents({
30347             /**
30348              * @event invalid
30349              * Fires after the field has been marked as invalid.
30350              * @param {Roo.form.FieldLabel} this
30351              * @param {String} msg The validation message
30352              */
30353             invalid : true,
30354             /**
30355              * @event valid
30356              * Fires after the field has been validated with no errors.
30357              * @param {Roo.form.FieldLabel} this
30358              */
30359             valid : true
30360         });
30361 };
30362
30363 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30364     
30365     tag: 'label',
30366     cls: '',
30367     html: '',
30368     target: '',
30369     allowBlank : true,
30370     invalidClass : 'has-warning',
30371     validClass : 'has-success',
30372     iconTooltip : 'This field is required',
30373     indicatorpos : 'left',
30374     
30375     getAutoCreate : function(){
30376         
30377         var cls = "";
30378         if (!this.allowBlank) {
30379             cls  = "visible";
30380         }
30381         
30382         var cfg = {
30383             tag : this.tag,
30384             cls : 'roo-bootstrap-field-label ' + this.cls,
30385             for : this.target,
30386             cn : [
30387                 {
30388                     tag : 'i',
30389                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30390                     tooltip : this.iconTooltip
30391                 },
30392                 {
30393                     tag : 'span',
30394                     html : this.html
30395                 }
30396             ] 
30397         };
30398         
30399         if(this.indicatorpos == 'right'){
30400             var cfg = {
30401                 tag : this.tag,
30402                 cls : 'roo-bootstrap-field-label ' + this.cls,
30403                 for : this.target,
30404                 cn : [
30405                     {
30406                         tag : 'span',
30407                         html : this.html
30408                     },
30409                     {
30410                         tag : 'i',
30411                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30412                         tooltip : this.iconTooltip
30413                     }
30414                 ] 
30415             };
30416         }
30417         
30418         return cfg;
30419     },
30420     
30421     initEvents: function() 
30422     {
30423         Roo.bootstrap.Element.superclass.initEvents.call(this);
30424         
30425         this.indicator = this.indicatorEl();
30426         
30427         if(this.indicator){
30428             this.indicator.removeClass('visible');
30429             this.indicator.addClass('invisible');
30430         }
30431         
30432         Roo.bootstrap.FieldLabel.register(this);
30433     },
30434     
30435     indicatorEl : function()
30436     {
30437         var indicator = this.el.select('i.roo-required-indicator',true).first();
30438         
30439         if(!indicator){
30440             return false;
30441         }
30442         
30443         return indicator;
30444         
30445     },
30446     
30447     /**
30448      * Mark this field as valid
30449      */
30450     markValid : function()
30451     {
30452         if(this.indicator){
30453             this.indicator.removeClass('visible');
30454             this.indicator.addClass('invisible');
30455         }
30456         
30457         this.el.removeClass(this.invalidClass);
30458         
30459         this.el.addClass(this.validClass);
30460         
30461         this.fireEvent('valid', this);
30462     },
30463     
30464     /**
30465      * Mark this field as invalid
30466      * @param {String} msg The validation message
30467      */
30468     markInvalid : function(msg)
30469     {
30470         if(this.indicator){
30471             this.indicator.removeClass('invisible');
30472             this.indicator.addClass('visible');
30473         }
30474         
30475         this.el.removeClass(this.validClass);
30476         
30477         this.el.addClass(this.invalidClass);
30478         
30479         this.fireEvent('invalid', this, msg);
30480     }
30481     
30482    
30483 });
30484
30485 Roo.apply(Roo.bootstrap.FieldLabel, {
30486     
30487     groups: {},
30488     
30489      /**
30490     * register a FieldLabel Group
30491     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30492     */
30493     register : function(label)
30494     {
30495         if(this.groups.hasOwnProperty(label.target)){
30496             return;
30497         }
30498      
30499         this.groups[label.target] = label;
30500         
30501     },
30502     /**
30503     * fetch a FieldLabel Group based on the target
30504     * @param {string} target
30505     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30506     */
30507     get: function(target) {
30508         if (typeof(this.groups[target]) == 'undefined') {
30509             return false;
30510         }
30511         
30512         return this.groups[target] ;
30513     }
30514 });
30515
30516  
30517
30518  /*
30519  * - LGPL
30520  *
30521  * page DateSplitField.
30522  * 
30523  */
30524
30525
30526 /**
30527  * @class Roo.bootstrap.DateSplitField
30528  * @extends Roo.bootstrap.Component
30529  * Bootstrap DateSplitField class
30530  * @cfg {string} fieldLabel - the label associated
30531  * @cfg {Number} labelWidth set the width of label (0-12)
30532  * @cfg {String} labelAlign (top|left)
30533  * @cfg {Boolean} dayAllowBlank (true|false) default false
30534  * @cfg {Boolean} monthAllowBlank (true|false) default false
30535  * @cfg {Boolean} yearAllowBlank (true|false) default false
30536  * @cfg {string} dayPlaceholder 
30537  * @cfg {string} monthPlaceholder
30538  * @cfg {string} yearPlaceholder
30539  * @cfg {string} dayFormat default 'd'
30540  * @cfg {string} monthFormat default 'm'
30541  * @cfg {string} yearFormat default 'Y'
30542  * @cfg {Number} labellg set the width of label (1-12)
30543  * @cfg {Number} labelmd set the width of label (1-12)
30544  * @cfg {Number} labelsm set the width of label (1-12)
30545  * @cfg {Number} labelxs set the width of label (1-12)
30546
30547  *     
30548  * @constructor
30549  * Create a new DateSplitField
30550  * @param {Object} config The config object
30551  */
30552
30553 Roo.bootstrap.DateSplitField = function(config){
30554     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30555     
30556     this.addEvents({
30557         // raw events
30558          /**
30559          * @event years
30560          * getting the data of years
30561          * @param {Roo.bootstrap.DateSplitField} this
30562          * @param {Object} years
30563          */
30564         "years" : true,
30565         /**
30566          * @event days
30567          * getting the data of days
30568          * @param {Roo.bootstrap.DateSplitField} this
30569          * @param {Object} days
30570          */
30571         "days" : true,
30572         /**
30573          * @event invalid
30574          * Fires after the field has been marked as invalid.
30575          * @param {Roo.form.Field} this
30576          * @param {String} msg The validation message
30577          */
30578         invalid : true,
30579        /**
30580          * @event valid
30581          * Fires after the field has been validated with no errors.
30582          * @param {Roo.form.Field} this
30583          */
30584         valid : true
30585     });
30586 };
30587
30588 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30589     
30590     fieldLabel : '',
30591     labelAlign : 'top',
30592     labelWidth : 3,
30593     dayAllowBlank : false,
30594     monthAllowBlank : false,
30595     yearAllowBlank : false,
30596     dayPlaceholder : '',
30597     monthPlaceholder : '',
30598     yearPlaceholder : '',
30599     dayFormat : 'd',
30600     monthFormat : 'm',
30601     yearFormat : 'Y',
30602     isFormField : true,
30603     labellg : 0,
30604     labelmd : 0,
30605     labelsm : 0,
30606     labelxs : 0,
30607     
30608     getAutoCreate : function()
30609     {
30610         var cfg = {
30611             tag : 'div',
30612             cls : 'row roo-date-split-field-group',
30613             cn : [
30614                 {
30615                     tag : 'input',
30616                     type : 'hidden',
30617                     cls : 'form-hidden-field roo-date-split-field-group-value',
30618                     name : this.name
30619                 }
30620             ]
30621         };
30622         
30623         var labelCls = 'col-md-12';
30624         var contentCls = 'col-md-4';
30625         
30626         if(this.fieldLabel){
30627             
30628             var label = {
30629                 tag : 'div',
30630                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30631                 cn : [
30632                     {
30633                         tag : 'label',
30634                         html : this.fieldLabel
30635                     }
30636                 ]
30637             };
30638             
30639             if(this.labelAlign == 'left'){
30640             
30641                 if(this.labelWidth > 12){
30642                     label.style = "width: " + this.labelWidth + 'px';
30643                 }
30644
30645                 if(this.labelWidth < 13 && this.labelmd == 0){
30646                     this.labelmd = this.labelWidth;
30647                 }
30648
30649                 if(this.labellg > 0){
30650                     labelCls = ' col-lg-' + this.labellg;
30651                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30652                 }
30653
30654                 if(this.labelmd > 0){
30655                     labelCls = ' col-md-' + this.labelmd;
30656                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30657                 }
30658
30659                 if(this.labelsm > 0){
30660                     labelCls = ' col-sm-' + this.labelsm;
30661                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30662                 }
30663
30664                 if(this.labelxs > 0){
30665                     labelCls = ' col-xs-' + this.labelxs;
30666                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30667                 }
30668             }
30669             
30670             label.cls += ' ' + labelCls;
30671             
30672             cfg.cn.push(label);
30673         }
30674         
30675         Roo.each(['day', 'month', 'year'], function(t){
30676             cfg.cn.push({
30677                 tag : 'div',
30678                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30679             });
30680         }, this);
30681         
30682         return cfg;
30683     },
30684     
30685     inputEl: function ()
30686     {
30687         return this.el.select('.roo-date-split-field-group-value', true).first();
30688     },
30689     
30690     onRender : function(ct, position) 
30691     {
30692         var _this = this;
30693         
30694         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30695         
30696         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30697         
30698         this.dayField = new Roo.bootstrap.ComboBox({
30699             allowBlank : this.dayAllowBlank,
30700             alwaysQuery : true,
30701             displayField : 'value',
30702             editable : false,
30703             fieldLabel : '',
30704             forceSelection : true,
30705             mode : 'local',
30706             placeholder : this.dayPlaceholder,
30707             selectOnFocus : true,
30708             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30709             triggerAction : 'all',
30710             typeAhead : true,
30711             valueField : 'value',
30712             store : new Roo.data.SimpleStore({
30713                 data : (function() {    
30714                     var days = [];
30715                     _this.fireEvent('days', _this, days);
30716                     return days;
30717                 })(),
30718                 fields : [ 'value' ]
30719             }),
30720             listeners : {
30721                 select : function (_self, record, index)
30722                 {
30723                     _this.setValue(_this.getValue());
30724                 }
30725             }
30726         });
30727
30728         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30729         
30730         this.monthField = new Roo.bootstrap.MonthField({
30731             after : '<i class=\"fa fa-calendar\"></i>',
30732             allowBlank : this.monthAllowBlank,
30733             placeholder : this.monthPlaceholder,
30734             readOnly : true,
30735             listeners : {
30736                 render : function (_self)
30737                 {
30738                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30739                         e.preventDefault();
30740                         _self.focus();
30741                     });
30742                 },
30743                 select : function (_self, oldvalue, newvalue)
30744                 {
30745                     _this.setValue(_this.getValue());
30746                 }
30747             }
30748         });
30749         
30750         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30751         
30752         this.yearField = new Roo.bootstrap.ComboBox({
30753             allowBlank : this.yearAllowBlank,
30754             alwaysQuery : true,
30755             displayField : 'value',
30756             editable : false,
30757             fieldLabel : '',
30758             forceSelection : true,
30759             mode : 'local',
30760             placeholder : this.yearPlaceholder,
30761             selectOnFocus : true,
30762             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30763             triggerAction : 'all',
30764             typeAhead : true,
30765             valueField : 'value',
30766             store : new Roo.data.SimpleStore({
30767                 data : (function() {
30768                     var years = [];
30769                     _this.fireEvent('years', _this, years);
30770                     return years;
30771                 })(),
30772                 fields : [ 'value' ]
30773             }),
30774             listeners : {
30775                 select : function (_self, record, index)
30776                 {
30777                     _this.setValue(_this.getValue());
30778                 }
30779             }
30780         });
30781
30782         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30783     },
30784     
30785     setValue : function(v, format)
30786     {
30787         this.inputEl.dom.value = v;
30788         
30789         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30790         
30791         var d = Date.parseDate(v, f);
30792         
30793         if(!d){
30794             this.validate();
30795             return;
30796         }
30797         
30798         this.setDay(d.format(this.dayFormat));
30799         this.setMonth(d.format(this.monthFormat));
30800         this.setYear(d.format(this.yearFormat));
30801         
30802         this.validate();
30803         
30804         return;
30805     },
30806     
30807     setDay : function(v)
30808     {
30809         this.dayField.setValue(v);
30810         this.inputEl.dom.value = this.getValue();
30811         this.validate();
30812         return;
30813     },
30814     
30815     setMonth : function(v)
30816     {
30817         this.monthField.setValue(v, true);
30818         this.inputEl.dom.value = this.getValue();
30819         this.validate();
30820         return;
30821     },
30822     
30823     setYear : function(v)
30824     {
30825         this.yearField.setValue(v);
30826         this.inputEl.dom.value = this.getValue();
30827         this.validate();
30828         return;
30829     },
30830     
30831     getDay : function()
30832     {
30833         return this.dayField.getValue();
30834     },
30835     
30836     getMonth : function()
30837     {
30838         return this.monthField.getValue();
30839     },
30840     
30841     getYear : function()
30842     {
30843         return this.yearField.getValue();
30844     },
30845     
30846     getValue : function()
30847     {
30848         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30849         
30850         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30851         
30852         return date;
30853     },
30854     
30855     reset : function()
30856     {
30857         this.setDay('');
30858         this.setMonth('');
30859         this.setYear('');
30860         this.inputEl.dom.value = '';
30861         this.validate();
30862         return;
30863     },
30864     
30865     validate : function()
30866     {
30867         var d = this.dayField.validate();
30868         var m = this.monthField.validate();
30869         var y = this.yearField.validate();
30870         
30871         var valid = true;
30872         
30873         if(
30874                 (!this.dayAllowBlank && !d) ||
30875                 (!this.monthAllowBlank && !m) ||
30876                 (!this.yearAllowBlank && !y)
30877         ){
30878             valid = false;
30879         }
30880         
30881         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30882             return valid;
30883         }
30884         
30885         if(valid){
30886             this.markValid();
30887             return valid;
30888         }
30889         
30890         this.markInvalid();
30891         
30892         return valid;
30893     },
30894     
30895     markValid : function()
30896     {
30897         
30898         var label = this.el.select('label', true).first();
30899         var icon = this.el.select('i.fa-star', true).first();
30900
30901         if(label && icon){
30902             icon.remove();
30903         }
30904         
30905         this.fireEvent('valid', this);
30906     },
30907     
30908      /**
30909      * Mark this field as invalid
30910      * @param {String} msg The validation message
30911      */
30912     markInvalid : function(msg)
30913     {
30914         
30915         var label = this.el.select('label', true).first();
30916         var icon = this.el.select('i.fa-star', true).first();
30917
30918         if(label && !icon){
30919             this.el.select('.roo-date-split-field-label', true).createChild({
30920                 tag : 'i',
30921                 cls : 'text-danger fa fa-lg fa-star',
30922                 tooltip : 'This field is required',
30923                 style : 'margin-right:5px;'
30924             }, label, true);
30925         }
30926         
30927         this.fireEvent('invalid', this, msg);
30928     },
30929     
30930     clearInvalid : function()
30931     {
30932         var label = this.el.select('label', true).first();
30933         var icon = this.el.select('i.fa-star', true).first();
30934
30935         if(label && icon){
30936             icon.remove();
30937         }
30938         
30939         this.fireEvent('valid', this);
30940     },
30941     
30942     getName: function()
30943     {
30944         return this.name;
30945     }
30946     
30947 });
30948
30949  /**
30950  *
30951  * This is based on 
30952  * http://masonry.desandro.com
30953  *
30954  * The idea is to render all the bricks based on vertical width...
30955  *
30956  * The original code extends 'outlayer' - we might need to use that....
30957  * 
30958  */
30959
30960
30961 /**
30962  * @class Roo.bootstrap.LayoutMasonry
30963  * @extends Roo.bootstrap.Component
30964  * Bootstrap Layout Masonry class
30965  * 
30966  * @constructor
30967  * Create a new Element
30968  * @param {Object} config The config object
30969  */
30970
30971 Roo.bootstrap.LayoutMasonry = function(config){
30972     
30973     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30974     
30975     this.bricks = [];
30976     
30977     Roo.bootstrap.LayoutMasonry.register(this);
30978     
30979     this.addEvents({
30980         // raw events
30981         /**
30982          * @event layout
30983          * Fire after layout the items
30984          * @param {Roo.bootstrap.LayoutMasonry} this
30985          * @param {Roo.EventObject} e
30986          */
30987         "layout" : true
30988     });
30989     
30990 };
30991
30992 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30993     
30994     /**
30995      * @cfg {Boolean} isLayoutInstant = no animation?
30996      */   
30997     isLayoutInstant : false, // needed?
30998    
30999     /**
31000      * @cfg {Number} boxWidth  width of the columns
31001      */   
31002     boxWidth : 450,
31003     
31004       /**
31005      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31006      */   
31007     boxHeight : 0,
31008     
31009     /**
31010      * @cfg {Number} padWidth padding below box..
31011      */   
31012     padWidth : 10, 
31013     
31014     /**
31015      * @cfg {Number} gutter gutter width..
31016      */   
31017     gutter : 10,
31018     
31019      /**
31020      * @cfg {Number} maxCols maximum number of columns
31021      */   
31022     
31023     maxCols: 0,
31024     
31025     /**
31026      * @cfg {Boolean} isAutoInitial defalut true
31027      */   
31028     isAutoInitial : true, 
31029     
31030     containerWidth: 0,
31031     
31032     /**
31033      * @cfg {Boolean} isHorizontal defalut false
31034      */   
31035     isHorizontal : false, 
31036
31037     currentSize : null,
31038     
31039     tag: 'div',
31040     
31041     cls: '',
31042     
31043     bricks: null, //CompositeElement
31044     
31045     cols : 1,
31046     
31047     _isLayoutInited : false,
31048     
31049 //    isAlternative : false, // only use for vertical layout...
31050     
31051     /**
31052      * @cfg {Number} alternativePadWidth padding below box..
31053      */   
31054     alternativePadWidth : 50,
31055     
31056     selectedBrick : [],
31057     
31058     getAutoCreate : function(){
31059         
31060         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31061         
31062         var cfg = {
31063             tag: this.tag,
31064             cls: 'blog-masonary-wrapper ' + this.cls,
31065             cn : {
31066                 cls : 'mas-boxes masonary'
31067             }
31068         };
31069         
31070         return cfg;
31071     },
31072     
31073     getChildContainer: function( )
31074     {
31075         if (this.boxesEl) {
31076             return this.boxesEl;
31077         }
31078         
31079         this.boxesEl = this.el.select('.mas-boxes').first();
31080         
31081         return this.boxesEl;
31082     },
31083     
31084     
31085     initEvents : function()
31086     {
31087         var _this = this;
31088         
31089         if(this.isAutoInitial){
31090             Roo.log('hook children rendered');
31091             this.on('childrenrendered', function() {
31092                 Roo.log('children rendered');
31093                 _this.initial();
31094             } ,this);
31095         }
31096     },
31097     
31098     initial : function()
31099     {
31100         this.selectedBrick = [];
31101         
31102         this.currentSize = this.el.getBox(true);
31103         
31104         Roo.EventManager.onWindowResize(this.resize, this); 
31105
31106         if(!this.isAutoInitial){
31107             this.layout();
31108             return;
31109         }
31110         
31111         this.layout();
31112         
31113         return;
31114         //this.layout.defer(500,this);
31115         
31116     },
31117     
31118     resize : function()
31119     {
31120         var cs = this.el.getBox(true);
31121         
31122         if (
31123                 this.currentSize.width == cs.width && 
31124                 this.currentSize.x == cs.x && 
31125                 this.currentSize.height == cs.height && 
31126                 this.currentSize.y == cs.y 
31127         ) {
31128             Roo.log("no change in with or X or Y");
31129             return;
31130         }
31131         
31132         this.currentSize = cs;
31133         
31134         this.layout();
31135         
31136     },
31137     
31138     layout : function()
31139     {   
31140         this._resetLayout();
31141         
31142         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31143         
31144         this.layoutItems( isInstant );
31145       
31146         this._isLayoutInited = true;
31147         
31148         this.fireEvent('layout', this);
31149         
31150     },
31151     
31152     _resetLayout : function()
31153     {
31154         if(this.isHorizontal){
31155             this.horizontalMeasureColumns();
31156             return;
31157         }
31158         
31159         this.verticalMeasureColumns();
31160         
31161     },
31162     
31163     verticalMeasureColumns : function()
31164     {
31165         this.getContainerWidth();
31166         
31167 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31168 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31169 //            return;
31170 //        }
31171         
31172         var boxWidth = this.boxWidth + this.padWidth;
31173         
31174         if(this.containerWidth < this.boxWidth){
31175             boxWidth = this.containerWidth
31176         }
31177         
31178         var containerWidth = this.containerWidth;
31179         
31180         var cols = Math.floor(containerWidth / boxWidth);
31181         
31182         this.cols = Math.max( cols, 1 );
31183         
31184         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31185         
31186         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31187         
31188         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31189         
31190         this.colWidth = boxWidth + avail - this.padWidth;
31191         
31192         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31193         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31194     },
31195     
31196     horizontalMeasureColumns : function()
31197     {
31198         this.getContainerWidth();
31199         
31200         var boxWidth = this.boxWidth;
31201         
31202         if(this.containerWidth < boxWidth){
31203             boxWidth = this.containerWidth;
31204         }
31205         
31206         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31207         
31208         this.el.setHeight(boxWidth);
31209         
31210     },
31211     
31212     getContainerWidth : function()
31213     {
31214         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31215     },
31216     
31217     layoutItems : function( isInstant )
31218     {
31219         Roo.log(this.bricks);
31220         
31221         var items = Roo.apply([], this.bricks);
31222         
31223         if(this.isHorizontal){
31224             this._horizontalLayoutItems( items , isInstant );
31225             return;
31226         }
31227         
31228 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31229 //            this._verticalAlternativeLayoutItems( items , isInstant );
31230 //            return;
31231 //        }
31232         
31233         this._verticalLayoutItems( items , isInstant );
31234         
31235     },
31236     
31237     _verticalLayoutItems : function ( items , isInstant)
31238     {
31239         if ( !items || !items.length ) {
31240             return;
31241         }
31242         
31243         var standard = [
31244             ['xs', 'xs', 'xs', 'tall'],
31245             ['xs', 'xs', 'tall'],
31246             ['xs', 'xs', 'sm'],
31247             ['xs', 'xs', 'xs'],
31248             ['xs', 'tall'],
31249             ['xs', 'sm'],
31250             ['xs', 'xs'],
31251             ['xs'],
31252             
31253             ['sm', 'xs', 'xs'],
31254             ['sm', 'xs'],
31255             ['sm'],
31256             
31257             ['tall', 'xs', 'xs', 'xs'],
31258             ['tall', 'xs', 'xs'],
31259             ['tall', 'xs'],
31260             ['tall']
31261             
31262         ];
31263         
31264         var queue = [];
31265         
31266         var boxes = [];
31267         
31268         var box = [];
31269         
31270         Roo.each(items, function(item, k){
31271             
31272             switch (item.size) {
31273                 // these layouts take up a full box,
31274                 case 'md' :
31275                 case 'md-left' :
31276                 case 'md-right' :
31277                 case 'wide' :
31278                     
31279                     if(box.length){
31280                         boxes.push(box);
31281                         box = [];
31282                     }
31283                     
31284                     boxes.push([item]);
31285                     
31286                     break;
31287                     
31288                 case 'xs' :
31289                 case 'sm' :
31290                 case 'tall' :
31291                     
31292                     box.push(item);
31293                     
31294                     break;
31295                 default :
31296                     break;
31297                     
31298             }
31299             
31300         }, this);
31301         
31302         if(box.length){
31303             boxes.push(box);
31304             box = [];
31305         }
31306         
31307         var filterPattern = function(box, length)
31308         {
31309             if(!box.length){
31310                 return;
31311             }
31312             
31313             var match = false;
31314             
31315             var pattern = box.slice(0, length);
31316             
31317             var format = [];
31318             
31319             Roo.each(pattern, function(i){
31320                 format.push(i.size);
31321             }, this);
31322             
31323             Roo.each(standard, function(s){
31324                 
31325                 if(String(s) != String(format)){
31326                     return;
31327                 }
31328                 
31329                 match = true;
31330                 return false;
31331                 
31332             }, this);
31333             
31334             if(!match && length == 1){
31335                 return;
31336             }
31337             
31338             if(!match){
31339                 filterPattern(box, length - 1);
31340                 return;
31341             }
31342                 
31343             queue.push(pattern);
31344
31345             box = box.slice(length, box.length);
31346
31347             filterPattern(box, 4);
31348
31349             return;
31350             
31351         }
31352         
31353         Roo.each(boxes, function(box, k){
31354             
31355             if(!box.length){
31356                 return;
31357             }
31358             
31359             if(box.length == 1){
31360                 queue.push(box);
31361                 return;
31362             }
31363             
31364             filterPattern(box, 4);
31365             
31366         }, this);
31367         
31368         this._processVerticalLayoutQueue( queue, isInstant );
31369         
31370     },
31371     
31372 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31373 //    {
31374 //        if ( !items || !items.length ) {
31375 //            return;
31376 //        }
31377 //
31378 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31379 //        
31380 //    },
31381     
31382     _horizontalLayoutItems : function ( items , isInstant)
31383     {
31384         if ( !items || !items.length || items.length < 3) {
31385             return;
31386         }
31387         
31388         items.reverse();
31389         
31390         var eItems = items.slice(0, 3);
31391         
31392         items = items.slice(3, items.length);
31393         
31394         var standard = [
31395             ['xs', 'xs', 'xs', 'wide'],
31396             ['xs', 'xs', 'wide'],
31397             ['xs', 'xs', 'sm'],
31398             ['xs', 'xs', 'xs'],
31399             ['xs', 'wide'],
31400             ['xs', 'sm'],
31401             ['xs', 'xs'],
31402             ['xs'],
31403             
31404             ['sm', 'xs', 'xs'],
31405             ['sm', 'xs'],
31406             ['sm'],
31407             
31408             ['wide', 'xs', 'xs', 'xs'],
31409             ['wide', 'xs', 'xs'],
31410             ['wide', 'xs'],
31411             ['wide'],
31412             
31413             ['wide-thin']
31414         ];
31415         
31416         var queue = [];
31417         
31418         var boxes = [];
31419         
31420         var box = [];
31421         
31422         Roo.each(items, function(item, k){
31423             
31424             switch (item.size) {
31425                 case 'md' :
31426                 case 'md-left' :
31427                 case 'md-right' :
31428                 case 'tall' :
31429                     
31430                     if(box.length){
31431                         boxes.push(box);
31432                         box = [];
31433                     }
31434                     
31435                     boxes.push([item]);
31436                     
31437                     break;
31438                     
31439                 case 'xs' :
31440                 case 'sm' :
31441                 case 'wide' :
31442                 case 'wide-thin' :
31443                     
31444                     box.push(item);
31445                     
31446                     break;
31447                 default :
31448                     break;
31449                     
31450             }
31451             
31452         }, this);
31453         
31454         if(box.length){
31455             boxes.push(box);
31456             box = [];
31457         }
31458         
31459         var filterPattern = function(box, length)
31460         {
31461             if(!box.length){
31462                 return;
31463             }
31464             
31465             var match = false;
31466             
31467             var pattern = box.slice(0, length);
31468             
31469             var format = [];
31470             
31471             Roo.each(pattern, function(i){
31472                 format.push(i.size);
31473             }, this);
31474             
31475             Roo.each(standard, function(s){
31476                 
31477                 if(String(s) != String(format)){
31478                     return;
31479                 }
31480                 
31481                 match = true;
31482                 return false;
31483                 
31484             }, this);
31485             
31486             if(!match && length == 1){
31487                 return;
31488             }
31489             
31490             if(!match){
31491                 filterPattern(box, length - 1);
31492                 return;
31493             }
31494                 
31495             queue.push(pattern);
31496
31497             box = box.slice(length, box.length);
31498
31499             filterPattern(box, 4);
31500
31501             return;
31502             
31503         }
31504         
31505         Roo.each(boxes, function(box, k){
31506             
31507             if(!box.length){
31508                 return;
31509             }
31510             
31511             if(box.length == 1){
31512                 queue.push(box);
31513                 return;
31514             }
31515             
31516             filterPattern(box, 4);
31517             
31518         }, this);
31519         
31520         
31521         var prune = [];
31522         
31523         var pos = this.el.getBox(true);
31524         
31525         var minX = pos.x;
31526         
31527         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31528         
31529         var hit_end = false;
31530         
31531         Roo.each(queue, function(box){
31532             
31533             if(hit_end){
31534                 
31535                 Roo.each(box, function(b){
31536                 
31537                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31538                     b.el.hide();
31539
31540                 }, this);
31541
31542                 return;
31543             }
31544             
31545             var mx = 0;
31546             
31547             Roo.each(box, function(b){
31548                 
31549                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31550                 b.el.show();
31551
31552                 mx = Math.max(mx, b.x);
31553                 
31554             }, this);
31555             
31556             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31557             
31558             if(maxX < minX){
31559                 
31560                 Roo.each(box, function(b){
31561                 
31562                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31563                     b.el.hide();
31564                     
31565                 }, this);
31566                 
31567                 hit_end = true;
31568                 
31569                 return;
31570             }
31571             
31572             prune.push(box);
31573             
31574         }, this);
31575         
31576         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31577     },
31578     
31579     /** Sets position of item in DOM
31580     * @param {Element} item
31581     * @param {Number} x - horizontal position
31582     * @param {Number} y - vertical position
31583     * @param {Boolean} isInstant - disables transitions
31584     */
31585     _processVerticalLayoutQueue : function( queue, isInstant )
31586     {
31587         var pos = this.el.getBox(true);
31588         var x = pos.x;
31589         var y = pos.y;
31590         var maxY = [];
31591         
31592         for (var i = 0; i < this.cols; i++){
31593             maxY[i] = pos.y;
31594         }
31595         
31596         Roo.each(queue, function(box, k){
31597             
31598             var col = k % this.cols;
31599             
31600             Roo.each(box, function(b,kk){
31601                 
31602                 b.el.position('absolute');
31603                 
31604                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31605                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31606                 
31607                 if(b.size == 'md-left' || b.size == 'md-right'){
31608                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31609                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31610                 }
31611                 
31612                 b.el.setWidth(width);
31613                 b.el.setHeight(height);
31614                 // iframe?
31615                 b.el.select('iframe',true).setSize(width,height);
31616                 
31617             }, this);
31618             
31619             for (var i = 0; i < this.cols; i++){
31620                 
31621                 if(maxY[i] < maxY[col]){
31622                     col = i;
31623                     continue;
31624                 }
31625                 
31626                 col = Math.min(col, i);
31627                 
31628             }
31629             
31630             x = pos.x + col * (this.colWidth + this.padWidth);
31631             
31632             y = maxY[col];
31633             
31634             var positions = [];
31635             
31636             switch (box.length){
31637                 case 1 :
31638                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31639                     break;
31640                 case 2 :
31641                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31642                     break;
31643                 case 3 :
31644                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31645                     break;
31646                 case 4 :
31647                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31648                     break;
31649                 default :
31650                     break;
31651             }
31652             
31653             Roo.each(box, function(b,kk){
31654                 
31655                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31656                 
31657                 var sz = b.el.getSize();
31658                 
31659                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31660                 
31661             }, this);
31662             
31663         }, this);
31664         
31665         var mY = 0;
31666         
31667         for (var i = 0; i < this.cols; i++){
31668             mY = Math.max(mY, maxY[i]);
31669         }
31670         
31671         this.el.setHeight(mY - pos.y);
31672         
31673     },
31674     
31675 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31676 //    {
31677 //        var pos = this.el.getBox(true);
31678 //        var x = pos.x;
31679 //        var y = pos.y;
31680 //        var maxX = pos.right;
31681 //        
31682 //        var maxHeight = 0;
31683 //        
31684 //        Roo.each(items, function(item, k){
31685 //            
31686 //            var c = k % 2;
31687 //            
31688 //            item.el.position('absolute');
31689 //                
31690 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31691 //
31692 //            item.el.setWidth(width);
31693 //
31694 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31695 //
31696 //            item.el.setHeight(height);
31697 //            
31698 //            if(c == 0){
31699 //                item.el.setXY([x, y], isInstant ? false : true);
31700 //            } else {
31701 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31702 //            }
31703 //            
31704 //            y = y + height + this.alternativePadWidth;
31705 //            
31706 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31707 //            
31708 //        }, this);
31709 //        
31710 //        this.el.setHeight(maxHeight);
31711 //        
31712 //    },
31713     
31714     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31715     {
31716         var pos = this.el.getBox(true);
31717         
31718         var minX = pos.x;
31719         var minY = pos.y;
31720         
31721         var maxX = pos.right;
31722         
31723         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31724         
31725         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31726         
31727         Roo.each(queue, function(box, k){
31728             
31729             Roo.each(box, function(b, kk){
31730                 
31731                 b.el.position('absolute');
31732                 
31733                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31734                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31735                 
31736                 if(b.size == 'md-left' || b.size == 'md-right'){
31737                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31738                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31739                 }
31740                 
31741                 b.el.setWidth(width);
31742                 b.el.setHeight(height);
31743                 
31744             }, this);
31745             
31746             if(!box.length){
31747                 return;
31748             }
31749             
31750             var positions = [];
31751             
31752             switch (box.length){
31753                 case 1 :
31754                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31755                     break;
31756                 case 2 :
31757                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31758                     break;
31759                 case 3 :
31760                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31761                     break;
31762                 case 4 :
31763                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31764                     break;
31765                 default :
31766                     break;
31767             }
31768             
31769             Roo.each(box, function(b,kk){
31770                 
31771                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31772                 
31773                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31774                 
31775             }, this);
31776             
31777         }, this);
31778         
31779     },
31780     
31781     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31782     {
31783         Roo.each(eItems, function(b,k){
31784             
31785             b.size = (k == 0) ? 'sm' : 'xs';
31786             b.x = (k == 0) ? 2 : 1;
31787             b.y = (k == 0) ? 2 : 1;
31788             
31789             b.el.position('absolute');
31790             
31791             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31792                 
31793             b.el.setWidth(width);
31794             
31795             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31796             
31797             b.el.setHeight(height);
31798             
31799         }, this);
31800
31801         var positions = [];
31802         
31803         positions.push({
31804             x : maxX - this.unitWidth * 2 - this.gutter,
31805             y : minY
31806         });
31807         
31808         positions.push({
31809             x : maxX - this.unitWidth,
31810             y : minY + (this.unitWidth + this.gutter) * 2
31811         });
31812         
31813         positions.push({
31814             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31815             y : minY
31816         });
31817         
31818         Roo.each(eItems, function(b,k){
31819             
31820             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31821
31822         }, this);
31823         
31824     },
31825     
31826     getVerticalOneBoxColPositions : function(x, y, box)
31827     {
31828         var pos = [];
31829         
31830         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31831         
31832         if(box[0].size == 'md-left'){
31833             rand = 0;
31834         }
31835         
31836         if(box[0].size == 'md-right'){
31837             rand = 1;
31838         }
31839         
31840         pos.push({
31841             x : x + (this.unitWidth + this.gutter) * rand,
31842             y : y
31843         });
31844         
31845         return pos;
31846     },
31847     
31848     getVerticalTwoBoxColPositions : function(x, y, box)
31849     {
31850         var pos = [];
31851         
31852         if(box[0].size == 'xs'){
31853             
31854             pos.push({
31855                 x : x,
31856                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31857             });
31858
31859             pos.push({
31860                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31861                 y : y
31862             });
31863             
31864             return pos;
31865             
31866         }
31867         
31868         pos.push({
31869             x : x,
31870             y : y
31871         });
31872
31873         pos.push({
31874             x : x + (this.unitWidth + this.gutter) * 2,
31875             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31876         });
31877         
31878         return pos;
31879         
31880     },
31881     
31882     getVerticalThreeBoxColPositions : function(x, y, box)
31883     {
31884         var pos = [];
31885         
31886         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31887             
31888             pos.push({
31889                 x : x,
31890                 y : y
31891             });
31892
31893             pos.push({
31894                 x : x + (this.unitWidth + this.gutter) * 1,
31895                 y : y
31896             });
31897             
31898             pos.push({
31899                 x : x + (this.unitWidth + this.gutter) * 2,
31900                 y : y
31901             });
31902             
31903             return pos;
31904             
31905         }
31906         
31907         if(box[0].size == 'xs' && box[1].size == 'xs'){
31908             
31909             pos.push({
31910                 x : x,
31911                 y : y
31912             });
31913
31914             pos.push({
31915                 x : x,
31916                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31917             });
31918             
31919             pos.push({
31920                 x : x + (this.unitWidth + this.gutter) * 1,
31921                 y : y
31922             });
31923             
31924             return pos;
31925             
31926         }
31927         
31928         pos.push({
31929             x : x,
31930             y : y
31931         });
31932
31933         pos.push({
31934             x : x + (this.unitWidth + this.gutter) * 2,
31935             y : y
31936         });
31937
31938         pos.push({
31939             x : x + (this.unitWidth + this.gutter) * 2,
31940             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31941         });
31942             
31943         return pos;
31944         
31945     },
31946     
31947     getVerticalFourBoxColPositions : function(x, y, box)
31948     {
31949         var pos = [];
31950         
31951         if(box[0].size == 'xs'){
31952             
31953             pos.push({
31954                 x : x,
31955                 y : y
31956             });
31957
31958             pos.push({
31959                 x : x,
31960                 y : y + (this.unitHeight + this.gutter) * 1
31961             });
31962             
31963             pos.push({
31964                 x : x,
31965                 y : y + (this.unitHeight + this.gutter) * 2
31966             });
31967             
31968             pos.push({
31969                 x : x + (this.unitWidth + this.gutter) * 1,
31970                 y : y
31971             });
31972             
31973             return pos;
31974             
31975         }
31976         
31977         pos.push({
31978             x : x,
31979             y : y
31980         });
31981
31982         pos.push({
31983             x : x + (this.unitWidth + this.gutter) * 2,
31984             y : y
31985         });
31986
31987         pos.push({
31988             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31989             y : y + (this.unitHeight + this.gutter) * 1
31990         });
31991
31992         pos.push({
31993             x : x + (this.unitWidth + this.gutter) * 2,
31994             y : y + (this.unitWidth + this.gutter) * 2
31995         });
31996
31997         return pos;
31998         
31999     },
32000     
32001     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32002     {
32003         var pos = [];
32004         
32005         if(box[0].size == 'md-left'){
32006             pos.push({
32007                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32008                 y : minY
32009             });
32010             
32011             return pos;
32012         }
32013         
32014         if(box[0].size == 'md-right'){
32015             pos.push({
32016                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32017                 y : minY + (this.unitWidth + this.gutter) * 1
32018             });
32019             
32020             return pos;
32021         }
32022         
32023         var rand = Math.floor(Math.random() * (4 - box[0].y));
32024         
32025         pos.push({
32026             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32027             y : minY + (this.unitWidth + this.gutter) * rand
32028         });
32029         
32030         return pos;
32031         
32032     },
32033     
32034     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32035     {
32036         var pos = [];
32037         
32038         if(box[0].size == 'xs'){
32039             
32040             pos.push({
32041                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32042                 y : minY
32043             });
32044
32045             pos.push({
32046                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32047                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32048             });
32049             
32050             return pos;
32051             
32052         }
32053         
32054         pos.push({
32055             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32056             y : minY
32057         });
32058
32059         pos.push({
32060             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32061             y : minY + (this.unitWidth + this.gutter) * 2
32062         });
32063         
32064         return pos;
32065         
32066     },
32067     
32068     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32069     {
32070         var pos = [];
32071         
32072         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32073             
32074             pos.push({
32075                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32076                 y : minY
32077             });
32078
32079             pos.push({
32080                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32081                 y : minY + (this.unitWidth + this.gutter) * 1
32082             });
32083             
32084             pos.push({
32085                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32086                 y : minY + (this.unitWidth + this.gutter) * 2
32087             });
32088             
32089             return pos;
32090             
32091         }
32092         
32093         if(box[0].size == 'xs' && box[1].size == 'xs'){
32094             
32095             pos.push({
32096                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32097                 y : minY
32098             });
32099
32100             pos.push({
32101                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32102                 y : minY
32103             });
32104             
32105             pos.push({
32106                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32107                 y : minY + (this.unitWidth + this.gutter) * 1
32108             });
32109             
32110             return pos;
32111             
32112         }
32113         
32114         pos.push({
32115             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32116             y : minY
32117         });
32118
32119         pos.push({
32120             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32121             y : minY + (this.unitWidth + this.gutter) * 2
32122         });
32123
32124         pos.push({
32125             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32126             y : minY + (this.unitWidth + this.gutter) * 2
32127         });
32128             
32129         return pos;
32130         
32131     },
32132     
32133     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32134     {
32135         var pos = [];
32136         
32137         if(box[0].size == 'xs'){
32138             
32139             pos.push({
32140                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32141                 y : minY
32142             });
32143
32144             pos.push({
32145                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32146                 y : minY
32147             });
32148             
32149             pos.push({
32150                 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),
32151                 y : minY
32152             });
32153             
32154             pos.push({
32155                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32156                 y : minY + (this.unitWidth + this.gutter) * 1
32157             });
32158             
32159             return pos;
32160             
32161         }
32162         
32163         pos.push({
32164             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32165             y : minY
32166         });
32167         
32168         pos.push({
32169             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32170             y : minY + (this.unitWidth + this.gutter) * 2
32171         });
32172         
32173         pos.push({
32174             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32175             y : minY + (this.unitWidth + this.gutter) * 2
32176         });
32177         
32178         pos.push({
32179             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),
32180             y : minY + (this.unitWidth + this.gutter) * 2
32181         });
32182
32183         return pos;
32184         
32185     },
32186     
32187     /**
32188     * remove a Masonry Brick
32189     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32190     */
32191     removeBrick : function(brick_id)
32192     {
32193         if (!brick_id) {
32194             return;
32195         }
32196         
32197         for (var i = 0; i<this.bricks.length; i++) {
32198             if (this.bricks[i].id == brick_id) {
32199                 this.bricks.splice(i,1);
32200                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32201                 this.initial();
32202             }
32203         }
32204     },
32205     
32206     /**
32207     * adds a Masonry Brick
32208     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32209     */
32210     addBrick : function(cfg)
32211     {
32212         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32213         //this.register(cn);
32214         cn.parentId = this.id;
32215         cn.render(this.el);
32216         return cn;
32217     },
32218     
32219     /**
32220     * register a Masonry Brick
32221     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32222     */
32223     
32224     register : function(brick)
32225     {
32226         this.bricks.push(brick);
32227         brick.masonryId = this.id;
32228     },
32229     
32230     /**
32231     * clear all the Masonry Brick
32232     */
32233     clearAll : function()
32234     {
32235         this.bricks = [];
32236         //this.getChildContainer().dom.innerHTML = "";
32237         this.el.dom.innerHTML = '';
32238     },
32239     
32240     getSelected : function()
32241     {
32242         if (!this.selectedBrick) {
32243             return false;
32244         }
32245         
32246         return this.selectedBrick;
32247     }
32248 });
32249
32250 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32251     
32252     groups: {},
32253      /**
32254     * register a Masonry Layout
32255     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32256     */
32257     
32258     register : function(layout)
32259     {
32260         this.groups[layout.id] = layout;
32261     },
32262     /**
32263     * fetch a  Masonry Layout based on the masonry layout ID
32264     * @param {string} the masonry layout to add
32265     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32266     */
32267     
32268     get: function(layout_id) {
32269         if (typeof(this.groups[layout_id]) == 'undefined') {
32270             return false;
32271         }
32272         return this.groups[layout_id] ;
32273     }
32274     
32275     
32276     
32277 });
32278
32279  
32280
32281  /**
32282  *
32283  * This is based on 
32284  * http://masonry.desandro.com
32285  *
32286  * The idea is to render all the bricks based on vertical width...
32287  *
32288  * The original code extends 'outlayer' - we might need to use that....
32289  * 
32290  */
32291
32292
32293 /**
32294  * @class Roo.bootstrap.LayoutMasonryAuto
32295  * @extends Roo.bootstrap.Component
32296  * Bootstrap Layout Masonry class
32297  * 
32298  * @constructor
32299  * Create a new Element
32300  * @param {Object} config The config object
32301  */
32302
32303 Roo.bootstrap.LayoutMasonryAuto = function(config){
32304     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32305 };
32306
32307 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32308     
32309       /**
32310      * @cfg {Boolean} isFitWidth  - resize the width..
32311      */   
32312     isFitWidth : false,  // options..
32313     /**
32314      * @cfg {Boolean} isOriginLeft = left align?
32315      */   
32316     isOriginLeft : true,
32317     /**
32318      * @cfg {Boolean} isOriginTop = top align?
32319      */   
32320     isOriginTop : false,
32321     /**
32322      * @cfg {Boolean} isLayoutInstant = no animation?
32323      */   
32324     isLayoutInstant : false, // needed?
32325     /**
32326      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32327      */   
32328     isResizingContainer : true,
32329     /**
32330      * @cfg {Number} columnWidth  width of the columns 
32331      */   
32332     
32333     columnWidth : 0,
32334     
32335     /**
32336      * @cfg {Number} maxCols maximum number of columns
32337      */   
32338     
32339     maxCols: 0,
32340     /**
32341      * @cfg {Number} padHeight padding below box..
32342      */   
32343     
32344     padHeight : 10, 
32345     
32346     /**
32347      * @cfg {Boolean} isAutoInitial defalut true
32348      */   
32349     
32350     isAutoInitial : true, 
32351     
32352     // private?
32353     gutter : 0,
32354     
32355     containerWidth: 0,
32356     initialColumnWidth : 0,
32357     currentSize : null,
32358     
32359     colYs : null, // array.
32360     maxY : 0,
32361     padWidth: 10,
32362     
32363     
32364     tag: 'div',
32365     cls: '',
32366     bricks: null, //CompositeElement
32367     cols : 0, // array?
32368     // element : null, // wrapped now this.el
32369     _isLayoutInited : null, 
32370     
32371     
32372     getAutoCreate : function(){
32373         
32374         var cfg = {
32375             tag: this.tag,
32376             cls: 'blog-masonary-wrapper ' + this.cls,
32377             cn : {
32378                 cls : 'mas-boxes masonary'
32379             }
32380         };
32381         
32382         return cfg;
32383     },
32384     
32385     getChildContainer: function( )
32386     {
32387         if (this.boxesEl) {
32388             return this.boxesEl;
32389         }
32390         
32391         this.boxesEl = this.el.select('.mas-boxes').first();
32392         
32393         return this.boxesEl;
32394     },
32395     
32396     
32397     initEvents : function()
32398     {
32399         var _this = this;
32400         
32401         if(this.isAutoInitial){
32402             Roo.log('hook children rendered');
32403             this.on('childrenrendered', function() {
32404                 Roo.log('children rendered');
32405                 _this.initial();
32406             } ,this);
32407         }
32408         
32409     },
32410     
32411     initial : function()
32412     {
32413         this.reloadItems();
32414
32415         this.currentSize = this.el.getBox(true);
32416
32417         /// was window resize... - let's see if this works..
32418         Roo.EventManager.onWindowResize(this.resize, this); 
32419
32420         if(!this.isAutoInitial){
32421             this.layout();
32422             return;
32423         }
32424         
32425         this.layout.defer(500,this);
32426     },
32427     
32428     reloadItems: function()
32429     {
32430         this.bricks = this.el.select('.masonry-brick', true);
32431         
32432         this.bricks.each(function(b) {
32433             //Roo.log(b.getSize());
32434             if (!b.attr('originalwidth')) {
32435                 b.attr('originalwidth',  b.getSize().width);
32436             }
32437             
32438         });
32439         
32440         Roo.log(this.bricks.elements.length);
32441     },
32442     
32443     resize : function()
32444     {
32445         Roo.log('resize');
32446         var cs = this.el.getBox(true);
32447         
32448         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32449             Roo.log("no change in with or X");
32450             return;
32451         }
32452         this.currentSize = cs;
32453         this.layout();
32454     },
32455     
32456     layout : function()
32457     {
32458          Roo.log('layout');
32459         this._resetLayout();
32460         //this._manageStamps();
32461       
32462         // don't animate first layout
32463         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32464         this.layoutItems( isInstant );
32465       
32466         // flag for initalized
32467         this._isLayoutInited = true;
32468     },
32469     
32470     layoutItems : function( isInstant )
32471     {
32472         //var items = this._getItemsForLayout( this.items );
32473         // original code supports filtering layout items.. we just ignore it..
32474         
32475         this._layoutItems( this.bricks , isInstant );
32476       
32477         this._postLayout();
32478     },
32479     _layoutItems : function ( items , isInstant)
32480     {
32481        //this.fireEvent( 'layout', this, items );
32482     
32483
32484         if ( !items || !items.elements.length ) {
32485           // no items, emit event with empty array
32486             return;
32487         }
32488
32489         var queue = [];
32490         items.each(function(item) {
32491             Roo.log("layout item");
32492             Roo.log(item);
32493             // get x/y object from method
32494             var position = this._getItemLayoutPosition( item );
32495             // enqueue
32496             position.item = item;
32497             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32498             queue.push( position );
32499         }, this);
32500       
32501         this._processLayoutQueue( queue );
32502     },
32503     /** Sets position of item in DOM
32504     * @param {Element} item
32505     * @param {Number} x - horizontal position
32506     * @param {Number} y - vertical position
32507     * @param {Boolean} isInstant - disables transitions
32508     */
32509     _processLayoutQueue : function( queue )
32510     {
32511         for ( var i=0, len = queue.length; i < len; i++ ) {
32512             var obj = queue[i];
32513             obj.item.position('absolute');
32514             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32515         }
32516     },
32517       
32518     
32519     /**
32520     * Any logic you want to do after each layout,
32521     * i.e. size the container
32522     */
32523     _postLayout : function()
32524     {
32525         this.resizeContainer();
32526     },
32527     
32528     resizeContainer : function()
32529     {
32530         if ( !this.isResizingContainer ) {
32531             return;
32532         }
32533         var size = this._getContainerSize();
32534         if ( size ) {
32535             this.el.setSize(size.width,size.height);
32536             this.boxesEl.setSize(size.width,size.height);
32537         }
32538     },
32539     
32540     
32541     
32542     _resetLayout : function()
32543     {
32544         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32545         this.colWidth = this.el.getWidth();
32546         //this.gutter = this.el.getWidth(); 
32547         
32548         this.measureColumns();
32549
32550         // reset column Y
32551         var i = this.cols;
32552         this.colYs = [];
32553         while (i--) {
32554             this.colYs.push( 0 );
32555         }
32556     
32557         this.maxY = 0;
32558     },
32559
32560     measureColumns : function()
32561     {
32562         this.getContainerWidth();
32563       // if columnWidth is 0, default to outerWidth of first item
32564         if ( !this.columnWidth ) {
32565             var firstItem = this.bricks.first();
32566             Roo.log(firstItem);
32567             this.columnWidth  = this.containerWidth;
32568             if (firstItem && firstItem.attr('originalwidth') ) {
32569                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32570             }
32571             // columnWidth fall back to item of first element
32572             Roo.log("set column width?");
32573                         this.initialColumnWidth = this.columnWidth  ;
32574
32575             // if first elem has no width, default to size of container
32576             
32577         }
32578         
32579         
32580         if (this.initialColumnWidth) {
32581             this.columnWidth = this.initialColumnWidth;
32582         }
32583         
32584         
32585             
32586         // column width is fixed at the top - however if container width get's smaller we should
32587         // reduce it...
32588         
32589         // this bit calcs how man columns..
32590             
32591         var columnWidth = this.columnWidth += this.gutter;
32592       
32593         // calculate columns
32594         var containerWidth = this.containerWidth + this.gutter;
32595         
32596         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32597         // fix rounding errors, typically with gutters
32598         var excess = columnWidth - containerWidth % columnWidth;
32599         
32600         
32601         // if overshoot is less than a pixel, round up, otherwise floor it
32602         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32603         cols = Math[ mathMethod ]( cols );
32604         this.cols = Math.max( cols, 1 );
32605         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32606         
32607          // padding positioning..
32608         var totalColWidth = this.cols * this.columnWidth;
32609         var padavail = this.containerWidth - totalColWidth;
32610         // so for 2 columns - we need 3 'pads'
32611         
32612         var padNeeded = (1+this.cols) * this.padWidth;
32613         
32614         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32615         
32616         this.columnWidth += padExtra
32617         //this.padWidth = Math.floor(padavail /  ( this.cols));
32618         
32619         // adjust colum width so that padding is fixed??
32620         
32621         // we have 3 columns ... total = width * 3
32622         // we have X left over... that should be used by 
32623         
32624         //if (this.expandC) {
32625             
32626         //}
32627         
32628         
32629         
32630     },
32631     
32632     getContainerWidth : function()
32633     {
32634        /* // container is parent if fit width
32635         var container = this.isFitWidth ? this.element.parentNode : this.element;
32636         // check that this.size and size are there
32637         // IE8 triggers resize on body size change, so they might not be
32638         
32639         var size = getSize( container );  //FIXME
32640         this.containerWidth = size && size.innerWidth; //FIXME
32641         */
32642          
32643         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32644         
32645     },
32646     
32647     _getItemLayoutPosition : function( item )  // what is item?
32648     {
32649         // we resize the item to our columnWidth..
32650       
32651         item.setWidth(this.columnWidth);
32652         item.autoBoxAdjust  = false;
32653         
32654         var sz = item.getSize();
32655  
32656         // how many columns does this brick span
32657         var remainder = this.containerWidth % this.columnWidth;
32658         
32659         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32660         // round if off by 1 pixel, otherwise use ceil
32661         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32662         colSpan = Math.min( colSpan, this.cols );
32663         
32664         // normally this should be '1' as we dont' currently allow multi width columns..
32665         
32666         var colGroup = this._getColGroup( colSpan );
32667         // get the minimum Y value from the columns
32668         var minimumY = Math.min.apply( Math, colGroup );
32669         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32670         
32671         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32672          
32673         // position the brick
32674         var position = {
32675             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32676             y: this.currentSize.y + minimumY + this.padHeight
32677         };
32678         
32679         Roo.log(position);
32680         // apply setHeight to necessary columns
32681         var setHeight = minimumY + sz.height + this.padHeight;
32682         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32683         
32684         var setSpan = this.cols + 1 - colGroup.length;
32685         for ( var i = 0; i < setSpan; i++ ) {
32686           this.colYs[ shortColIndex + i ] = setHeight ;
32687         }
32688       
32689         return position;
32690     },
32691     
32692     /**
32693      * @param {Number} colSpan - number of columns the element spans
32694      * @returns {Array} colGroup
32695      */
32696     _getColGroup : function( colSpan )
32697     {
32698         if ( colSpan < 2 ) {
32699           // if brick spans only one column, use all the column Ys
32700           return this.colYs;
32701         }
32702       
32703         var colGroup = [];
32704         // how many different places could this brick fit horizontally
32705         var groupCount = this.cols + 1 - colSpan;
32706         // for each group potential horizontal position
32707         for ( var i = 0; i < groupCount; i++ ) {
32708           // make an array of colY values for that one group
32709           var groupColYs = this.colYs.slice( i, i + colSpan );
32710           // and get the max value of the array
32711           colGroup[i] = Math.max.apply( Math, groupColYs );
32712         }
32713         return colGroup;
32714     },
32715     /*
32716     _manageStamp : function( stamp )
32717     {
32718         var stampSize =  stamp.getSize();
32719         var offset = stamp.getBox();
32720         // get the columns that this stamp affects
32721         var firstX = this.isOriginLeft ? offset.x : offset.right;
32722         var lastX = firstX + stampSize.width;
32723         var firstCol = Math.floor( firstX / this.columnWidth );
32724         firstCol = Math.max( 0, firstCol );
32725         
32726         var lastCol = Math.floor( lastX / this.columnWidth );
32727         // lastCol should not go over if multiple of columnWidth #425
32728         lastCol -= lastX % this.columnWidth ? 0 : 1;
32729         lastCol = Math.min( this.cols - 1, lastCol );
32730         
32731         // set colYs to bottom of the stamp
32732         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32733             stampSize.height;
32734             
32735         for ( var i = firstCol; i <= lastCol; i++ ) {
32736           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32737         }
32738     },
32739     */
32740     
32741     _getContainerSize : function()
32742     {
32743         this.maxY = Math.max.apply( Math, this.colYs );
32744         var size = {
32745             height: this.maxY
32746         };
32747       
32748         if ( this.isFitWidth ) {
32749             size.width = this._getContainerFitWidth();
32750         }
32751       
32752         return size;
32753     },
32754     
32755     _getContainerFitWidth : function()
32756     {
32757         var unusedCols = 0;
32758         // count unused columns
32759         var i = this.cols;
32760         while ( --i ) {
32761           if ( this.colYs[i] !== 0 ) {
32762             break;
32763           }
32764           unusedCols++;
32765         }
32766         // fit container to columns that have been used
32767         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32768     },
32769     
32770     needsResizeLayout : function()
32771     {
32772         var previousWidth = this.containerWidth;
32773         this.getContainerWidth();
32774         return previousWidth !== this.containerWidth;
32775     }
32776  
32777 });
32778
32779  
32780
32781  /*
32782  * - LGPL
32783  *
32784  * element
32785  * 
32786  */
32787
32788 /**
32789  * @class Roo.bootstrap.MasonryBrick
32790  * @extends Roo.bootstrap.Component
32791  * Bootstrap MasonryBrick class
32792  * 
32793  * @constructor
32794  * Create a new MasonryBrick
32795  * @param {Object} config The config object
32796  */
32797
32798 Roo.bootstrap.MasonryBrick = function(config){
32799     
32800     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32801     
32802     Roo.bootstrap.MasonryBrick.register(this);
32803     
32804     this.addEvents({
32805         // raw events
32806         /**
32807          * @event click
32808          * When a MasonryBrick is clcik
32809          * @param {Roo.bootstrap.MasonryBrick} this
32810          * @param {Roo.EventObject} e
32811          */
32812         "click" : true
32813     });
32814 };
32815
32816 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32817     
32818     /**
32819      * @cfg {String} title
32820      */   
32821     title : '',
32822     /**
32823      * @cfg {String} html
32824      */   
32825     html : '',
32826     /**
32827      * @cfg {String} bgimage
32828      */   
32829     bgimage : '',
32830     /**
32831      * @cfg {String} videourl
32832      */   
32833     videourl : '',
32834     /**
32835      * @cfg {String} cls
32836      */   
32837     cls : '',
32838     /**
32839      * @cfg {String} href
32840      */   
32841     href : '',
32842     /**
32843      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32844      */   
32845     size : 'xs',
32846     
32847     /**
32848      * @cfg {String} placetitle (center|bottom)
32849      */   
32850     placetitle : '',
32851     
32852     /**
32853      * @cfg {Boolean} isFitContainer defalut true
32854      */   
32855     isFitContainer : true, 
32856     
32857     /**
32858      * @cfg {Boolean} preventDefault defalut false
32859      */   
32860     preventDefault : false, 
32861     
32862     /**
32863      * @cfg {Boolean} inverse defalut false
32864      */   
32865     maskInverse : false, 
32866     
32867     getAutoCreate : function()
32868     {
32869         if(!this.isFitContainer){
32870             return this.getSplitAutoCreate();
32871         }
32872         
32873         var cls = 'masonry-brick masonry-brick-full';
32874         
32875         if(this.href.length){
32876             cls += ' masonry-brick-link';
32877         }
32878         
32879         if(this.bgimage.length){
32880             cls += ' masonry-brick-image';
32881         }
32882         
32883         if(this.maskInverse){
32884             cls += ' mask-inverse';
32885         }
32886         
32887         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32888             cls += ' enable-mask';
32889         }
32890         
32891         if(this.size){
32892             cls += ' masonry-' + this.size + '-brick';
32893         }
32894         
32895         if(this.placetitle.length){
32896             
32897             switch (this.placetitle) {
32898                 case 'center' :
32899                     cls += ' masonry-center-title';
32900                     break;
32901                 case 'bottom' :
32902                     cls += ' masonry-bottom-title';
32903                     break;
32904                 default:
32905                     break;
32906             }
32907             
32908         } else {
32909             if(!this.html.length && !this.bgimage.length){
32910                 cls += ' masonry-center-title';
32911             }
32912
32913             if(!this.html.length && this.bgimage.length){
32914                 cls += ' masonry-bottom-title';
32915             }
32916         }
32917         
32918         if(this.cls){
32919             cls += ' ' + this.cls;
32920         }
32921         
32922         var cfg = {
32923             tag: (this.href.length) ? 'a' : 'div',
32924             cls: cls,
32925             cn: [
32926                 {
32927                     tag: 'div',
32928                     cls: 'masonry-brick-mask'
32929                 },
32930                 {
32931                     tag: 'div',
32932                     cls: 'masonry-brick-paragraph',
32933                     cn: []
32934                 }
32935             ]
32936         };
32937         
32938         if(this.href.length){
32939             cfg.href = this.href;
32940         }
32941         
32942         var cn = cfg.cn[1].cn;
32943         
32944         if(this.title.length){
32945             cn.push({
32946                 tag: 'h4',
32947                 cls: 'masonry-brick-title',
32948                 html: this.title
32949             });
32950         }
32951         
32952         if(this.html.length){
32953             cn.push({
32954                 tag: 'p',
32955                 cls: 'masonry-brick-text',
32956                 html: this.html
32957             });
32958         }
32959         
32960         if (!this.title.length && !this.html.length) {
32961             cfg.cn[1].cls += ' hide';
32962         }
32963         
32964         if(this.bgimage.length){
32965             cfg.cn.push({
32966                 tag: 'img',
32967                 cls: 'masonry-brick-image-view',
32968                 src: this.bgimage
32969             });
32970         }
32971         
32972         if(this.videourl.length){
32973             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32974             // youtube support only?
32975             cfg.cn.push({
32976                 tag: 'iframe',
32977                 cls: 'masonry-brick-image-view',
32978                 src: vurl,
32979                 frameborder : 0,
32980                 allowfullscreen : true
32981             });
32982         }
32983         
32984         return cfg;
32985         
32986     },
32987     
32988     getSplitAutoCreate : function()
32989     {
32990         var cls = 'masonry-brick masonry-brick-split';
32991         
32992         if(this.href.length){
32993             cls += ' masonry-brick-link';
32994         }
32995         
32996         if(this.bgimage.length){
32997             cls += ' masonry-brick-image';
32998         }
32999         
33000         if(this.size){
33001             cls += ' masonry-' + this.size + '-brick';
33002         }
33003         
33004         switch (this.placetitle) {
33005             case 'center' :
33006                 cls += ' masonry-center-title';
33007                 break;
33008             case 'bottom' :
33009                 cls += ' masonry-bottom-title';
33010                 break;
33011             default:
33012                 if(!this.bgimage.length){
33013                     cls += ' masonry-center-title';
33014                 }
33015
33016                 if(this.bgimage.length){
33017                     cls += ' masonry-bottom-title';
33018                 }
33019                 break;
33020         }
33021         
33022         if(this.cls){
33023             cls += ' ' + this.cls;
33024         }
33025         
33026         var cfg = {
33027             tag: (this.href.length) ? 'a' : 'div',
33028             cls: cls,
33029             cn: [
33030                 {
33031                     tag: 'div',
33032                     cls: 'masonry-brick-split-head',
33033                     cn: [
33034                         {
33035                             tag: 'div',
33036                             cls: 'masonry-brick-paragraph',
33037                             cn: []
33038                         }
33039                     ]
33040                 },
33041                 {
33042                     tag: 'div',
33043                     cls: 'masonry-brick-split-body',
33044                     cn: []
33045                 }
33046             ]
33047         };
33048         
33049         if(this.href.length){
33050             cfg.href = this.href;
33051         }
33052         
33053         if(this.title.length){
33054             cfg.cn[0].cn[0].cn.push({
33055                 tag: 'h4',
33056                 cls: 'masonry-brick-title',
33057                 html: this.title
33058             });
33059         }
33060         
33061         if(this.html.length){
33062             cfg.cn[1].cn.push({
33063                 tag: 'p',
33064                 cls: 'masonry-brick-text',
33065                 html: this.html
33066             });
33067         }
33068
33069         if(this.bgimage.length){
33070             cfg.cn[0].cn.push({
33071                 tag: 'img',
33072                 cls: 'masonry-brick-image-view',
33073                 src: this.bgimage
33074             });
33075         }
33076         
33077         if(this.videourl.length){
33078             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33079             // youtube support only?
33080             cfg.cn[0].cn.cn.push({
33081                 tag: 'iframe',
33082                 cls: 'masonry-brick-image-view',
33083                 src: vurl,
33084                 frameborder : 0,
33085                 allowfullscreen : true
33086             });
33087         }
33088         
33089         return cfg;
33090     },
33091     
33092     initEvents: function() 
33093     {
33094         switch (this.size) {
33095             case 'xs' :
33096                 this.x = 1;
33097                 this.y = 1;
33098                 break;
33099             case 'sm' :
33100                 this.x = 2;
33101                 this.y = 2;
33102                 break;
33103             case 'md' :
33104             case 'md-left' :
33105             case 'md-right' :
33106                 this.x = 3;
33107                 this.y = 3;
33108                 break;
33109             case 'tall' :
33110                 this.x = 2;
33111                 this.y = 3;
33112                 break;
33113             case 'wide' :
33114                 this.x = 3;
33115                 this.y = 2;
33116                 break;
33117             case 'wide-thin' :
33118                 this.x = 3;
33119                 this.y = 1;
33120                 break;
33121                         
33122             default :
33123                 break;
33124         }
33125         
33126         if(Roo.isTouch){
33127             this.el.on('touchstart', this.onTouchStart, this);
33128             this.el.on('touchmove', this.onTouchMove, this);
33129             this.el.on('touchend', this.onTouchEnd, this);
33130             this.el.on('contextmenu', this.onContextMenu, this);
33131         } else {
33132             this.el.on('mouseenter'  ,this.enter, this);
33133             this.el.on('mouseleave', this.leave, this);
33134             this.el.on('click', this.onClick, this);
33135         }
33136         
33137         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33138             this.parent().bricks.push(this);   
33139         }
33140         
33141     },
33142     
33143     onClick: function(e, el)
33144     {
33145         var time = this.endTimer - this.startTimer;
33146         // Roo.log(e.preventDefault());
33147         if(Roo.isTouch){
33148             if(time > 1000){
33149                 e.preventDefault();
33150                 return;
33151             }
33152         }
33153         
33154         if(!this.preventDefault){
33155             return;
33156         }
33157         
33158         e.preventDefault();
33159         
33160         if (this.activeClass != '') {
33161             this.selectBrick();
33162         }
33163         
33164         this.fireEvent('click', this, e);
33165     },
33166     
33167     enter: function(e, el)
33168     {
33169         e.preventDefault();
33170         
33171         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33172             return;
33173         }
33174         
33175         if(this.bgimage.length && this.html.length){
33176             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33177         }
33178     },
33179     
33180     leave: function(e, el)
33181     {
33182         e.preventDefault();
33183         
33184         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33185             return;
33186         }
33187         
33188         if(this.bgimage.length && this.html.length){
33189             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33190         }
33191     },
33192     
33193     onTouchStart: function(e, el)
33194     {
33195 //        e.preventDefault();
33196         
33197         this.touchmoved = false;
33198         
33199         if(!this.isFitContainer){
33200             return;
33201         }
33202         
33203         if(!this.bgimage.length || !this.html.length){
33204             return;
33205         }
33206         
33207         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33208         
33209         this.timer = new Date().getTime();
33210         
33211     },
33212     
33213     onTouchMove: function(e, el)
33214     {
33215         this.touchmoved = true;
33216     },
33217     
33218     onContextMenu : function(e,el)
33219     {
33220         e.preventDefault();
33221         e.stopPropagation();
33222         return false;
33223     },
33224     
33225     onTouchEnd: function(e, el)
33226     {
33227 //        e.preventDefault();
33228         
33229         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33230         
33231             this.leave(e,el);
33232             
33233             return;
33234         }
33235         
33236         if(!this.bgimage.length || !this.html.length){
33237             
33238             if(this.href.length){
33239                 window.location.href = this.href;
33240             }
33241             
33242             return;
33243         }
33244         
33245         if(!this.isFitContainer){
33246             return;
33247         }
33248         
33249         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33250         
33251         window.location.href = this.href;
33252     },
33253     
33254     //selection on single brick only
33255     selectBrick : function() {
33256         
33257         if (!this.parentId) {
33258             return;
33259         }
33260         
33261         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33262         var index = m.selectedBrick.indexOf(this.id);
33263         
33264         if ( index > -1) {
33265             m.selectedBrick.splice(index,1);
33266             this.el.removeClass(this.activeClass);
33267             return;
33268         }
33269         
33270         for(var i = 0; i < m.selectedBrick.length; i++) {
33271             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33272             b.el.removeClass(b.activeClass);
33273         }
33274         
33275         m.selectedBrick = [];
33276         
33277         m.selectedBrick.push(this.id);
33278         this.el.addClass(this.activeClass);
33279         return;
33280     },
33281     
33282     isSelected : function(){
33283         return this.el.hasClass(this.activeClass);
33284         
33285     }
33286 });
33287
33288 Roo.apply(Roo.bootstrap.MasonryBrick, {
33289     
33290     //groups: {},
33291     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33292      /**
33293     * register a Masonry Brick
33294     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33295     */
33296     
33297     register : function(brick)
33298     {
33299         //this.groups[brick.id] = brick;
33300         this.groups.add(brick.id, brick);
33301     },
33302     /**
33303     * fetch a  masonry brick based on the masonry brick ID
33304     * @param {string} the masonry brick to add
33305     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33306     */
33307     
33308     get: function(brick_id) 
33309     {
33310         // if (typeof(this.groups[brick_id]) == 'undefined') {
33311         //     return false;
33312         // }
33313         // return this.groups[brick_id] ;
33314         
33315         if(this.groups.key(brick_id)) {
33316             return this.groups.key(brick_id);
33317         }
33318         
33319         return false;
33320     }
33321     
33322     
33323     
33324 });
33325
33326  /*
33327  * - LGPL
33328  *
33329  * element
33330  * 
33331  */
33332
33333 /**
33334  * @class Roo.bootstrap.Brick
33335  * @extends Roo.bootstrap.Component
33336  * Bootstrap Brick class
33337  * 
33338  * @constructor
33339  * Create a new Brick
33340  * @param {Object} config The config object
33341  */
33342
33343 Roo.bootstrap.Brick = function(config){
33344     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33345     
33346     this.addEvents({
33347         // raw events
33348         /**
33349          * @event click
33350          * When a Brick is click
33351          * @param {Roo.bootstrap.Brick} this
33352          * @param {Roo.EventObject} e
33353          */
33354         "click" : true
33355     });
33356 };
33357
33358 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33359     
33360     /**
33361      * @cfg {String} title
33362      */   
33363     title : '',
33364     /**
33365      * @cfg {String} html
33366      */   
33367     html : '',
33368     /**
33369      * @cfg {String} bgimage
33370      */   
33371     bgimage : '',
33372     /**
33373      * @cfg {String} cls
33374      */   
33375     cls : '',
33376     /**
33377      * @cfg {String} href
33378      */   
33379     href : '',
33380     /**
33381      * @cfg {String} video
33382      */   
33383     video : '',
33384     /**
33385      * @cfg {Boolean} square
33386      */   
33387     square : true,
33388     
33389     getAutoCreate : function()
33390     {
33391         var cls = 'roo-brick';
33392         
33393         if(this.href.length){
33394             cls += ' roo-brick-link';
33395         }
33396         
33397         if(this.bgimage.length){
33398             cls += ' roo-brick-image';
33399         }
33400         
33401         if(!this.html.length && !this.bgimage.length){
33402             cls += ' roo-brick-center-title';
33403         }
33404         
33405         if(!this.html.length && this.bgimage.length){
33406             cls += ' roo-brick-bottom-title';
33407         }
33408         
33409         if(this.cls){
33410             cls += ' ' + this.cls;
33411         }
33412         
33413         var cfg = {
33414             tag: (this.href.length) ? 'a' : 'div',
33415             cls: cls,
33416             cn: [
33417                 {
33418                     tag: 'div',
33419                     cls: 'roo-brick-paragraph',
33420                     cn: []
33421                 }
33422             ]
33423         };
33424         
33425         if(this.href.length){
33426             cfg.href = this.href;
33427         }
33428         
33429         var cn = cfg.cn[0].cn;
33430         
33431         if(this.title.length){
33432             cn.push({
33433                 tag: 'h4',
33434                 cls: 'roo-brick-title',
33435                 html: this.title
33436             });
33437         }
33438         
33439         if(this.html.length){
33440             cn.push({
33441                 tag: 'p',
33442                 cls: 'roo-brick-text',
33443                 html: this.html
33444             });
33445         } else {
33446             cn.cls += ' hide';
33447         }
33448         
33449         if(this.bgimage.length){
33450             cfg.cn.push({
33451                 tag: 'img',
33452                 cls: 'roo-brick-image-view',
33453                 src: this.bgimage
33454             });
33455         }
33456         
33457         return cfg;
33458     },
33459     
33460     initEvents: function() 
33461     {
33462         if(this.title.length || this.html.length){
33463             this.el.on('mouseenter'  ,this.enter, this);
33464             this.el.on('mouseleave', this.leave, this);
33465         }
33466         
33467         Roo.EventManager.onWindowResize(this.resize, this); 
33468         
33469         if(this.bgimage.length){
33470             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33471             this.imageEl.on('load', this.onImageLoad, this);
33472             return;
33473         }
33474         
33475         this.resize();
33476     },
33477     
33478     onImageLoad : function()
33479     {
33480         this.resize();
33481     },
33482     
33483     resize : function()
33484     {
33485         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33486         
33487         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33488         
33489         if(this.bgimage.length){
33490             var image = this.el.select('.roo-brick-image-view', true).first();
33491             
33492             image.setWidth(paragraph.getWidth());
33493             
33494             if(this.square){
33495                 image.setHeight(paragraph.getWidth());
33496             }
33497             
33498             this.el.setHeight(image.getHeight());
33499             paragraph.setHeight(image.getHeight());
33500             
33501         }
33502         
33503     },
33504     
33505     enter: function(e, el)
33506     {
33507         e.preventDefault();
33508         
33509         if(this.bgimage.length){
33510             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33511             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33512         }
33513     },
33514     
33515     leave: function(e, el)
33516     {
33517         e.preventDefault();
33518         
33519         if(this.bgimage.length){
33520             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33521             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33522         }
33523     }
33524     
33525 });
33526
33527  
33528
33529  /*
33530  * - LGPL
33531  *
33532  * Number field 
33533  */
33534
33535 /**
33536  * @class Roo.bootstrap.NumberField
33537  * @extends Roo.bootstrap.Input
33538  * Bootstrap NumberField class
33539  * 
33540  * 
33541  * 
33542  * 
33543  * @constructor
33544  * Create a new NumberField
33545  * @param {Object} config The config object
33546  */
33547
33548 Roo.bootstrap.NumberField = function(config){
33549     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33550 };
33551
33552 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33553     
33554     /**
33555      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33556      */
33557     allowDecimals : true,
33558     /**
33559      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33560      */
33561     decimalSeparator : ".",
33562     /**
33563      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33564      */
33565     decimalPrecision : 2,
33566     /**
33567      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33568      */
33569     allowNegative : true,
33570     
33571     /**
33572      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33573      */
33574     allowZero: true,
33575     /**
33576      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33577      */
33578     minValue : Number.NEGATIVE_INFINITY,
33579     /**
33580      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33581      */
33582     maxValue : Number.MAX_VALUE,
33583     /**
33584      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33585      */
33586     minText : "The minimum value for this field is {0}",
33587     /**
33588      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33589      */
33590     maxText : "The maximum value for this field is {0}",
33591     /**
33592      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33593      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33594      */
33595     nanText : "{0} is not a valid number",
33596     /**
33597      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33598      */
33599     thousandsDelimiter : false,
33600     /**
33601      * @cfg {String} valueAlign alignment of value
33602      */
33603     valueAlign : "left",
33604
33605     getAutoCreate : function()
33606     {
33607         var hiddenInput = {
33608             tag: 'input',
33609             type: 'hidden',
33610             id: Roo.id(),
33611             cls: 'hidden-number-input'
33612         };
33613         
33614         if (this.name) {
33615             hiddenInput.name = this.name;
33616         }
33617         
33618         this.name = '';
33619         
33620         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33621         
33622         this.name = hiddenInput.name;
33623         
33624         if(cfg.cn.length > 0) {
33625             cfg.cn.push(hiddenInput);
33626         }
33627         
33628         return cfg;
33629     },
33630
33631     // private
33632     initEvents : function()
33633     {   
33634         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33635         
33636         var allowed = "0123456789";
33637         
33638         if(this.allowDecimals){
33639             allowed += this.decimalSeparator;
33640         }
33641         
33642         if(this.allowNegative){
33643             allowed += "-";
33644         }
33645         
33646         if(this.thousandsDelimiter) {
33647             allowed += ",";
33648         }
33649         
33650         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33651         
33652         var keyPress = function(e){
33653             
33654             var k = e.getKey();
33655             
33656             var c = e.getCharCode();
33657             
33658             if(
33659                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33660                     allowed.indexOf(String.fromCharCode(c)) === -1
33661             ){
33662                 e.stopEvent();
33663                 return;
33664             }
33665             
33666             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33667                 return;
33668             }
33669             
33670             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33671                 e.stopEvent();
33672             }
33673         };
33674         
33675         this.el.on("keypress", keyPress, this);
33676     },
33677     
33678     validateValue : function(value)
33679     {
33680         
33681         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33682             return false;
33683         }
33684         
33685         var num = this.parseValue(value);
33686         
33687         if(isNaN(num)){
33688             this.markInvalid(String.format(this.nanText, value));
33689             return false;
33690         }
33691         
33692         if(num < this.minValue){
33693             this.markInvalid(String.format(this.minText, this.minValue));
33694             return false;
33695         }
33696         
33697         if(num > this.maxValue){
33698             this.markInvalid(String.format(this.maxText, this.maxValue));
33699             return false;
33700         }
33701         
33702         return true;
33703     },
33704
33705     getValue : function()
33706     {
33707         var v = this.hiddenEl().getValue();
33708         
33709         return this.fixPrecision(this.parseValue(v));
33710     },
33711
33712     parseValue : function(value)
33713     {
33714         if(this.thousandsDelimiter) {
33715             value += "";
33716             r = new RegExp(",", "g");
33717             value = value.replace(r, "");
33718         }
33719         
33720         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33721         return isNaN(value) ? '' : value;
33722     },
33723
33724     fixPrecision : function(value)
33725     {
33726         if(this.thousandsDelimiter) {
33727             value += "";
33728             r = new RegExp(",", "g");
33729             value = value.replace(r, "");
33730         }
33731         
33732         var nan = isNaN(value);
33733         
33734         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33735             return nan ? '' : value;
33736         }
33737         return parseFloat(value).toFixed(this.decimalPrecision);
33738     },
33739
33740     setValue : function(v)
33741     {
33742         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33743         
33744         this.value = v;
33745         
33746         if(this.rendered){
33747             
33748             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33749             
33750             this.inputEl().dom.value = (v == '') ? '' :
33751                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33752             
33753             if(!this.allowZero && v === '0') {
33754                 this.hiddenEl().dom.value = '';
33755                 this.inputEl().dom.value = '';
33756             }
33757             
33758             this.validate();
33759         }
33760     },
33761
33762     decimalPrecisionFcn : function(v)
33763     {
33764         return Math.floor(v);
33765     },
33766
33767     beforeBlur : function()
33768     {
33769         var v = this.parseValue(this.getRawValue());
33770         
33771         if(v || v === 0 || v === ''){
33772             this.setValue(v);
33773         }
33774     },
33775     
33776     hiddenEl : function()
33777     {
33778         return this.el.select('input.hidden-number-input',true).first();
33779     }
33780     
33781 });
33782
33783  
33784
33785 /*
33786 * Licence: LGPL
33787 */
33788
33789 /**
33790  * @class Roo.bootstrap.DocumentSlider
33791  * @extends Roo.bootstrap.Component
33792  * Bootstrap DocumentSlider class
33793  * 
33794  * @constructor
33795  * Create a new DocumentViewer
33796  * @param {Object} config The config object
33797  */
33798
33799 Roo.bootstrap.DocumentSlider = function(config){
33800     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33801     
33802     this.files = [];
33803     
33804     this.addEvents({
33805         /**
33806          * @event initial
33807          * Fire after initEvent
33808          * @param {Roo.bootstrap.DocumentSlider} this
33809          */
33810         "initial" : true,
33811         /**
33812          * @event update
33813          * Fire after update
33814          * @param {Roo.bootstrap.DocumentSlider} this
33815          */
33816         "update" : true,
33817         /**
33818          * @event click
33819          * Fire after click
33820          * @param {Roo.bootstrap.DocumentSlider} this
33821          */
33822         "click" : true
33823     });
33824 };
33825
33826 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33827     
33828     files : false,
33829     
33830     indicator : 0,
33831     
33832     getAutoCreate : function()
33833     {
33834         var cfg = {
33835             tag : 'div',
33836             cls : 'roo-document-slider',
33837             cn : [
33838                 {
33839                     tag : 'div',
33840                     cls : 'roo-document-slider-header',
33841                     cn : [
33842                         {
33843                             tag : 'div',
33844                             cls : 'roo-document-slider-header-title'
33845                         }
33846                     ]
33847                 },
33848                 {
33849                     tag : 'div',
33850                     cls : 'roo-document-slider-body',
33851                     cn : [
33852                         {
33853                             tag : 'div',
33854                             cls : 'roo-document-slider-prev',
33855                             cn : [
33856                                 {
33857                                     tag : 'i',
33858                                     cls : 'fa fa-chevron-left'
33859                                 }
33860                             ]
33861                         },
33862                         {
33863                             tag : 'div',
33864                             cls : 'roo-document-slider-thumb',
33865                             cn : [
33866                                 {
33867                                     tag : 'img',
33868                                     cls : 'roo-document-slider-image'
33869                                 }
33870                             ]
33871                         },
33872                         {
33873                             tag : 'div',
33874                             cls : 'roo-document-slider-next',
33875                             cn : [
33876                                 {
33877                                     tag : 'i',
33878                                     cls : 'fa fa-chevron-right'
33879                                 }
33880                             ]
33881                         }
33882                     ]
33883                 }
33884             ]
33885         };
33886         
33887         return cfg;
33888     },
33889     
33890     initEvents : function()
33891     {
33892         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33893         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33894         
33895         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33896         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33897         
33898         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33899         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33900         
33901         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33902         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33903         
33904         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33905         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33906         
33907         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33908         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33909         
33910         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33911         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33912         
33913         this.thumbEl.on('click', this.onClick, this);
33914         
33915         this.prevIndicator.on('click', this.prev, this);
33916         
33917         this.nextIndicator.on('click', this.next, this);
33918         
33919     },
33920     
33921     initial : function()
33922     {
33923         if(this.files.length){
33924             this.indicator = 1;
33925             this.update()
33926         }
33927         
33928         this.fireEvent('initial', this);
33929     },
33930     
33931     update : function()
33932     {
33933         this.imageEl.attr('src', this.files[this.indicator - 1]);
33934         
33935         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33936         
33937         this.prevIndicator.show();
33938         
33939         if(this.indicator == 1){
33940             this.prevIndicator.hide();
33941         }
33942         
33943         this.nextIndicator.show();
33944         
33945         if(this.indicator == this.files.length){
33946             this.nextIndicator.hide();
33947         }
33948         
33949         this.thumbEl.scrollTo('top');
33950         
33951         this.fireEvent('update', this);
33952     },
33953     
33954     onClick : function(e)
33955     {
33956         e.preventDefault();
33957         
33958         this.fireEvent('click', this);
33959     },
33960     
33961     prev : function(e)
33962     {
33963         e.preventDefault();
33964         
33965         this.indicator = Math.max(1, this.indicator - 1);
33966         
33967         this.update();
33968     },
33969     
33970     next : function(e)
33971     {
33972         e.preventDefault();
33973         
33974         this.indicator = Math.min(this.files.length, this.indicator + 1);
33975         
33976         this.update();
33977     }
33978 });
33979 /*
33980  * - LGPL
33981  *
33982  * RadioSet
33983  *
33984  *
33985  */
33986
33987 /**
33988  * @class Roo.bootstrap.RadioSet
33989  * @extends Roo.bootstrap.Input
33990  * Bootstrap RadioSet class
33991  * @cfg {String} indicatorpos (left|right) default left
33992  * @cfg {Boolean} inline (true|false) inline the element (default true)
33993  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33994  * @constructor
33995  * Create a new RadioSet
33996  * @param {Object} config The config object
33997  */
33998
33999 Roo.bootstrap.RadioSet = function(config){
34000     
34001     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34002     
34003     this.radioes = [];
34004     
34005     Roo.bootstrap.RadioSet.register(this);
34006     
34007     this.addEvents({
34008         /**
34009         * @event check
34010         * Fires when the element is checked or unchecked.
34011         * @param {Roo.bootstrap.RadioSet} this This radio
34012         * @param {Roo.bootstrap.Radio} item The checked item
34013         */
34014        check : true,
34015        /**
34016         * @event click
34017         * Fires when the element is click.
34018         * @param {Roo.bootstrap.RadioSet} this This radio set
34019         * @param {Roo.bootstrap.Radio} item The checked item
34020         * @param {Roo.EventObject} e The event object
34021         */
34022        click : true
34023     });
34024     
34025 };
34026
34027 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34028
34029     radioes : false,
34030     
34031     inline : true,
34032     
34033     weight : '',
34034     
34035     indicatorpos : 'left',
34036     
34037     getAutoCreate : function()
34038     {
34039         var label = {
34040             tag : 'label',
34041             cls : 'roo-radio-set-label',
34042             cn : [
34043                 {
34044                     tag : 'span',
34045                     html : this.fieldLabel
34046                 }
34047             ]
34048         };
34049         
34050         if(this.indicatorpos == 'left'){
34051             label.cn.unshift({
34052                 tag : 'i',
34053                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34054                 tooltip : 'This field is required'
34055             });
34056         } else {
34057             label.cn.push({
34058                 tag : 'i',
34059                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34060                 tooltip : 'This field is required'
34061             });
34062         }
34063         
34064         var items = {
34065             tag : 'div',
34066             cls : 'roo-radio-set-items'
34067         };
34068         
34069         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34070         
34071         if (align === 'left' && this.fieldLabel.length) {
34072             
34073             items = {
34074                 cls : "roo-radio-set-right", 
34075                 cn: [
34076                     items
34077                 ]
34078             };
34079             
34080             if(this.labelWidth > 12){
34081                 label.style = "width: " + this.labelWidth + 'px';
34082             }
34083             
34084             if(this.labelWidth < 13 && this.labelmd == 0){
34085                 this.labelmd = this.labelWidth;
34086             }
34087             
34088             if(this.labellg > 0){
34089                 label.cls += ' col-lg-' + this.labellg;
34090                 items.cls += ' col-lg-' + (12 - this.labellg);
34091             }
34092             
34093             if(this.labelmd > 0){
34094                 label.cls += ' col-md-' + this.labelmd;
34095                 items.cls += ' col-md-' + (12 - this.labelmd);
34096             }
34097             
34098             if(this.labelsm > 0){
34099                 label.cls += ' col-sm-' + this.labelsm;
34100                 items.cls += ' col-sm-' + (12 - this.labelsm);
34101             }
34102             
34103             if(this.labelxs > 0){
34104                 label.cls += ' col-xs-' + this.labelxs;
34105                 items.cls += ' col-xs-' + (12 - this.labelxs);
34106             }
34107         }
34108         
34109         var cfg = {
34110             tag : 'div',
34111             cls : 'roo-radio-set',
34112             cn : [
34113                 {
34114                     tag : 'input',
34115                     cls : 'roo-radio-set-input',
34116                     type : 'hidden',
34117                     name : this.name,
34118                     value : this.value ? this.value :  ''
34119                 },
34120                 label,
34121                 items
34122             ]
34123         };
34124         
34125         if(this.weight.length){
34126             cfg.cls += ' roo-radio-' + this.weight;
34127         }
34128         
34129         if(this.inline) {
34130             cfg.cls += ' roo-radio-set-inline';
34131         }
34132         
34133         var settings=this;
34134         ['xs','sm','md','lg'].map(function(size){
34135             if (settings[size]) {
34136                 cfg.cls += ' col-' + size + '-' + settings[size];
34137             }
34138         });
34139         
34140         return cfg;
34141         
34142     },
34143
34144     initEvents : function()
34145     {
34146         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34147         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34148         
34149         if(!this.fieldLabel.length){
34150             this.labelEl.hide();
34151         }
34152         
34153         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34154         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34155         
34156         this.indicator = this.indicatorEl();
34157         
34158         if(this.indicator){
34159             this.indicator.addClass('invisible');
34160         }
34161         
34162         this.originalValue = this.getValue();
34163         
34164     },
34165     
34166     inputEl: function ()
34167     {
34168         return this.el.select('.roo-radio-set-input', true).first();
34169     },
34170     
34171     getChildContainer : function()
34172     {
34173         return this.itemsEl;
34174     },
34175     
34176     register : function(item)
34177     {
34178         this.radioes.push(item);
34179         
34180     },
34181     
34182     validate : function()
34183     {   
34184         if(this.getVisibilityEl().hasClass('hidden')){
34185             return true;
34186         }
34187         
34188         var valid = false;
34189         
34190         Roo.each(this.radioes, function(i){
34191             if(!i.checked){
34192                 return;
34193             }
34194             
34195             valid = true;
34196             return false;
34197         });
34198         
34199         if(this.allowBlank) {
34200             return true;
34201         }
34202         
34203         if(this.disabled || valid){
34204             this.markValid();
34205             return true;
34206         }
34207         
34208         this.markInvalid();
34209         return false;
34210         
34211     },
34212     
34213     markValid : function()
34214     {
34215         if(this.labelEl.isVisible(true)){
34216             this.indicatorEl().removeClass('visible');
34217             this.indicatorEl().addClass('invisible');
34218         }
34219         
34220         this.el.removeClass([this.invalidClass, this.validClass]);
34221         this.el.addClass(this.validClass);
34222         
34223         this.fireEvent('valid', this);
34224     },
34225     
34226     markInvalid : function(msg)
34227     {
34228         if(this.allowBlank || this.disabled){
34229             return;
34230         }
34231         
34232         if(this.labelEl.isVisible(true)){
34233             this.indicatorEl().removeClass('invisible');
34234             this.indicatorEl().addClass('visible');
34235         }
34236         
34237         this.el.removeClass([this.invalidClass, this.validClass]);
34238         this.el.addClass(this.invalidClass);
34239         
34240         this.fireEvent('invalid', this, msg);
34241         
34242     },
34243     
34244     setValue : function(v, suppressEvent)
34245     {   
34246         if(this.value === v){
34247             return;
34248         }
34249         
34250         this.value = v;
34251         
34252         if(this.rendered){
34253             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34254         }
34255         
34256         Roo.each(this.radioes, function(i){
34257             i.checked = false;
34258             i.el.removeClass('checked');
34259         });
34260         
34261         Roo.each(this.radioes, function(i){
34262             
34263             if(i.value === v || i.value.toString() === v.toString()){
34264                 i.checked = true;
34265                 i.el.addClass('checked');
34266                 
34267                 if(suppressEvent !== true){
34268                     this.fireEvent('check', this, i);
34269                 }
34270                 
34271                 return false;
34272             }
34273             
34274         }, this);
34275         
34276         this.validate();
34277     },
34278     
34279     clearInvalid : function(){
34280         
34281         if(!this.el || this.preventMark){
34282             return;
34283         }
34284         
34285         this.el.removeClass([this.invalidClass]);
34286         
34287         this.fireEvent('valid', this);
34288     }
34289     
34290 });
34291
34292 Roo.apply(Roo.bootstrap.RadioSet, {
34293     
34294     groups: {},
34295     
34296     register : function(set)
34297     {
34298         this.groups[set.name] = set;
34299     },
34300     
34301     get: function(name) 
34302     {
34303         if (typeof(this.groups[name]) == 'undefined') {
34304             return false;
34305         }
34306         
34307         return this.groups[name] ;
34308     }
34309     
34310 });
34311 /*
34312  * Based on:
34313  * Ext JS Library 1.1.1
34314  * Copyright(c) 2006-2007, Ext JS, LLC.
34315  *
34316  * Originally Released Under LGPL - original licence link has changed is not relivant.
34317  *
34318  * Fork - LGPL
34319  * <script type="text/javascript">
34320  */
34321
34322
34323 /**
34324  * @class Roo.bootstrap.SplitBar
34325  * @extends Roo.util.Observable
34326  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34327  * <br><br>
34328  * Usage:
34329  * <pre><code>
34330 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34331                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34332 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34333 split.minSize = 100;
34334 split.maxSize = 600;
34335 split.animate = true;
34336 split.on('moved', splitterMoved);
34337 </code></pre>
34338  * @constructor
34339  * Create a new SplitBar
34340  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34341  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34342  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34343  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34344                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34345                         position of the SplitBar).
34346  */
34347 Roo.bootstrap.SplitBar = function(cfg){
34348     
34349     /** @private */
34350     
34351     //{
34352     //  dragElement : elm
34353     //  resizingElement: el,
34354         // optional..
34355     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34356     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34357         // existingProxy ???
34358     //}
34359     
34360     this.el = Roo.get(cfg.dragElement, true);
34361     this.el.dom.unselectable = "on";
34362     /** @private */
34363     this.resizingEl = Roo.get(cfg.resizingElement, true);
34364
34365     /**
34366      * @private
34367      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34368      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34369      * @type Number
34370      */
34371     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34372     
34373     /**
34374      * The minimum size of the resizing element. (Defaults to 0)
34375      * @type Number
34376      */
34377     this.minSize = 0;
34378     
34379     /**
34380      * The maximum size of the resizing element. (Defaults to 2000)
34381      * @type Number
34382      */
34383     this.maxSize = 2000;
34384     
34385     /**
34386      * Whether to animate the transition to the new size
34387      * @type Boolean
34388      */
34389     this.animate = false;
34390     
34391     /**
34392      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34393      * @type Boolean
34394      */
34395     this.useShim = false;
34396     
34397     /** @private */
34398     this.shim = null;
34399     
34400     if(!cfg.existingProxy){
34401         /** @private */
34402         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34403     }else{
34404         this.proxy = Roo.get(cfg.existingProxy).dom;
34405     }
34406     /** @private */
34407     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34408     
34409     /** @private */
34410     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34411     
34412     /** @private */
34413     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34414     
34415     /** @private */
34416     this.dragSpecs = {};
34417     
34418     /**
34419      * @private The adapter to use to positon and resize elements
34420      */
34421     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34422     this.adapter.init(this);
34423     
34424     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34425         /** @private */
34426         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34427         this.el.addClass("roo-splitbar-h");
34428     }else{
34429         /** @private */
34430         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34431         this.el.addClass("roo-splitbar-v");
34432     }
34433     
34434     this.addEvents({
34435         /**
34436          * @event resize
34437          * Fires when the splitter is moved (alias for {@link #event-moved})
34438          * @param {Roo.bootstrap.SplitBar} this
34439          * @param {Number} newSize the new width or height
34440          */
34441         "resize" : true,
34442         /**
34443          * @event moved
34444          * Fires when the splitter is moved
34445          * @param {Roo.bootstrap.SplitBar} this
34446          * @param {Number} newSize the new width or height
34447          */
34448         "moved" : true,
34449         /**
34450          * @event beforeresize
34451          * Fires before the splitter is dragged
34452          * @param {Roo.bootstrap.SplitBar} this
34453          */
34454         "beforeresize" : true,
34455
34456         "beforeapply" : true
34457     });
34458
34459     Roo.util.Observable.call(this);
34460 };
34461
34462 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34463     onStartProxyDrag : function(x, y){
34464         this.fireEvent("beforeresize", this);
34465         if(!this.overlay){
34466             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34467             o.unselectable();
34468             o.enableDisplayMode("block");
34469             // all splitbars share the same overlay
34470             Roo.bootstrap.SplitBar.prototype.overlay = o;
34471         }
34472         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34473         this.overlay.show();
34474         Roo.get(this.proxy).setDisplayed("block");
34475         var size = this.adapter.getElementSize(this);
34476         this.activeMinSize = this.getMinimumSize();;
34477         this.activeMaxSize = this.getMaximumSize();;
34478         var c1 = size - this.activeMinSize;
34479         var c2 = Math.max(this.activeMaxSize - size, 0);
34480         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34481             this.dd.resetConstraints();
34482             this.dd.setXConstraint(
34483                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34484                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34485             );
34486             this.dd.setYConstraint(0, 0);
34487         }else{
34488             this.dd.resetConstraints();
34489             this.dd.setXConstraint(0, 0);
34490             this.dd.setYConstraint(
34491                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34492                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34493             );
34494          }
34495         this.dragSpecs.startSize = size;
34496         this.dragSpecs.startPoint = [x, y];
34497         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34498     },
34499     
34500     /** 
34501      * @private Called after the drag operation by the DDProxy
34502      */
34503     onEndProxyDrag : function(e){
34504         Roo.get(this.proxy).setDisplayed(false);
34505         var endPoint = Roo.lib.Event.getXY(e);
34506         if(this.overlay){
34507             this.overlay.hide();
34508         }
34509         var newSize;
34510         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34511             newSize = this.dragSpecs.startSize + 
34512                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34513                     endPoint[0] - this.dragSpecs.startPoint[0] :
34514                     this.dragSpecs.startPoint[0] - endPoint[0]
34515                 );
34516         }else{
34517             newSize = this.dragSpecs.startSize + 
34518                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34519                     endPoint[1] - this.dragSpecs.startPoint[1] :
34520                     this.dragSpecs.startPoint[1] - endPoint[1]
34521                 );
34522         }
34523         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34524         if(newSize != this.dragSpecs.startSize){
34525             if(this.fireEvent('beforeapply', this, newSize) !== false){
34526                 this.adapter.setElementSize(this, newSize);
34527                 this.fireEvent("moved", this, newSize);
34528                 this.fireEvent("resize", this, newSize);
34529             }
34530         }
34531     },
34532     
34533     /**
34534      * Get the adapter this SplitBar uses
34535      * @return The adapter object
34536      */
34537     getAdapter : function(){
34538         return this.adapter;
34539     },
34540     
34541     /**
34542      * Set the adapter this SplitBar uses
34543      * @param {Object} adapter A SplitBar adapter object
34544      */
34545     setAdapter : function(adapter){
34546         this.adapter = adapter;
34547         this.adapter.init(this);
34548     },
34549     
34550     /**
34551      * Gets the minimum size for the resizing element
34552      * @return {Number} The minimum size
34553      */
34554     getMinimumSize : function(){
34555         return this.minSize;
34556     },
34557     
34558     /**
34559      * Sets the minimum size for the resizing element
34560      * @param {Number} minSize The minimum size
34561      */
34562     setMinimumSize : function(minSize){
34563         this.minSize = minSize;
34564     },
34565     
34566     /**
34567      * Gets the maximum size for the resizing element
34568      * @return {Number} The maximum size
34569      */
34570     getMaximumSize : function(){
34571         return this.maxSize;
34572     },
34573     
34574     /**
34575      * Sets the maximum size for the resizing element
34576      * @param {Number} maxSize The maximum size
34577      */
34578     setMaximumSize : function(maxSize){
34579         this.maxSize = maxSize;
34580     },
34581     
34582     /**
34583      * Sets the initialize size for the resizing element
34584      * @param {Number} size The initial size
34585      */
34586     setCurrentSize : function(size){
34587         var oldAnimate = this.animate;
34588         this.animate = false;
34589         this.adapter.setElementSize(this, size);
34590         this.animate = oldAnimate;
34591     },
34592     
34593     /**
34594      * Destroy this splitbar. 
34595      * @param {Boolean} removeEl True to remove the element
34596      */
34597     destroy : function(removeEl){
34598         if(this.shim){
34599             this.shim.remove();
34600         }
34601         this.dd.unreg();
34602         this.proxy.parentNode.removeChild(this.proxy);
34603         if(removeEl){
34604             this.el.remove();
34605         }
34606     }
34607 });
34608
34609 /**
34610  * @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.
34611  */
34612 Roo.bootstrap.SplitBar.createProxy = function(dir){
34613     var proxy = new Roo.Element(document.createElement("div"));
34614     proxy.unselectable();
34615     var cls = 'roo-splitbar-proxy';
34616     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34617     document.body.appendChild(proxy.dom);
34618     return proxy.dom;
34619 };
34620
34621 /** 
34622  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34623  * Default Adapter. It assumes the splitter and resizing element are not positioned
34624  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34625  */
34626 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34627 };
34628
34629 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34630     // do nothing for now
34631     init : function(s){
34632     
34633     },
34634     /**
34635      * Called before drag operations to get the current size of the resizing element. 
34636      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34637      */
34638      getElementSize : function(s){
34639         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34640             return s.resizingEl.getWidth();
34641         }else{
34642             return s.resizingEl.getHeight();
34643         }
34644     },
34645     
34646     /**
34647      * Called after drag operations to set the size of the resizing element.
34648      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34649      * @param {Number} newSize The new size to set
34650      * @param {Function} onComplete A function to be invoked when resizing is complete
34651      */
34652     setElementSize : function(s, newSize, onComplete){
34653         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34654             if(!s.animate){
34655                 s.resizingEl.setWidth(newSize);
34656                 if(onComplete){
34657                     onComplete(s, newSize);
34658                 }
34659             }else{
34660                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34661             }
34662         }else{
34663             
34664             if(!s.animate){
34665                 s.resizingEl.setHeight(newSize);
34666                 if(onComplete){
34667                     onComplete(s, newSize);
34668                 }
34669             }else{
34670                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34671             }
34672         }
34673     }
34674 };
34675
34676 /** 
34677  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34678  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34679  * Adapter that  moves the splitter element to align with the resized sizing element. 
34680  * Used with an absolute positioned SplitBar.
34681  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34682  * document.body, make sure you assign an id to the body element.
34683  */
34684 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34685     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34686     this.container = Roo.get(container);
34687 };
34688
34689 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34690     init : function(s){
34691         this.basic.init(s);
34692     },
34693     
34694     getElementSize : function(s){
34695         return this.basic.getElementSize(s);
34696     },
34697     
34698     setElementSize : function(s, newSize, onComplete){
34699         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34700     },
34701     
34702     moveSplitter : function(s){
34703         var yes = Roo.bootstrap.SplitBar;
34704         switch(s.placement){
34705             case yes.LEFT:
34706                 s.el.setX(s.resizingEl.getRight());
34707                 break;
34708             case yes.RIGHT:
34709                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34710                 break;
34711             case yes.TOP:
34712                 s.el.setY(s.resizingEl.getBottom());
34713                 break;
34714             case yes.BOTTOM:
34715                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34716                 break;
34717         }
34718     }
34719 };
34720
34721 /**
34722  * Orientation constant - Create a vertical SplitBar
34723  * @static
34724  * @type Number
34725  */
34726 Roo.bootstrap.SplitBar.VERTICAL = 1;
34727
34728 /**
34729  * Orientation constant - Create a horizontal SplitBar
34730  * @static
34731  * @type Number
34732  */
34733 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34734
34735 /**
34736  * Placement constant - The resizing element is to the left of the splitter element
34737  * @static
34738  * @type Number
34739  */
34740 Roo.bootstrap.SplitBar.LEFT = 1;
34741
34742 /**
34743  * Placement constant - The resizing element is to the right of the splitter element
34744  * @static
34745  * @type Number
34746  */
34747 Roo.bootstrap.SplitBar.RIGHT = 2;
34748
34749 /**
34750  * Placement constant - The resizing element is positioned above the splitter element
34751  * @static
34752  * @type Number
34753  */
34754 Roo.bootstrap.SplitBar.TOP = 3;
34755
34756 /**
34757  * Placement constant - The resizing element is positioned under splitter element
34758  * @static
34759  * @type Number
34760  */
34761 Roo.bootstrap.SplitBar.BOTTOM = 4;
34762 Roo.namespace("Roo.bootstrap.layout");/*
34763  * Based on:
34764  * Ext JS Library 1.1.1
34765  * Copyright(c) 2006-2007, Ext JS, LLC.
34766  *
34767  * Originally Released Under LGPL - original licence link has changed is not relivant.
34768  *
34769  * Fork - LGPL
34770  * <script type="text/javascript">
34771  */
34772
34773 /**
34774  * @class Roo.bootstrap.layout.Manager
34775  * @extends Roo.bootstrap.Component
34776  * Base class for layout managers.
34777  */
34778 Roo.bootstrap.layout.Manager = function(config)
34779 {
34780     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34781
34782
34783
34784
34785
34786     /** false to disable window resize monitoring @type Boolean */
34787     this.monitorWindowResize = true;
34788     this.regions = {};
34789     this.addEvents({
34790         /**
34791          * @event layout
34792          * Fires when a layout is performed.
34793          * @param {Roo.LayoutManager} this
34794          */
34795         "layout" : true,
34796         /**
34797          * @event regionresized
34798          * Fires when the user resizes a region.
34799          * @param {Roo.LayoutRegion} region The resized region
34800          * @param {Number} newSize The new size (width for east/west, height for north/south)
34801          */
34802         "regionresized" : true,
34803         /**
34804          * @event regioncollapsed
34805          * Fires when a region is collapsed.
34806          * @param {Roo.LayoutRegion} region The collapsed region
34807          */
34808         "regioncollapsed" : true,
34809         /**
34810          * @event regionexpanded
34811          * Fires when a region is expanded.
34812          * @param {Roo.LayoutRegion} region The expanded region
34813          */
34814         "regionexpanded" : true
34815     });
34816     this.updating = false;
34817
34818     if (config.el) {
34819         this.el = Roo.get(config.el);
34820         this.initEvents();
34821     }
34822
34823 };
34824
34825 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34826
34827
34828     regions : null,
34829
34830     monitorWindowResize : true,
34831
34832
34833     updating : false,
34834
34835
34836     onRender : function(ct, position)
34837     {
34838         if(!this.el){
34839             this.el = Roo.get(ct);
34840             this.initEvents();
34841         }
34842         //this.fireEvent('render',this);
34843     },
34844
34845
34846     initEvents: function()
34847     {
34848
34849
34850         // ie scrollbar fix
34851         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34852             document.body.scroll = "no";
34853         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34854             this.el.position('relative');
34855         }
34856         this.id = this.el.id;
34857         this.el.addClass("roo-layout-container");
34858         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34859         if(this.el.dom != document.body ) {
34860             this.el.on('resize', this.layout,this);
34861             this.el.on('show', this.layout,this);
34862         }
34863
34864     },
34865
34866     /**
34867      * Returns true if this layout is currently being updated
34868      * @return {Boolean}
34869      */
34870     isUpdating : function(){
34871         return this.updating;
34872     },
34873
34874     /**
34875      * Suspend the LayoutManager from doing auto-layouts while
34876      * making multiple add or remove calls
34877      */
34878     beginUpdate : function(){
34879         this.updating = true;
34880     },
34881
34882     /**
34883      * Restore auto-layouts and optionally disable the manager from performing a layout
34884      * @param {Boolean} noLayout true to disable a layout update
34885      */
34886     endUpdate : function(noLayout){
34887         this.updating = false;
34888         if(!noLayout){
34889             this.layout();
34890         }
34891     },
34892
34893     layout: function(){
34894         // abstract...
34895     },
34896
34897     onRegionResized : function(region, newSize){
34898         this.fireEvent("regionresized", region, newSize);
34899         this.layout();
34900     },
34901
34902     onRegionCollapsed : function(region){
34903         this.fireEvent("regioncollapsed", region);
34904     },
34905
34906     onRegionExpanded : function(region){
34907         this.fireEvent("regionexpanded", region);
34908     },
34909
34910     /**
34911      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34912      * performs box-model adjustments.
34913      * @return {Object} The size as an object {width: (the width), height: (the height)}
34914      */
34915     getViewSize : function()
34916     {
34917         var size;
34918         if(this.el.dom != document.body){
34919             size = this.el.getSize();
34920         }else{
34921             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34922         }
34923         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34924         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34925         return size;
34926     },
34927
34928     /**
34929      * Returns the Element this layout is bound to.
34930      * @return {Roo.Element}
34931      */
34932     getEl : function(){
34933         return this.el;
34934     },
34935
34936     /**
34937      * Returns the specified region.
34938      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34939      * @return {Roo.LayoutRegion}
34940      */
34941     getRegion : function(target){
34942         return this.regions[target.toLowerCase()];
34943     },
34944
34945     onWindowResize : function(){
34946         if(this.monitorWindowResize){
34947             this.layout();
34948         }
34949     }
34950 });
34951 /*
34952  * Based on:
34953  * Ext JS Library 1.1.1
34954  * Copyright(c) 2006-2007, Ext JS, LLC.
34955  *
34956  * Originally Released Under LGPL - original licence link has changed is not relivant.
34957  *
34958  * Fork - LGPL
34959  * <script type="text/javascript">
34960  */
34961 /**
34962  * @class Roo.bootstrap.layout.Border
34963  * @extends Roo.bootstrap.layout.Manager
34964  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34965  * please see: examples/bootstrap/nested.html<br><br>
34966  
34967 <b>The container the layout is rendered into can be either the body element or any other element.
34968 If it is not the body element, the container needs to either be an absolute positioned element,
34969 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34970 the container size if it is not the body element.</b>
34971
34972 * @constructor
34973 * Create a new Border
34974 * @param {Object} config Configuration options
34975  */
34976 Roo.bootstrap.layout.Border = function(config){
34977     config = config || {};
34978     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34979     
34980     
34981     
34982     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34983         if(config[region]){
34984             config[region].region = region;
34985             this.addRegion(config[region]);
34986         }
34987     },this);
34988     
34989 };
34990
34991 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34992
34993 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34994     /**
34995      * Creates and adds a new region if it doesn't already exist.
34996      * @param {String} target The target region key (north, south, east, west or center).
34997      * @param {Object} config The regions config object
34998      * @return {BorderLayoutRegion} The new region
34999      */
35000     addRegion : function(config)
35001     {
35002         if(!this.regions[config.region]){
35003             var r = this.factory(config);
35004             this.bindRegion(r);
35005         }
35006         return this.regions[config.region];
35007     },
35008
35009     // private (kinda)
35010     bindRegion : function(r){
35011         this.regions[r.config.region] = r;
35012         
35013         r.on("visibilitychange",    this.layout, this);
35014         r.on("paneladded",          this.layout, this);
35015         r.on("panelremoved",        this.layout, this);
35016         r.on("invalidated",         this.layout, this);
35017         r.on("resized",             this.onRegionResized, this);
35018         r.on("collapsed",           this.onRegionCollapsed, this);
35019         r.on("expanded",            this.onRegionExpanded, this);
35020     },
35021
35022     /**
35023      * Performs a layout update.
35024      */
35025     layout : function()
35026     {
35027         if(this.updating) {
35028             return;
35029         }
35030         
35031         // render all the rebions if they have not been done alreayd?
35032         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35033             if(this.regions[region] && !this.regions[region].bodyEl){
35034                 this.regions[region].onRender(this.el)
35035             }
35036         },this);
35037         
35038         var size = this.getViewSize();
35039         var w = size.width;
35040         var h = size.height;
35041         var centerW = w;
35042         var centerH = h;
35043         var centerY = 0;
35044         var centerX = 0;
35045         //var x = 0, y = 0;
35046
35047         var rs = this.regions;
35048         var north = rs["north"];
35049         var south = rs["south"]; 
35050         var west = rs["west"];
35051         var east = rs["east"];
35052         var center = rs["center"];
35053         //if(this.hideOnLayout){ // not supported anymore
35054             //c.el.setStyle("display", "none");
35055         //}
35056         if(north && north.isVisible()){
35057             var b = north.getBox();
35058             var m = north.getMargins();
35059             b.width = w - (m.left+m.right);
35060             b.x = m.left;
35061             b.y = m.top;
35062             centerY = b.height + b.y + m.bottom;
35063             centerH -= centerY;
35064             north.updateBox(this.safeBox(b));
35065         }
35066         if(south && south.isVisible()){
35067             var b = south.getBox();
35068             var m = south.getMargins();
35069             b.width = w - (m.left+m.right);
35070             b.x = m.left;
35071             var totalHeight = (b.height + m.top + m.bottom);
35072             b.y = h - totalHeight + m.top;
35073             centerH -= totalHeight;
35074             south.updateBox(this.safeBox(b));
35075         }
35076         if(west && west.isVisible()){
35077             var b = west.getBox();
35078             var m = west.getMargins();
35079             b.height = centerH - (m.top+m.bottom);
35080             b.x = m.left;
35081             b.y = centerY + m.top;
35082             var totalWidth = (b.width + m.left + m.right);
35083             centerX += totalWidth;
35084             centerW -= totalWidth;
35085             west.updateBox(this.safeBox(b));
35086         }
35087         if(east && east.isVisible()){
35088             var b = east.getBox();
35089             var m = east.getMargins();
35090             b.height = centerH - (m.top+m.bottom);
35091             var totalWidth = (b.width + m.left + m.right);
35092             b.x = w - totalWidth + m.left;
35093             b.y = centerY + m.top;
35094             centerW -= totalWidth;
35095             east.updateBox(this.safeBox(b));
35096         }
35097         if(center){
35098             var m = center.getMargins();
35099             var centerBox = {
35100                 x: centerX + m.left,
35101                 y: centerY + m.top,
35102                 width: centerW - (m.left+m.right),
35103                 height: centerH - (m.top+m.bottom)
35104             };
35105             //if(this.hideOnLayout){
35106                 //center.el.setStyle("display", "block");
35107             //}
35108             center.updateBox(this.safeBox(centerBox));
35109         }
35110         this.el.repaint();
35111         this.fireEvent("layout", this);
35112     },
35113
35114     // private
35115     safeBox : function(box){
35116         box.width = Math.max(0, box.width);
35117         box.height = Math.max(0, box.height);
35118         return box;
35119     },
35120
35121     /**
35122      * Adds a ContentPanel (or subclass) to this layout.
35123      * @param {String} target The target region key (north, south, east, west or center).
35124      * @param {Roo.ContentPanel} panel The panel to add
35125      * @return {Roo.ContentPanel} The added panel
35126      */
35127     add : function(target, panel){
35128          
35129         target = target.toLowerCase();
35130         return this.regions[target].add(panel);
35131     },
35132
35133     /**
35134      * Remove a ContentPanel (or subclass) to this layout.
35135      * @param {String} target The target region key (north, south, east, west or center).
35136      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35137      * @return {Roo.ContentPanel} The removed panel
35138      */
35139     remove : function(target, panel){
35140         target = target.toLowerCase();
35141         return this.regions[target].remove(panel);
35142     },
35143
35144     /**
35145      * Searches all regions for a panel with the specified id
35146      * @param {String} panelId
35147      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35148      */
35149     findPanel : function(panelId){
35150         var rs = this.regions;
35151         for(var target in rs){
35152             if(typeof rs[target] != "function"){
35153                 var p = rs[target].getPanel(panelId);
35154                 if(p){
35155                     return p;
35156                 }
35157             }
35158         }
35159         return null;
35160     },
35161
35162     /**
35163      * Searches all regions for a panel with the specified id and activates (shows) it.
35164      * @param {String/ContentPanel} panelId The panels id or the panel itself
35165      * @return {Roo.ContentPanel} The shown panel or null
35166      */
35167     showPanel : function(panelId) {
35168       var rs = this.regions;
35169       for(var target in rs){
35170          var r = rs[target];
35171          if(typeof r != "function"){
35172             if(r.hasPanel(panelId)){
35173                return r.showPanel(panelId);
35174             }
35175          }
35176       }
35177       return null;
35178    },
35179
35180    /**
35181      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35182      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35183      */
35184    /*
35185     restoreState : function(provider){
35186         if(!provider){
35187             provider = Roo.state.Manager;
35188         }
35189         var sm = new Roo.LayoutStateManager();
35190         sm.init(this, provider);
35191     },
35192 */
35193  
35194  
35195     /**
35196      * Adds a xtype elements to the layout.
35197      * <pre><code>
35198
35199 layout.addxtype({
35200        xtype : 'ContentPanel',
35201        region: 'west',
35202        items: [ .... ]
35203    }
35204 );
35205
35206 layout.addxtype({
35207         xtype : 'NestedLayoutPanel',
35208         region: 'west',
35209         layout: {
35210            center: { },
35211            west: { }   
35212         },
35213         items : [ ... list of content panels or nested layout panels.. ]
35214    }
35215 );
35216 </code></pre>
35217      * @param {Object} cfg Xtype definition of item to add.
35218      */
35219     addxtype : function(cfg)
35220     {
35221         // basically accepts a pannel...
35222         // can accept a layout region..!?!?
35223         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35224         
35225         
35226         // theory?  children can only be panels??
35227         
35228         //if (!cfg.xtype.match(/Panel$/)) {
35229         //    return false;
35230         //}
35231         var ret = false;
35232         
35233         if (typeof(cfg.region) == 'undefined') {
35234             Roo.log("Failed to add Panel, region was not set");
35235             Roo.log(cfg);
35236             return false;
35237         }
35238         var region = cfg.region;
35239         delete cfg.region;
35240         
35241           
35242         var xitems = [];
35243         if (cfg.items) {
35244             xitems = cfg.items;
35245             delete cfg.items;
35246         }
35247         var nb = false;
35248         
35249         switch(cfg.xtype) 
35250         {
35251             case 'Content':  // ContentPanel (el, cfg)
35252             case 'Scroll':  // ContentPanel (el, cfg)
35253             case 'View': 
35254                 cfg.autoCreate = true;
35255                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35256                 //} else {
35257                 //    var el = this.el.createChild();
35258                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35259                 //}
35260                 
35261                 this.add(region, ret);
35262                 break;
35263             
35264             /*
35265             case 'TreePanel': // our new panel!
35266                 cfg.el = this.el.createChild();
35267                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35268                 this.add(region, ret);
35269                 break;
35270             */
35271             
35272             case 'Nest': 
35273                 // create a new Layout (which is  a Border Layout...
35274                 
35275                 var clayout = cfg.layout;
35276                 clayout.el  = this.el.createChild();
35277                 clayout.items   = clayout.items  || [];
35278                 
35279                 delete cfg.layout;
35280                 
35281                 // replace this exitems with the clayout ones..
35282                 xitems = clayout.items;
35283                  
35284                 // force background off if it's in center...
35285                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35286                     cfg.background = false;
35287                 }
35288                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35289                 
35290                 
35291                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35292                 //console.log('adding nested layout panel '  + cfg.toSource());
35293                 this.add(region, ret);
35294                 nb = {}; /// find first...
35295                 break;
35296             
35297             case 'Grid':
35298                 
35299                 // needs grid and region
35300                 
35301                 //var el = this.getRegion(region).el.createChild();
35302                 /*
35303                  *var el = this.el.createChild();
35304                 // create the grid first...
35305                 cfg.grid.container = el;
35306                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35307                 */
35308                 
35309                 if (region == 'center' && this.active ) {
35310                     cfg.background = false;
35311                 }
35312                 
35313                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35314                 
35315                 this.add(region, ret);
35316                 /*
35317                 if (cfg.background) {
35318                     // render grid on panel activation (if panel background)
35319                     ret.on('activate', function(gp) {
35320                         if (!gp.grid.rendered) {
35321                     //        gp.grid.render(el);
35322                         }
35323                     });
35324                 } else {
35325                   //  cfg.grid.render(el);
35326                 }
35327                 */
35328                 break;
35329            
35330            
35331             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35332                 // it was the old xcomponent building that caused this before.
35333                 // espeically if border is the top element in the tree.
35334                 ret = this;
35335                 break; 
35336                 
35337                     
35338                 
35339                 
35340                 
35341             default:
35342                 /*
35343                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35344                     
35345                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35346                     this.add(region, ret);
35347                 } else {
35348                 */
35349                     Roo.log(cfg);
35350                     throw "Can not add '" + cfg.xtype + "' to Border";
35351                     return null;
35352              
35353                                 
35354              
35355         }
35356         this.beginUpdate();
35357         // add children..
35358         var region = '';
35359         var abn = {};
35360         Roo.each(xitems, function(i)  {
35361             region = nb && i.region ? i.region : false;
35362             
35363             var add = ret.addxtype(i);
35364            
35365             if (region) {
35366                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35367                 if (!i.background) {
35368                     abn[region] = nb[region] ;
35369                 }
35370             }
35371             
35372         });
35373         this.endUpdate();
35374
35375         // make the last non-background panel active..
35376         //if (nb) { Roo.log(abn); }
35377         if (nb) {
35378             
35379             for(var r in abn) {
35380                 region = this.getRegion(r);
35381                 if (region) {
35382                     // tried using nb[r], but it does not work..
35383                      
35384                     region.showPanel(abn[r]);
35385                    
35386                 }
35387             }
35388         }
35389         return ret;
35390         
35391     },
35392     
35393     
35394 // private
35395     factory : function(cfg)
35396     {
35397         
35398         var validRegions = Roo.bootstrap.layout.Border.regions;
35399
35400         var target = cfg.region;
35401         cfg.mgr = this;
35402         
35403         var r = Roo.bootstrap.layout;
35404         Roo.log(target);
35405         switch(target){
35406             case "north":
35407                 return new r.North(cfg);
35408             case "south":
35409                 return new r.South(cfg);
35410             case "east":
35411                 return new r.East(cfg);
35412             case "west":
35413                 return new r.West(cfg);
35414             case "center":
35415                 return new r.Center(cfg);
35416         }
35417         throw 'Layout region "'+target+'" not supported.';
35418     }
35419     
35420     
35421 });
35422  /*
35423  * Based on:
35424  * Ext JS Library 1.1.1
35425  * Copyright(c) 2006-2007, Ext JS, LLC.
35426  *
35427  * Originally Released Under LGPL - original licence link has changed is not relivant.
35428  *
35429  * Fork - LGPL
35430  * <script type="text/javascript">
35431  */
35432  
35433 /**
35434  * @class Roo.bootstrap.layout.Basic
35435  * @extends Roo.util.Observable
35436  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35437  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35438  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35439  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35440  * @cfg {string}   region  the region that it inhabits..
35441  * @cfg {bool}   skipConfig skip config?
35442  * 
35443
35444  */
35445 Roo.bootstrap.layout.Basic = function(config){
35446     
35447     this.mgr = config.mgr;
35448     
35449     this.position = config.region;
35450     
35451     var skipConfig = config.skipConfig;
35452     
35453     this.events = {
35454         /**
35455          * @scope Roo.BasicLayoutRegion
35456          */
35457         
35458         /**
35459          * @event beforeremove
35460          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35461          * @param {Roo.LayoutRegion} this
35462          * @param {Roo.ContentPanel} panel The panel
35463          * @param {Object} e The cancel event object
35464          */
35465         "beforeremove" : true,
35466         /**
35467          * @event invalidated
35468          * Fires when the layout for this region is changed.
35469          * @param {Roo.LayoutRegion} this
35470          */
35471         "invalidated" : true,
35472         /**
35473          * @event visibilitychange
35474          * Fires when this region is shown or hidden 
35475          * @param {Roo.LayoutRegion} this
35476          * @param {Boolean} visibility true or false
35477          */
35478         "visibilitychange" : true,
35479         /**
35480          * @event paneladded
35481          * Fires when a panel is added. 
35482          * @param {Roo.LayoutRegion} this
35483          * @param {Roo.ContentPanel} panel The panel
35484          */
35485         "paneladded" : true,
35486         /**
35487          * @event panelremoved
35488          * Fires when a panel is removed. 
35489          * @param {Roo.LayoutRegion} this
35490          * @param {Roo.ContentPanel} panel The panel
35491          */
35492         "panelremoved" : true,
35493         /**
35494          * @event beforecollapse
35495          * Fires when this region before collapse.
35496          * @param {Roo.LayoutRegion} this
35497          */
35498         "beforecollapse" : true,
35499         /**
35500          * @event collapsed
35501          * Fires when this region is collapsed.
35502          * @param {Roo.LayoutRegion} this
35503          */
35504         "collapsed" : true,
35505         /**
35506          * @event expanded
35507          * Fires when this region is expanded.
35508          * @param {Roo.LayoutRegion} this
35509          */
35510         "expanded" : true,
35511         /**
35512          * @event slideshow
35513          * Fires when this region is slid into view.
35514          * @param {Roo.LayoutRegion} this
35515          */
35516         "slideshow" : true,
35517         /**
35518          * @event slidehide
35519          * Fires when this region slides out of view. 
35520          * @param {Roo.LayoutRegion} this
35521          */
35522         "slidehide" : true,
35523         /**
35524          * @event panelactivated
35525          * Fires when a panel is activated. 
35526          * @param {Roo.LayoutRegion} this
35527          * @param {Roo.ContentPanel} panel The activated panel
35528          */
35529         "panelactivated" : true,
35530         /**
35531          * @event resized
35532          * Fires when the user resizes this region. 
35533          * @param {Roo.LayoutRegion} this
35534          * @param {Number} newSize The new size (width for east/west, height for north/south)
35535          */
35536         "resized" : true
35537     };
35538     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35539     this.panels = new Roo.util.MixedCollection();
35540     this.panels.getKey = this.getPanelId.createDelegate(this);
35541     this.box = null;
35542     this.activePanel = null;
35543     // ensure listeners are added...
35544     
35545     if (config.listeners || config.events) {
35546         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35547             listeners : config.listeners || {},
35548             events : config.events || {}
35549         });
35550     }
35551     
35552     if(skipConfig !== true){
35553         this.applyConfig(config);
35554     }
35555 };
35556
35557 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35558 {
35559     getPanelId : function(p){
35560         return p.getId();
35561     },
35562     
35563     applyConfig : function(config){
35564         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35565         this.config = config;
35566         
35567     },
35568     
35569     /**
35570      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35571      * the width, for horizontal (north, south) the height.
35572      * @param {Number} newSize The new width or height
35573      */
35574     resizeTo : function(newSize){
35575         var el = this.el ? this.el :
35576                  (this.activePanel ? this.activePanel.getEl() : null);
35577         if(el){
35578             switch(this.position){
35579                 case "east":
35580                 case "west":
35581                     el.setWidth(newSize);
35582                     this.fireEvent("resized", this, newSize);
35583                 break;
35584                 case "north":
35585                 case "south":
35586                     el.setHeight(newSize);
35587                     this.fireEvent("resized", this, newSize);
35588                 break;                
35589             }
35590         }
35591     },
35592     
35593     getBox : function(){
35594         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35595     },
35596     
35597     getMargins : function(){
35598         return this.margins;
35599     },
35600     
35601     updateBox : function(box){
35602         this.box = box;
35603         var el = this.activePanel.getEl();
35604         el.dom.style.left = box.x + "px";
35605         el.dom.style.top = box.y + "px";
35606         this.activePanel.setSize(box.width, box.height);
35607     },
35608     
35609     /**
35610      * Returns the container element for this region.
35611      * @return {Roo.Element}
35612      */
35613     getEl : function(){
35614         return this.activePanel;
35615     },
35616     
35617     /**
35618      * Returns true if this region is currently visible.
35619      * @return {Boolean}
35620      */
35621     isVisible : function(){
35622         return this.activePanel ? true : false;
35623     },
35624     
35625     setActivePanel : function(panel){
35626         panel = this.getPanel(panel);
35627         if(this.activePanel && this.activePanel != panel){
35628             this.activePanel.setActiveState(false);
35629             this.activePanel.getEl().setLeftTop(-10000,-10000);
35630         }
35631         this.activePanel = panel;
35632         panel.setActiveState(true);
35633         if(this.box){
35634             panel.setSize(this.box.width, this.box.height);
35635         }
35636         this.fireEvent("panelactivated", this, panel);
35637         this.fireEvent("invalidated");
35638     },
35639     
35640     /**
35641      * Show the specified panel.
35642      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35643      * @return {Roo.ContentPanel} The shown panel or null
35644      */
35645     showPanel : function(panel){
35646         panel = this.getPanel(panel);
35647         if(panel){
35648             this.setActivePanel(panel);
35649         }
35650         return panel;
35651     },
35652     
35653     /**
35654      * Get the active panel for this region.
35655      * @return {Roo.ContentPanel} The active panel or null
35656      */
35657     getActivePanel : function(){
35658         return this.activePanel;
35659     },
35660     
35661     /**
35662      * Add the passed ContentPanel(s)
35663      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35664      * @return {Roo.ContentPanel} The panel added (if only one was added)
35665      */
35666     add : function(panel){
35667         if(arguments.length > 1){
35668             for(var i = 0, len = arguments.length; i < len; i++) {
35669                 this.add(arguments[i]);
35670             }
35671             return null;
35672         }
35673         if(this.hasPanel(panel)){
35674             this.showPanel(panel);
35675             return panel;
35676         }
35677         var el = panel.getEl();
35678         if(el.dom.parentNode != this.mgr.el.dom){
35679             this.mgr.el.dom.appendChild(el.dom);
35680         }
35681         if(panel.setRegion){
35682             panel.setRegion(this);
35683         }
35684         this.panels.add(panel);
35685         el.setStyle("position", "absolute");
35686         if(!panel.background){
35687             this.setActivePanel(panel);
35688             if(this.config.initialSize && this.panels.getCount()==1){
35689                 this.resizeTo(this.config.initialSize);
35690             }
35691         }
35692         this.fireEvent("paneladded", this, panel);
35693         return panel;
35694     },
35695     
35696     /**
35697      * Returns true if the panel is in this region.
35698      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35699      * @return {Boolean}
35700      */
35701     hasPanel : function(panel){
35702         if(typeof panel == "object"){ // must be panel obj
35703             panel = panel.getId();
35704         }
35705         return this.getPanel(panel) ? true : false;
35706     },
35707     
35708     /**
35709      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35710      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35711      * @param {Boolean} preservePanel Overrides the config preservePanel option
35712      * @return {Roo.ContentPanel} The panel that was removed
35713      */
35714     remove : function(panel, preservePanel){
35715         panel = this.getPanel(panel);
35716         if(!panel){
35717             return null;
35718         }
35719         var e = {};
35720         this.fireEvent("beforeremove", this, panel, e);
35721         if(e.cancel === true){
35722             return null;
35723         }
35724         var panelId = panel.getId();
35725         this.panels.removeKey(panelId);
35726         return panel;
35727     },
35728     
35729     /**
35730      * Returns the panel specified or null if it's not in this region.
35731      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35732      * @return {Roo.ContentPanel}
35733      */
35734     getPanel : function(id){
35735         if(typeof id == "object"){ // must be panel obj
35736             return id;
35737         }
35738         return this.panels.get(id);
35739     },
35740     
35741     /**
35742      * Returns this regions position (north/south/east/west/center).
35743      * @return {String} 
35744      */
35745     getPosition: function(){
35746         return this.position;    
35747     }
35748 });/*
35749  * Based on:
35750  * Ext JS Library 1.1.1
35751  * Copyright(c) 2006-2007, Ext JS, LLC.
35752  *
35753  * Originally Released Under LGPL - original licence link has changed is not relivant.
35754  *
35755  * Fork - LGPL
35756  * <script type="text/javascript">
35757  */
35758  
35759 /**
35760  * @class Roo.bootstrap.layout.Region
35761  * @extends Roo.bootstrap.layout.Basic
35762  * This class represents a region in a layout manager.
35763  
35764  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35765  * @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})
35766  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35767  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35768  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35769  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35770  * @cfg {String}    title           The title for the region (overrides panel titles)
35771  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35772  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35773  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35774  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35775  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35776  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35777  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35778  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35779  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35780  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35781
35782  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35783  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35784  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35785  * @cfg {Number}    width           For East/West panels
35786  * @cfg {Number}    height          For North/South panels
35787  * @cfg {Boolean}   split           To show the splitter
35788  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35789  * 
35790  * @cfg {string}   cls             Extra CSS classes to add to region
35791  * 
35792  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35793  * @cfg {string}   region  the region that it inhabits..
35794  *
35795
35796  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35797  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35798
35799  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35800  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35801  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35802  */
35803 Roo.bootstrap.layout.Region = function(config)
35804 {
35805     this.applyConfig(config);
35806
35807     var mgr = config.mgr;
35808     var pos = config.region;
35809     config.skipConfig = true;
35810     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35811     
35812     if (mgr.el) {
35813         this.onRender(mgr.el);   
35814     }
35815      
35816     this.visible = true;
35817     this.collapsed = false;
35818     this.unrendered_panels = [];
35819 };
35820
35821 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35822
35823     position: '', // set by wrapper (eg. north/south etc..)
35824     unrendered_panels : null,  // unrendered panels.
35825     createBody : function(){
35826         /** This region's body element 
35827         * @type Roo.Element */
35828         this.bodyEl = this.el.createChild({
35829                 tag: "div",
35830                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35831         });
35832     },
35833
35834     onRender: function(ctr, pos)
35835     {
35836         var dh = Roo.DomHelper;
35837         /** This region's container element 
35838         * @type Roo.Element */
35839         this.el = dh.append(ctr.dom, {
35840                 tag: "div",
35841                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35842             }, true);
35843         /** This region's title element 
35844         * @type Roo.Element */
35845     
35846         this.titleEl = dh.append(this.el.dom,
35847             {
35848                     tag: "div",
35849                     unselectable: "on",
35850                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35851                     children:[
35852                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35853                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35854                     ]}, true);
35855         
35856         this.titleEl.enableDisplayMode();
35857         /** This region's title text element 
35858         * @type HTMLElement */
35859         this.titleTextEl = this.titleEl.dom.firstChild;
35860         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35861         /*
35862         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35863         this.closeBtn.enableDisplayMode();
35864         this.closeBtn.on("click", this.closeClicked, this);
35865         this.closeBtn.hide();
35866     */
35867         this.createBody(this.config);
35868         if(this.config.hideWhenEmpty){
35869             this.hide();
35870             this.on("paneladded", this.validateVisibility, this);
35871             this.on("panelremoved", this.validateVisibility, this);
35872         }
35873         if(this.autoScroll){
35874             this.bodyEl.setStyle("overflow", "auto");
35875         }else{
35876             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35877         }
35878         //if(c.titlebar !== false){
35879             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35880                 this.titleEl.hide();
35881             }else{
35882                 this.titleEl.show();
35883                 if(this.config.title){
35884                     this.titleTextEl.innerHTML = this.config.title;
35885                 }
35886             }
35887         //}
35888         if(this.config.collapsed){
35889             this.collapse(true);
35890         }
35891         if(this.config.hidden){
35892             this.hide();
35893         }
35894         
35895         if (this.unrendered_panels && this.unrendered_panels.length) {
35896             for (var i =0;i< this.unrendered_panels.length; i++) {
35897                 this.add(this.unrendered_panels[i]);
35898             }
35899             this.unrendered_panels = null;
35900             
35901         }
35902         
35903     },
35904     
35905     applyConfig : function(c)
35906     {
35907         /*
35908          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35909             var dh = Roo.DomHelper;
35910             if(c.titlebar !== false){
35911                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35912                 this.collapseBtn.on("click", this.collapse, this);
35913                 this.collapseBtn.enableDisplayMode();
35914                 /*
35915                 if(c.showPin === true || this.showPin){
35916                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35917                     this.stickBtn.enableDisplayMode();
35918                     this.stickBtn.on("click", this.expand, this);
35919                     this.stickBtn.hide();
35920                 }
35921                 
35922             }
35923             */
35924             /** This region's collapsed element
35925             * @type Roo.Element */
35926             /*
35927              *
35928             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35929                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35930             ]}, true);
35931             
35932             if(c.floatable !== false){
35933                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35934                this.collapsedEl.on("click", this.collapseClick, this);
35935             }
35936
35937             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35938                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35939                    id: "message", unselectable: "on", style:{"float":"left"}});
35940                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35941              }
35942             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35943             this.expandBtn.on("click", this.expand, this);
35944             
35945         }
35946         
35947         if(this.collapseBtn){
35948             this.collapseBtn.setVisible(c.collapsible == true);
35949         }
35950         
35951         this.cmargins = c.cmargins || this.cmargins ||
35952                          (this.position == "west" || this.position == "east" ?
35953                              {top: 0, left: 2, right:2, bottom: 0} :
35954                              {top: 2, left: 0, right:0, bottom: 2});
35955         */
35956         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35957         
35958         
35959         this.bottomTabs = c.tabPosition != "top";
35960         
35961         this.autoScroll = c.autoScroll || false;
35962         
35963         
35964        
35965         
35966         this.duration = c.duration || .30;
35967         this.slideDuration = c.slideDuration || .45;
35968         this.config = c;
35969        
35970     },
35971     /**
35972      * Returns true if this region is currently visible.
35973      * @return {Boolean}
35974      */
35975     isVisible : function(){
35976         return this.visible;
35977     },
35978
35979     /**
35980      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35981      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35982      */
35983     //setCollapsedTitle : function(title){
35984     //    title = title || "&#160;";
35985      //   if(this.collapsedTitleTextEl){
35986       //      this.collapsedTitleTextEl.innerHTML = title;
35987        // }
35988     //},
35989
35990     getBox : function(){
35991         var b;
35992       //  if(!this.collapsed){
35993             b = this.el.getBox(false, true);
35994        // }else{
35995           //  b = this.collapsedEl.getBox(false, true);
35996         //}
35997         return b;
35998     },
35999
36000     getMargins : function(){
36001         return this.margins;
36002         //return this.collapsed ? this.cmargins : this.margins;
36003     },
36004 /*
36005     highlight : function(){
36006         this.el.addClass("x-layout-panel-dragover");
36007     },
36008
36009     unhighlight : function(){
36010         this.el.removeClass("x-layout-panel-dragover");
36011     },
36012 */
36013     updateBox : function(box)
36014     {
36015         if (!this.bodyEl) {
36016             return; // not rendered yet..
36017         }
36018         
36019         this.box = box;
36020         if(!this.collapsed){
36021             this.el.dom.style.left = box.x + "px";
36022             this.el.dom.style.top = box.y + "px";
36023             this.updateBody(box.width, box.height);
36024         }else{
36025             this.collapsedEl.dom.style.left = box.x + "px";
36026             this.collapsedEl.dom.style.top = box.y + "px";
36027             this.collapsedEl.setSize(box.width, box.height);
36028         }
36029         if(this.tabs){
36030             this.tabs.autoSizeTabs();
36031         }
36032     },
36033
36034     updateBody : function(w, h)
36035     {
36036         if(w !== null){
36037             this.el.setWidth(w);
36038             w -= this.el.getBorderWidth("rl");
36039             if(this.config.adjustments){
36040                 w += this.config.adjustments[0];
36041             }
36042         }
36043         if(h !== null && h > 0){
36044             this.el.setHeight(h);
36045             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36046             h -= this.el.getBorderWidth("tb");
36047             if(this.config.adjustments){
36048                 h += this.config.adjustments[1];
36049             }
36050             this.bodyEl.setHeight(h);
36051             if(this.tabs){
36052                 h = this.tabs.syncHeight(h);
36053             }
36054         }
36055         if(this.panelSize){
36056             w = w !== null ? w : this.panelSize.width;
36057             h = h !== null ? h : this.panelSize.height;
36058         }
36059         if(this.activePanel){
36060             var el = this.activePanel.getEl();
36061             w = w !== null ? w : el.getWidth();
36062             h = h !== null ? h : el.getHeight();
36063             this.panelSize = {width: w, height: h};
36064             this.activePanel.setSize(w, h);
36065         }
36066         if(Roo.isIE && this.tabs){
36067             this.tabs.el.repaint();
36068         }
36069     },
36070
36071     /**
36072      * Returns the container element for this region.
36073      * @return {Roo.Element}
36074      */
36075     getEl : function(){
36076         return this.el;
36077     },
36078
36079     /**
36080      * Hides this region.
36081      */
36082     hide : function(){
36083         //if(!this.collapsed){
36084             this.el.dom.style.left = "-2000px";
36085             this.el.hide();
36086         //}else{
36087          //   this.collapsedEl.dom.style.left = "-2000px";
36088          //   this.collapsedEl.hide();
36089        // }
36090         this.visible = false;
36091         this.fireEvent("visibilitychange", this, false);
36092     },
36093
36094     /**
36095      * Shows this region if it was previously hidden.
36096      */
36097     show : function(){
36098         //if(!this.collapsed){
36099             this.el.show();
36100         //}else{
36101         //    this.collapsedEl.show();
36102        // }
36103         this.visible = true;
36104         this.fireEvent("visibilitychange", this, true);
36105     },
36106 /*
36107     closeClicked : function(){
36108         if(this.activePanel){
36109             this.remove(this.activePanel);
36110         }
36111     },
36112
36113     collapseClick : function(e){
36114         if(this.isSlid){
36115            e.stopPropagation();
36116            this.slideIn();
36117         }else{
36118            e.stopPropagation();
36119            this.slideOut();
36120         }
36121     },
36122 */
36123     /**
36124      * Collapses this region.
36125      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36126      */
36127     /*
36128     collapse : function(skipAnim, skipCheck = false){
36129         if(this.collapsed) {
36130             return;
36131         }
36132         
36133         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36134             
36135             this.collapsed = true;
36136             if(this.split){
36137                 this.split.el.hide();
36138             }
36139             if(this.config.animate && skipAnim !== true){
36140                 this.fireEvent("invalidated", this);
36141                 this.animateCollapse();
36142             }else{
36143                 this.el.setLocation(-20000,-20000);
36144                 this.el.hide();
36145                 this.collapsedEl.show();
36146                 this.fireEvent("collapsed", this);
36147                 this.fireEvent("invalidated", this);
36148             }
36149         }
36150         
36151     },
36152 */
36153     animateCollapse : function(){
36154         // overridden
36155     },
36156
36157     /**
36158      * Expands this region if it was previously collapsed.
36159      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36160      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36161      */
36162     /*
36163     expand : function(e, skipAnim){
36164         if(e) {
36165             e.stopPropagation();
36166         }
36167         if(!this.collapsed || this.el.hasActiveFx()) {
36168             return;
36169         }
36170         if(this.isSlid){
36171             this.afterSlideIn();
36172             skipAnim = true;
36173         }
36174         this.collapsed = false;
36175         if(this.config.animate && skipAnim !== true){
36176             this.animateExpand();
36177         }else{
36178             this.el.show();
36179             if(this.split){
36180                 this.split.el.show();
36181             }
36182             this.collapsedEl.setLocation(-2000,-2000);
36183             this.collapsedEl.hide();
36184             this.fireEvent("invalidated", this);
36185             this.fireEvent("expanded", this);
36186         }
36187     },
36188 */
36189     animateExpand : function(){
36190         // overridden
36191     },
36192
36193     initTabs : function()
36194     {
36195         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36196         
36197         var ts = new Roo.bootstrap.panel.Tabs({
36198                 el: this.bodyEl.dom,
36199                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36200                 disableTooltips: this.config.disableTabTips,
36201                 toolbar : this.config.toolbar
36202             });
36203         
36204         if(this.config.hideTabs){
36205             ts.stripWrap.setDisplayed(false);
36206         }
36207         this.tabs = ts;
36208         ts.resizeTabs = this.config.resizeTabs === true;
36209         ts.minTabWidth = this.config.minTabWidth || 40;
36210         ts.maxTabWidth = this.config.maxTabWidth || 250;
36211         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36212         ts.monitorResize = false;
36213         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36214         ts.bodyEl.addClass('roo-layout-tabs-body');
36215         this.panels.each(this.initPanelAsTab, this);
36216     },
36217
36218     initPanelAsTab : function(panel){
36219         var ti = this.tabs.addTab(
36220             panel.getEl().id,
36221             panel.getTitle(),
36222             null,
36223             this.config.closeOnTab && panel.isClosable(),
36224             panel.tpl
36225         );
36226         if(panel.tabTip !== undefined){
36227             ti.setTooltip(panel.tabTip);
36228         }
36229         ti.on("activate", function(){
36230               this.setActivePanel(panel);
36231         }, this);
36232         
36233         if(this.config.closeOnTab){
36234             ti.on("beforeclose", function(t, e){
36235                 e.cancel = true;
36236                 this.remove(panel);
36237             }, this);
36238         }
36239         
36240         panel.tabItem = ti;
36241         
36242         return ti;
36243     },
36244
36245     updatePanelTitle : function(panel, title)
36246     {
36247         if(this.activePanel == panel){
36248             this.updateTitle(title);
36249         }
36250         if(this.tabs){
36251             var ti = this.tabs.getTab(panel.getEl().id);
36252             ti.setText(title);
36253             if(panel.tabTip !== undefined){
36254                 ti.setTooltip(panel.tabTip);
36255             }
36256         }
36257     },
36258
36259     updateTitle : function(title){
36260         if(this.titleTextEl && !this.config.title){
36261             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36262         }
36263     },
36264
36265     setActivePanel : function(panel)
36266     {
36267         panel = this.getPanel(panel);
36268         if(this.activePanel && this.activePanel != panel){
36269             if(this.activePanel.setActiveState(false) === false){
36270                 return;
36271             }
36272         }
36273         this.activePanel = panel;
36274         panel.setActiveState(true);
36275         if(this.panelSize){
36276             panel.setSize(this.panelSize.width, this.panelSize.height);
36277         }
36278         if(this.closeBtn){
36279             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36280         }
36281         this.updateTitle(panel.getTitle());
36282         if(this.tabs){
36283             this.fireEvent("invalidated", this);
36284         }
36285         this.fireEvent("panelactivated", this, panel);
36286     },
36287
36288     /**
36289      * Shows the specified panel.
36290      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36291      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36292      */
36293     showPanel : function(panel)
36294     {
36295         panel = this.getPanel(panel);
36296         if(panel){
36297             if(this.tabs){
36298                 var tab = this.tabs.getTab(panel.getEl().id);
36299                 if(tab.isHidden()){
36300                     this.tabs.unhideTab(tab.id);
36301                 }
36302                 tab.activate();
36303             }else{
36304                 this.setActivePanel(panel);
36305             }
36306         }
36307         return panel;
36308     },
36309
36310     /**
36311      * Get the active panel for this region.
36312      * @return {Roo.ContentPanel} The active panel or null
36313      */
36314     getActivePanel : function(){
36315         return this.activePanel;
36316     },
36317
36318     validateVisibility : function(){
36319         if(this.panels.getCount() < 1){
36320             this.updateTitle("&#160;");
36321             this.closeBtn.hide();
36322             this.hide();
36323         }else{
36324             if(!this.isVisible()){
36325                 this.show();
36326             }
36327         }
36328     },
36329
36330     /**
36331      * Adds the passed ContentPanel(s) to this region.
36332      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36333      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36334      */
36335     add : function(panel)
36336     {
36337         if(arguments.length > 1){
36338             for(var i = 0, len = arguments.length; i < len; i++) {
36339                 this.add(arguments[i]);
36340             }
36341             return null;
36342         }
36343         
36344         // if we have not been rendered yet, then we can not really do much of this..
36345         if (!this.bodyEl) {
36346             this.unrendered_panels.push(panel);
36347             return panel;
36348         }
36349         
36350         
36351         
36352         
36353         if(this.hasPanel(panel)){
36354             this.showPanel(panel);
36355             return panel;
36356         }
36357         panel.setRegion(this);
36358         this.panels.add(panel);
36359        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36360             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36361             // and hide them... ???
36362             this.bodyEl.dom.appendChild(panel.getEl().dom);
36363             if(panel.background !== true){
36364                 this.setActivePanel(panel);
36365             }
36366             this.fireEvent("paneladded", this, panel);
36367             return panel;
36368         }
36369         */
36370         if(!this.tabs){
36371             this.initTabs();
36372         }else{
36373             this.initPanelAsTab(panel);
36374         }
36375         
36376         
36377         if(panel.background !== true){
36378             this.tabs.activate(panel.getEl().id);
36379         }
36380         this.fireEvent("paneladded", this, panel);
36381         return panel;
36382     },
36383
36384     /**
36385      * Hides the tab for the specified panel.
36386      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36387      */
36388     hidePanel : function(panel){
36389         if(this.tabs && (panel = this.getPanel(panel))){
36390             this.tabs.hideTab(panel.getEl().id);
36391         }
36392     },
36393
36394     /**
36395      * Unhides the tab for a previously hidden panel.
36396      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36397      */
36398     unhidePanel : function(panel){
36399         if(this.tabs && (panel = this.getPanel(panel))){
36400             this.tabs.unhideTab(panel.getEl().id);
36401         }
36402     },
36403
36404     clearPanels : function(){
36405         while(this.panels.getCount() > 0){
36406              this.remove(this.panels.first());
36407         }
36408     },
36409
36410     /**
36411      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36412      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36413      * @param {Boolean} preservePanel Overrides the config preservePanel option
36414      * @return {Roo.ContentPanel} The panel that was removed
36415      */
36416     remove : function(panel, preservePanel)
36417     {
36418         panel = this.getPanel(panel);
36419         if(!panel){
36420             return null;
36421         }
36422         var e = {};
36423         this.fireEvent("beforeremove", this, panel, e);
36424         if(e.cancel === true){
36425             return null;
36426         }
36427         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36428         var panelId = panel.getId();
36429         this.panels.removeKey(panelId);
36430         if(preservePanel){
36431             document.body.appendChild(panel.getEl().dom);
36432         }
36433         if(this.tabs){
36434             this.tabs.removeTab(panel.getEl().id);
36435         }else if (!preservePanel){
36436             this.bodyEl.dom.removeChild(panel.getEl().dom);
36437         }
36438         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36439             var p = this.panels.first();
36440             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36441             tempEl.appendChild(p.getEl().dom);
36442             this.bodyEl.update("");
36443             this.bodyEl.dom.appendChild(p.getEl().dom);
36444             tempEl = null;
36445             this.updateTitle(p.getTitle());
36446             this.tabs = null;
36447             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36448             this.setActivePanel(p);
36449         }
36450         panel.setRegion(null);
36451         if(this.activePanel == panel){
36452             this.activePanel = null;
36453         }
36454         if(this.config.autoDestroy !== false && preservePanel !== true){
36455             try{panel.destroy();}catch(e){}
36456         }
36457         this.fireEvent("panelremoved", this, panel);
36458         return panel;
36459     },
36460
36461     /**
36462      * Returns the TabPanel component used by this region
36463      * @return {Roo.TabPanel}
36464      */
36465     getTabs : function(){
36466         return this.tabs;
36467     },
36468
36469     createTool : function(parentEl, className){
36470         var btn = Roo.DomHelper.append(parentEl, {
36471             tag: "div",
36472             cls: "x-layout-tools-button",
36473             children: [ {
36474                 tag: "div",
36475                 cls: "roo-layout-tools-button-inner " + className,
36476                 html: "&#160;"
36477             }]
36478         }, true);
36479         btn.addClassOnOver("roo-layout-tools-button-over");
36480         return btn;
36481     }
36482 });/*
36483  * Based on:
36484  * Ext JS Library 1.1.1
36485  * Copyright(c) 2006-2007, Ext JS, LLC.
36486  *
36487  * Originally Released Under LGPL - original licence link has changed is not relivant.
36488  *
36489  * Fork - LGPL
36490  * <script type="text/javascript">
36491  */
36492  
36493
36494
36495 /**
36496  * @class Roo.SplitLayoutRegion
36497  * @extends Roo.LayoutRegion
36498  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36499  */
36500 Roo.bootstrap.layout.Split = function(config){
36501     this.cursor = config.cursor;
36502     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36503 };
36504
36505 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36506 {
36507     splitTip : "Drag to resize.",
36508     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36509     useSplitTips : false,
36510
36511     applyConfig : function(config){
36512         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36513     },
36514     
36515     onRender : function(ctr,pos) {
36516         
36517         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36518         if(!this.config.split){
36519             return;
36520         }
36521         if(!this.split){
36522             
36523             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36524                             tag: "div",
36525                             id: this.el.id + "-split",
36526                             cls: "roo-layout-split roo-layout-split-"+this.position,
36527                             html: "&#160;"
36528             });
36529             /** The SplitBar for this region 
36530             * @type Roo.SplitBar */
36531             // does not exist yet...
36532             Roo.log([this.position, this.orientation]);
36533             
36534             this.split = new Roo.bootstrap.SplitBar({
36535                 dragElement : splitEl,
36536                 resizingElement: this.el,
36537                 orientation : this.orientation
36538             });
36539             
36540             this.split.on("moved", this.onSplitMove, this);
36541             this.split.useShim = this.config.useShim === true;
36542             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36543             if(this.useSplitTips){
36544                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36545             }
36546             //if(config.collapsible){
36547             //    this.split.el.on("dblclick", this.collapse,  this);
36548             //}
36549         }
36550         if(typeof this.config.minSize != "undefined"){
36551             this.split.minSize = this.config.minSize;
36552         }
36553         if(typeof this.config.maxSize != "undefined"){
36554             this.split.maxSize = this.config.maxSize;
36555         }
36556         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36557             this.hideSplitter();
36558         }
36559         
36560     },
36561
36562     getHMaxSize : function(){
36563          var cmax = this.config.maxSize || 10000;
36564          var center = this.mgr.getRegion("center");
36565          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36566     },
36567
36568     getVMaxSize : function(){
36569          var cmax = this.config.maxSize || 10000;
36570          var center = this.mgr.getRegion("center");
36571          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36572     },
36573
36574     onSplitMove : function(split, newSize){
36575         this.fireEvent("resized", this, newSize);
36576     },
36577     
36578     /** 
36579      * Returns the {@link Roo.SplitBar} for this region.
36580      * @return {Roo.SplitBar}
36581      */
36582     getSplitBar : function(){
36583         return this.split;
36584     },
36585     
36586     hide : function(){
36587         this.hideSplitter();
36588         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36589     },
36590
36591     hideSplitter : function(){
36592         if(this.split){
36593             this.split.el.setLocation(-2000,-2000);
36594             this.split.el.hide();
36595         }
36596     },
36597
36598     show : function(){
36599         if(this.split){
36600             this.split.el.show();
36601         }
36602         Roo.bootstrap.layout.Split.superclass.show.call(this);
36603     },
36604     
36605     beforeSlide: function(){
36606         if(Roo.isGecko){// firefox overflow auto bug workaround
36607             this.bodyEl.clip();
36608             if(this.tabs) {
36609                 this.tabs.bodyEl.clip();
36610             }
36611             if(this.activePanel){
36612                 this.activePanel.getEl().clip();
36613                 
36614                 if(this.activePanel.beforeSlide){
36615                     this.activePanel.beforeSlide();
36616                 }
36617             }
36618         }
36619     },
36620     
36621     afterSlide : function(){
36622         if(Roo.isGecko){// firefox overflow auto bug workaround
36623             this.bodyEl.unclip();
36624             if(this.tabs) {
36625                 this.tabs.bodyEl.unclip();
36626             }
36627             if(this.activePanel){
36628                 this.activePanel.getEl().unclip();
36629                 if(this.activePanel.afterSlide){
36630                     this.activePanel.afterSlide();
36631                 }
36632             }
36633         }
36634     },
36635
36636     initAutoHide : function(){
36637         if(this.autoHide !== false){
36638             if(!this.autoHideHd){
36639                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36640                 this.autoHideHd = {
36641                     "mouseout": function(e){
36642                         if(!e.within(this.el, true)){
36643                             st.delay(500);
36644                         }
36645                     },
36646                     "mouseover" : function(e){
36647                         st.cancel();
36648                     },
36649                     scope : this
36650                 };
36651             }
36652             this.el.on(this.autoHideHd);
36653         }
36654     },
36655
36656     clearAutoHide : function(){
36657         if(this.autoHide !== false){
36658             this.el.un("mouseout", this.autoHideHd.mouseout);
36659             this.el.un("mouseover", this.autoHideHd.mouseover);
36660         }
36661     },
36662
36663     clearMonitor : function(){
36664         Roo.get(document).un("click", this.slideInIf, this);
36665     },
36666
36667     // these names are backwards but not changed for compat
36668     slideOut : function(){
36669         if(this.isSlid || this.el.hasActiveFx()){
36670             return;
36671         }
36672         this.isSlid = true;
36673         if(this.collapseBtn){
36674             this.collapseBtn.hide();
36675         }
36676         this.closeBtnState = this.closeBtn.getStyle('display');
36677         this.closeBtn.hide();
36678         if(this.stickBtn){
36679             this.stickBtn.show();
36680         }
36681         this.el.show();
36682         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36683         this.beforeSlide();
36684         this.el.setStyle("z-index", 10001);
36685         this.el.slideIn(this.getSlideAnchor(), {
36686             callback: function(){
36687                 this.afterSlide();
36688                 this.initAutoHide();
36689                 Roo.get(document).on("click", this.slideInIf, this);
36690                 this.fireEvent("slideshow", this);
36691             },
36692             scope: this,
36693             block: true
36694         });
36695     },
36696
36697     afterSlideIn : function(){
36698         this.clearAutoHide();
36699         this.isSlid = false;
36700         this.clearMonitor();
36701         this.el.setStyle("z-index", "");
36702         if(this.collapseBtn){
36703             this.collapseBtn.show();
36704         }
36705         this.closeBtn.setStyle('display', this.closeBtnState);
36706         if(this.stickBtn){
36707             this.stickBtn.hide();
36708         }
36709         this.fireEvent("slidehide", this);
36710     },
36711
36712     slideIn : function(cb){
36713         if(!this.isSlid || this.el.hasActiveFx()){
36714             Roo.callback(cb);
36715             return;
36716         }
36717         this.isSlid = false;
36718         this.beforeSlide();
36719         this.el.slideOut(this.getSlideAnchor(), {
36720             callback: function(){
36721                 this.el.setLeftTop(-10000, -10000);
36722                 this.afterSlide();
36723                 this.afterSlideIn();
36724                 Roo.callback(cb);
36725             },
36726             scope: this,
36727             block: true
36728         });
36729     },
36730     
36731     slideInIf : function(e){
36732         if(!e.within(this.el)){
36733             this.slideIn();
36734         }
36735     },
36736
36737     animateCollapse : function(){
36738         this.beforeSlide();
36739         this.el.setStyle("z-index", 20000);
36740         var anchor = this.getSlideAnchor();
36741         this.el.slideOut(anchor, {
36742             callback : function(){
36743                 this.el.setStyle("z-index", "");
36744                 this.collapsedEl.slideIn(anchor, {duration:.3});
36745                 this.afterSlide();
36746                 this.el.setLocation(-10000,-10000);
36747                 this.el.hide();
36748                 this.fireEvent("collapsed", this);
36749             },
36750             scope: this,
36751             block: true
36752         });
36753     },
36754
36755     animateExpand : function(){
36756         this.beforeSlide();
36757         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36758         this.el.setStyle("z-index", 20000);
36759         this.collapsedEl.hide({
36760             duration:.1
36761         });
36762         this.el.slideIn(this.getSlideAnchor(), {
36763             callback : function(){
36764                 this.el.setStyle("z-index", "");
36765                 this.afterSlide();
36766                 if(this.split){
36767                     this.split.el.show();
36768                 }
36769                 this.fireEvent("invalidated", this);
36770                 this.fireEvent("expanded", this);
36771             },
36772             scope: this,
36773             block: true
36774         });
36775     },
36776
36777     anchors : {
36778         "west" : "left",
36779         "east" : "right",
36780         "north" : "top",
36781         "south" : "bottom"
36782     },
36783
36784     sanchors : {
36785         "west" : "l",
36786         "east" : "r",
36787         "north" : "t",
36788         "south" : "b"
36789     },
36790
36791     canchors : {
36792         "west" : "tl-tr",
36793         "east" : "tr-tl",
36794         "north" : "tl-bl",
36795         "south" : "bl-tl"
36796     },
36797
36798     getAnchor : function(){
36799         return this.anchors[this.position];
36800     },
36801
36802     getCollapseAnchor : function(){
36803         return this.canchors[this.position];
36804     },
36805
36806     getSlideAnchor : function(){
36807         return this.sanchors[this.position];
36808     },
36809
36810     getAlignAdj : function(){
36811         var cm = this.cmargins;
36812         switch(this.position){
36813             case "west":
36814                 return [0, 0];
36815             break;
36816             case "east":
36817                 return [0, 0];
36818             break;
36819             case "north":
36820                 return [0, 0];
36821             break;
36822             case "south":
36823                 return [0, 0];
36824             break;
36825         }
36826     },
36827
36828     getExpandAdj : function(){
36829         var c = this.collapsedEl, cm = this.cmargins;
36830         switch(this.position){
36831             case "west":
36832                 return [-(cm.right+c.getWidth()+cm.left), 0];
36833             break;
36834             case "east":
36835                 return [cm.right+c.getWidth()+cm.left, 0];
36836             break;
36837             case "north":
36838                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36839             break;
36840             case "south":
36841                 return [0, cm.top+cm.bottom+c.getHeight()];
36842             break;
36843         }
36844     }
36845 });/*
36846  * Based on:
36847  * Ext JS Library 1.1.1
36848  * Copyright(c) 2006-2007, Ext JS, LLC.
36849  *
36850  * Originally Released Under LGPL - original licence link has changed is not relivant.
36851  *
36852  * Fork - LGPL
36853  * <script type="text/javascript">
36854  */
36855 /*
36856  * These classes are private internal classes
36857  */
36858 Roo.bootstrap.layout.Center = function(config){
36859     config.region = "center";
36860     Roo.bootstrap.layout.Region.call(this, config);
36861     this.visible = true;
36862     this.minWidth = config.minWidth || 20;
36863     this.minHeight = config.minHeight || 20;
36864 };
36865
36866 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36867     hide : function(){
36868         // center panel can't be hidden
36869     },
36870     
36871     show : function(){
36872         // center panel can't be hidden
36873     },
36874     
36875     getMinWidth: function(){
36876         return this.minWidth;
36877     },
36878     
36879     getMinHeight: function(){
36880         return this.minHeight;
36881     }
36882 });
36883
36884
36885
36886
36887  
36888
36889
36890
36891
36892
36893 Roo.bootstrap.layout.North = function(config)
36894 {
36895     config.region = 'north';
36896     config.cursor = 'n-resize';
36897     
36898     Roo.bootstrap.layout.Split.call(this, config);
36899     
36900     
36901     if(this.split){
36902         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36903         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36904         this.split.el.addClass("roo-layout-split-v");
36905     }
36906     var size = config.initialSize || config.height;
36907     if(typeof size != "undefined"){
36908         this.el.setHeight(size);
36909     }
36910 };
36911 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36912 {
36913     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36914     
36915     
36916     
36917     getBox : function(){
36918         if(this.collapsed){
36919             return this.collapsedEl.getBox();
36920         }
36921         var box = this.el.getBox();
36922         if(this.split){
36923             box.height += this.split.el.getHeight();
36924         }
36925         return box;
36926     },
36927     
36928     updateBox : function(box){
36929         if(this.split && !this.collapsed){
36930             box.height -= this.split.el.getHeight();
36931             this.split.el.setLeft(box.x);
36932             this.split.el.setTop(box.y+box.height);
36933             this.split.el.setWidth(box.width);
36934         }
36935         if(this.collapsed){
36936             this.updateBody(box.width, null);
36937         }
36938         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36939     }
36940 });
36941
36942
36943
36944
36945
36946 Roo.bootstrap.layout.South = function(config){
36947     config.region = 'south';
36948     config.cursor = 's-resize';
36949     Roo.bootstrap.layout.Split.call(this, config);
36950     if(this.split){
36951         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36952         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36953         this.split.el.addClass("roo-layout-split-v");
36954     }
36955     var size = config.initialSize || config.height;
36956     if(typeof size != "undefined"){
36957         this.el.setHeight(size);
36958     }
36959 };
36960
36961 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36962     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36963     getBox : function(){
36964         if(this.collapsed){
36965             return this.collapsedEl.getBox();
36966         }
36967         var box = this.el.getBox();
36968         if(this.split){
36969             var sh = this.split.el.getHeight();
36970             box.height += sh;
36971             box.y -= sh;
36972         }
36973         return box;
36974     },
36975     
36976     updateBox : function(box){
36977         if(this.split && !this.collapsed){
36978             var sh = this.split.el.getHeight();
36979             box.height -= sh;
36980             box.y += sh;
36981             this.split.el.setLeft(box.x);
36982             this.split.el.setTop(box.y-sh);
36983             this.split.el.setWidth(box.width);
36984         }
36985         if(this.collapsed){
36986             this.updateBody(box.width, null);
36987         }
36988         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36989     }
36990 });
36991
36992 Roo.bootstrap.layout.East = function(config){
36993     config.region = "east";
36994     config.cursor = "e-resize";
36995     Roo.bootstrap.layout.Split.call(this, config);
36996     if(this.split){
36997         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36998         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36999         this.split.el.addClass("roo-layout-split-h");
37000     }
37001     var size = config.initialSize || config.width;
37002     if(typeof size != "undefined"){
37003         this.el.setWidth(size);
37004     }
37005 };
37006 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37007     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37008     getBox : function(){
37009         if(this.collapsed){
37010             return this.collapsedEl.getBox();
37011         }
37012         var box = this.el.getBox();
37013         if(this.split){
37014             var sw = this.split.el.getWidth();
37015             box.width += sw;
37016             box.x -= sw;
37017         }
37018         return box;
37019     },
37020
37021     updateBox : function(box){
37022         if(this.split && !this.collapsed){
37023             var sw = this.split.el.getWidth();
37024             box.width -= sw;
37025             this.split.el.setLeft(box.x);
37026             this.split.el.setTop(box.y);
37027             this.split.el.setHeight(box.height);
37028             box.x += sw;
37029         }
37030         if(this.collapsed){
37031             this.updateBody(null, box.height);
37032         }
37033         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37034     }
37035 });
37036
37037 Roo.bootstrap.layout.West = function(config){
37038     config.region = "west";
37039     config.cursor = "w-resize";
37040     
37041     Roo.bootstrap.layout.Split.call(this, config);
37042     if(this.split){
37043         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37044         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37045         this.split.el.addClass("roo-layout-split-h");
37046     }
37047     
37048 };
37049 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37050     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37051     
37052     onRender: function(ctr, pos)
37053     {
37054         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37055         var size = this.config.initialSize || this.config.width;
37056         if(typeof size != "undefined"){
37057             this.el.setWidth(size);
37058         }
37059     },
37060     
37061     getBox : function(){
37062         if(this.collapsed){
37063             return this.collapsedEl.getBox();
37064         }
37065         var box = this.el.getBox();
37066         if(this.split){
37067             box.width += this.split.el.getWidth();
37068         }
37069         return box;
37070     },
37071     
37072     updateBox : function(box){
37073         if(this.split && !this.collapsed){
37074             var sw = this.split.el.getWidth();
37075             box.width -= sw;
37076             this.split.el.setLeft(box.x+box.width);
37077             this.split.el.setTop(box.y);
37078             this.split.el.setHeight(box.height);
37079         }
37080         if(this.collapsed){
37081             this.updateBody(null, box.height);
37082         }
37083         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37084     }
37085 });
37086 Roo.namespace("Roo.bootstrap.panel");/*
37087  * Based on:
37088  * Ext JS Library 1.1.1
37089  * Copyright(c) 2006-2007, Ext JS, LLC.
37090  *
37091  * Originally Released Under LGPL - original licence link has changed is not relivant.
37092  *
37093  * Fork - LGPL
37094  * <script type="text/javascript">
37095  */
37096 /**
37097  * @class Roo.ContentPanel
37098  * @extends Roo.util.Observable
37099  * A basic ContentPanel element.
37100  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37101  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37102  * @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
37103  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37104  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37105  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37106  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37107  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37108  * @cfg {String} title          The title for this panel
37109  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37110  * @cfg {String} url            Calls {@link #setUrl} with this value
37111  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37112  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37113  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37114  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37115  * @cfg {Boolean} badges render the badges
37116
37117  * @constructor
37118  * Create a new ContentPanel.
37119  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37120  * @param {String/Object} config A string to set only the title or a config object
37121  * @param {String} content (optional) Set the HTML content for this panel
37122  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37123  */
37124 Roo.bootstrap.panel.Content = function( config){
37125     
37126     this.tpl = config.tpl || false;
37127     
37128     var el = config.el;
37129     var content = config.content;
37130
37131     if(config.autoCreate){ // xtype is available if this is called from factory
37132         el = Roo.id();
37133     }
37134     this.el = Roo.get(el);
37135     if(!this.el && config && config.autoCreate){
37136         if(typeof config.autoCreate == "object"){
37137             if(!config.autoCreate.id){
37138                 config.autoCreate.id = config.id||el;
37139             }
37140             this.el = Roo.DomHelper.append(document.body,
37141                         config.autoCreate, true);
37142         }else{
37143             var elcfg =  {   tag: "div",
37144                             cls: "roo-layout-inactive-content",
37145                             id: config.id||el
37146                             };
37147             if (config.html) {
37148                 elcfg.html = config.html;
37149                 
37150             }
37151                         
37152             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37153         }
37154     } 
37155     this.closable = false;
37156     this.loaded = false;
37157     this.active = false;
37158    
37159       
37160     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37161         
37162         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37163         
37164         this.wrapEl = this.el; //this.el.wrap();
37165         var ti = [];
37166         if (config.toolbar.items) {
37167             ti = config.toolbar.items ;
37168             delete config.toolbar.items ;
37169         }
37170         
37171         var nitems = [];
37172         this.toolbar.render(this.wrapEl, 'before');
37173         for(var i =0;i < ti.length;i++) {
37174           //  Roo.log(['add child', items[i]]);
37175             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37176         }
37177         this.toolbar.items = nitems;
37178         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37179         delete config.toolbar;
37180         
37181     }
37182     /*
37183     // xtype created footer. - not sure if will work as we normally have to render first..
37184     if (this.footer && !this.footer.el && this.footer.xtype) {
37185         if (!this.wrapEl) {
37186             this.wrapEl = this.el.wrap();
37187         }
37188     
37189         this.footer.container = this.wrapEl.createChild();
37190          
37191         this.footer = Roo.factory(this.footer, Roo);
37192         
37193     }
37194     */
37195     
37196      if(typeof config == "string"){
37197         this.title = config;
37198     }else{
37199         Roo.apply(this, config);
37200     }
37201     
37202     if(this.resizeEl){
37203         this.resizeEl = Roo.get(this.resizeEl, true);
37204     }else{
37205         this.resizeEl = this.el;
37206     }
37207     // handle view.xtype
37208     
37209  
37210     
37211     
37212     this.addEvents({
37213         /**
37214          * @event activate
37215          * Fires when this panel is activated. 
37216          * @param {Roo.ContentPanel} this
37217          */
37218         "activate" : true,
37219         /**
37220          * @event deactivate
37221          * Fires when this panel is activated. 
37222          * @param {Roo.ContentPanel} this
37223          */
37224         "deactivate" : true,
37225
37226         /**
37227          * @event resize
37228          * Fires when this panel is resized if fitToFrame is true.
37229          * @param {Roo.ContentPanel} this
37230          * @param {Number} width The width after any component adjustments
37231          * @param {Number} height The height after any component adjustments
37232          */
37233         "resize" : true,
37234         
37235          /**
37236          * @event render
37237          * Fires when this tab is created
37238          * @param {Roo.ContentPanel} this
37239          */
37240         "render" : true
37241         
37242         
37243         
37244     });
37245     
37246
37247     
37248     
37249     if(this.autoScroll){
37250         this.resizeEl.setStyle("overflow", "auto");
37251     } else {
37252         // fix randome scrolling
37253         //this.el.on('scroll', function() {
37254         //    Roo.log('fix random scolling');
37255         //    this.scrollTo('top',0); 
37256         //});
37257     }
37258     content = content || this.content;
37259     if(content){
37260         this.setContent(content);
37261     }
37262     if(config && config.url){
37263         this.setUrl(this.url, this.params, this.loadOnce);
37264     }
37265     
37266     
37267     
37268     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37269     
37270     if (this.view && typeof(this.view.xtype) != 'undefined') {
37271         this.view.el = this.el.appendChild(document.createElement("div"));
37272         this.view = Roo.factory(this.view); 
37273         this.view.render  &&  this.view.render(false, '');  
37274     }
37275     
37276     
37277     this.fireEvent('render', this);
37278 };
37279
37280 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37281     
37282     tabTip : '',
37283     
37284     setRegion : function(region){
37285         this.region = region;
37286         this.setActiveClass(region && !this.background);
37287     },
37288     
37289     
37290     setActiveClass: function(state)
37291     {
37292         if(state){
37293            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37294            this.el.setStyle('position','relative');
37295         }else{
37296            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37297            this.el.setStyle('position', 'absolute');
37298         } 
37299     },
37300     
37301     /**
37302      * Returns the toolbar for this Panel if one was configured. 
37303      * @return {Roo.Toolbar} 
37304      */
37305     getToolbar : function(){
37306         return this.toolbar;
37307     },
37308     
37309     setActiveState : function(active)
37310     {
37311         this.active = active;
37312         this.setActiveClass(active);
37313         if(!active){
37314             if(this.fireEvent("deactivate", this) === false){
37315                 return false;
37316             }
37317             return true;
37318         }
37319         this.fireEvent("activate", this);
37320         return true;
37321     },
37322     /**
37323      * Updates this panel's element
37324      * @param {String} content The new content
37325      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37326     */
37327     setContent : function(content, loadScripts){
37328         this.el.update(content, loadScripts);
37329     },
37330
37331     ignoreResize : function(w, h){
37332         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37333             return true;
37334         }else{
37335             this.lastSize = {width: w, height: h};
37336             return false;
37337         }
37338     },
37339     /**
37340      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37341      * @return {Roo.UpdateManager} The UpdateManager
37342      */
37343     getUpdateManager : function(){
37344         return this.el.getUpdateManager();
37345     },
37346      /**
37347      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37348      * @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:
37349 <pre><code>
37350 panel.load({
37351     url: "your-url.php",
37352     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37353     callback: yourFunction,
37354     scope: yourObject, //(optional scope)
37355     discardUrl: false,
37356     nocache: false,
37357     text: "Loading...",
37358     timeout: 30,
37359     scripts: false
37360 });
37361 </code></pre>
37362      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37363      * 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.
37364      * @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}
37365      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37366      * @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.
37367      * @return {Roo.ContentPanel} this
37368      */
37369     load : function(){
37370         var um = this.el.getUpdateManager();
37371         um.update.apply(um, arguments);
37372         return this;
37373     },
37374
37375
37376     /**
37377      * 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.
37378      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37379      * @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)
37380      * @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)
37381      * @return {Roo.UpdateManager} The UpdateManager
37382      */
37383     setUrl : function(url, params, loadOnce){
37384         if(this.refreshDelegate){
37385             this.removeListener("activate", this.refreshDelegate);
37386         }
37387         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37388         this.on("activate", this.refreshDelegate);
37389         return this.el.getUpdateManager();
37390     },
37391     
37392     _handleRefresh : function(url, params, loadOnce){
37393         if(!loadOnce || !this.loaded){
37394             var updater = this.el.getUpdateManager();
37395             updater.update(url, params, this._setLoaded.createDelegate(this));
37396         }
37397     },
37398     
37399     _setLoaded : function(){
37400         this.loaded = true;
37401     }, 
37402     
37403     /**
37404      * Returns this panel's id
37405      * @return {String} 
37406      */
37407     getId : function(){
37408         return this.el.id;
37409     },
37410     
37411     /** 
37412      * Returns this panel's element - used by regiosn to add.
37413      * @return {Roo.Element} 
37414      */
37415     getEl : function(){
37416         return this.wrapEl || this.el;
37417     },
37418     
37419    
37420     
37421     adjustForComponents : function(width, height)
37422     {
37423         //Roo.log('adjustForComponents ');
37424         if(this.resizeEl != this.el){
37425             width -= this.el.getFrameWidth('lr');
37426             height -= this.el.getFrameWidth('tb');
37427         }
37428         if(this.toolbar){
37429             var te = this.toolbar.getEl();
37430             te.setWidth(width);
37431             height -= te.getHeight();
37432         }
37433         if(this.footer){
37434             var te = this.footer.getEl();
37435             te.setWidth(width);
37436             height -= te.getHeight();
37437         }
37438         
37439         
37440         if(this.adjustments){
37441             width += this.adjustments[0];
37442             height += this.adjustments[1];
37443         }
37444         return {"width": width, "height": height};
37445     },
37446     
37447     setSize : function(width, height){
37448         if(this.fitToFrame && !this.ignoreResize(width, height)){
37449             if(this.fitContainer && this.resizeEl != this.el){
37450                 this.el.setSize(width, height);
37451             }
37452             var size = this.adjustForComponents(width, height);
37453             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37454             this.fireEvent('resize', this, size.width, size.height);
37455         }
37456     },
37457     
37458     /**
37459      * Returns this panel's title
37460      * @return {String} 
37461      */
37462     getTitle : function(){
37463         
37464         if (typeof(this.title) != 'object') {
37465             return this.title;
37466         }
37467         
37468         var t = '';
37469         for (var k in this.title) {
37470             if (!this.title.hasOwnProperty(k)) {
37471                 continue;
37472             }
37473             
37474             if (k.indexOf('-') >= 0) {
37475                 var s = k.split('-');
37476                 for (var i = 0; i<s.length; i++) {
37477                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37478                 }
37479             } else {
37480                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37481             }
37482         }
37483         return t;
37484     },
37485     
37486     /**
37487      * Set this panel's title
37488      * @param {String} title
37489      */
37490     setTitle : function(title){
37491         this.title = title;
37492         if(this.region){
37493             this.region.updatePanelTitle(this, title);
37494         }
37495     },
37496     
37497     /**
37498      * Returns true is this panel was configured to be closable
37499      * @return {Boolean} 
37500      */
37501     isClosable : function(){
37502         return this.closable;
37503     },
37504     
37505     beforeSlide : function(){
37506         this.el.clip();
37507         this.resizeEl.clip();
37508     },
37509     
37510     afterSlide : function(){
37511         this.el.unclip();
37512         this.resizeEl.unclip();
37513     },
37514     
37515     /**
37516      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37517      *   Will fail silently if the {@link #setUrl} method has not been called.
37518      *   This does not activate the panel, just updates its content.
37519      */
37520     refresh : function(){
37521         if(this.refreshDelegate){
37522            this.loaded = false;
37523            this.refreshDelegate();
37524         }
37525     },
37526     
37527     /**
37528      * Destroys this panel
37529      */
37530     destroy : function(){
37531         this.el.removeAllListeners();
37532         var tempEl = document.createElement("span");
37533         tempEl.appendChild(this.el.dom);
37534         tempEl.innerHTML = "";
37535         this.el.remove();
37536         this.el = null;
37537     },
37538     
37539     /**
37540      * form - if the content panel contains a form - this is a reference to it.
37541      * @type {Roo.form.Form}
37542      */
37543     form : false,
37544     /**
37545      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37546      *    This contains a reference to it.
37547      * @type {Roo.View}
37548      */
37549     view : false,
37550     
37551       /**
37552      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37553      * <pre><code>
37554
37555 layout.addxtype({
37556        xtype : 'Form',
37557        items: [ .... ]
37558    }
37559 );
37560
37561 </code></pre>
37562      * @param {Object} cfg Xtype definition of item to add.
37563      */
37564     
37565     
37566     getChildContainer: function () {
37567         return this.getEl();
37568     }
37569     
37570     
37571     /*
37572         var  ret = new Roo.factory(cfg);
37573         return ret;
37574         
37575         
37576         // add form..
37577         if (cfg.xtype.match(/^Form$/)) {
37578             
37579             var el;
37580             //if (this.footer) {
37581             //    el = this.footer.container.insertSibling(false, 'before');
37582             //} else {
37583                 el = this.el.createChild();
37584             //}
37585
37586             this.form = new  Roo.form.Form(cfg);
37587             
37588             
37589             if ( this.form.allItems.length) {
37590                 this.form.render(el.dom);
37591             }
37592             return this.form;
37593         }
37594         // should only have one of theses..
37595         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37596             // views.. should not be just added - used named prop 'view''
37597             
37598             cfg.el = this.el.appendChild(document.createElement("div"));
37599             // factory?
37600             
37601             var ret = new Roo.factory(cfg);
37602              
37603              ret.render && ret.render(false, ''); // render blank..
37604             this.view = ret;
37605             return ret;
37606         }
37607         return false;
37608     }
37609     \*/
37610 });
37611  
37612 /**
37613  * @class Roo.bootstrap.panel.Grid
37614  * @extends Roo.bootstrap.panel.Content
37615  * @constructor
37616  * Create a new GridPanel.
37617  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37618  * @param {Object} config A the config object
37619   
37620  */
37621
37622
37623
37624 Roo.bootstrap.panel.Grid = function(config)
37625 {
37626     
37627       
37628     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37629         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37630
37631     config.el = this.wrapper;
37632     //this.el = this.wrapper;
37633     
37634       if (config.container) {
37635         // ctor'ed from a Border/panel.grid
37636         
37637         
37638         this.wrapper.setStyle("overflow", "hidden");
37639         this.wrapper.addClass('roo-grid-container');
37640
37641     }
37642     
37643     
37644     if(config.toolbar){
37645         var tool_el = this.wrapper.createChild();    
37646         this.toolbar = Roo.factory(config.toolbar);
37647         var ti = [];
37648         if (config.toolbar.items) {
37649             ti = config.toolbar.items ;
37650             delete config.toolbar.items ;
37651         }
37652         
37653         var nitems = [];
37654         this.toolbar.render(tool_el);
37655         for(var i =0;i < ti.length;i++) {
37656           //  Roo.log(['add child', items[i]]);
37657             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37658         }
37659         this.toolbar.items = nitems;
37660         
37661         delete config.toolbar;
37662     }
37663     
37664     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37665     config.grid.scrollBody = true;;
37666     config.grid.monitorWindowResize = false; // turn off autosizing
37667     config.grid.autoHeight = false;
37668     config.grid.autoWidth = false;
37669     
37670     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37671     
37672     if (config.background) {
37673         // render grid on panel activation (if panel background)
37674         this.on('activate', function(gp) {
37675             if (!gp.grid.rendered) {
37676                 gp.grid.render(this.wrapper);
37677                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37678             }
37679         });
37680             
37681     } else {
37682         this.grid.render(this.wrapper);
37683         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37684
37685     }
37686     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37687     // ??? needed ??? config.el = this.wrapper;
37688     
37689     
37690     
37691   
37692     // xtype created footer. - not sure if will work as we normally have to render first..
37693     if (this.footer && !this.footer.el && this.footer.xtype) {
37694         
37695         var ctr = this.grid.getView().getFooterPanel(true);
37696         this.footer.dataSource = this.grid.dataSource;
37697         this.footer = Roo.factory(this.footer, Roo);
37698         this.footer.render(ctr);
37699         
37700     }
37701     
37702     
37703     
37704     
37705      
37706 };
37707
37708 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37709     getId : function(){
37710         return this.grid.id;
37711     },
37712     
37713     /**
37714      * Returns the grid for this panel
37715      * @return {Roo.bootstrap.Table} 
37716      */
37717     getGrid : function(){
37718         return this.grid;    
37719     },
37720     
37721     setSize : function(width, height){
37722         if(!this.ignoreResize(width, height)){
37723             var grid = this.grid;
37724             var size = this.adjustForComponents(width, height);
37725             var gridel = grid.getGridEl();
37726             gridel.setSize(size.width, size.height);
37727             /*
37728             var thd = grid.getGridEl().select('thead',true).first();
37729             var tbd = grid.getGridEl().select('tbody', true).first();
37730             if (tbd) {
37731                 tbd.setSize(width, height - thd.getHeight());
37732             }
37733             */
37734             grid.autoSize();
37735         }
37736     },
37737      
37738     
37739     
37740     beforeSlide : function(){
37741         this.grid.getView().scroller.clip();
37742     },
37743     
37744     afterSlide : function(){
37745         this.grid.getView().scroller.unclip();
37746     },
37747     
37748     destroy : function(){
37749         this.grid.destroy();
37750         delete this.grid;
37751         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37752     }
37753 });
37754
37755 /**
37756  * @class Roo.bootstrap.panel.Nest
37757  * @extends Roo.bootstrap.panel.Content
37758  * @constructor
37759  * Create a new Panel, that can contain a layout.Border.
37760  * 
37761  * 
37762  * @param {Roo.BorderLayout} layout The layout for this panel
37763  * @param {String/Object} config A string to set only the title or a config object
37764  */
37765 Roo.bootstrap.panel.Nest = function(config)
37766 {
37767     // construct with only one argument..
37768     /* FIXME - implement nicer consturctors
37769     if (layout.layout) {
37770         config = layout;
37771         layout = config.layout;
37772         delete config.layout;
37773     }
37774     if (layout.xtype && !layout.getEl) {
37775         // then layout needs constructing..
37776         layout = Roo.factory(layout, Roo);
37777     }
37778     */
37779     
37780     config.el =  config.layout.getEl();
37781     
37782     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37783     
37784     config.layout.monitorWindowResize = false; // turn off autosizing
37785     this.layout = config.layout;
37786     this.layout.getEl().addClass("roo-layout-nested-layout");
37787     
37788     
37789     
37790     
37791 };
37792
37793 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37794
37795     setSize : function(width, height){
37796         if(!this.ignoreResize(width, height)){
37797             var size = this.adjustForComponents(width, height);
37798             var el = this.layout.getEl();
37799             if (size.height < 1) {
37800                 el.setWidth(size.width);   
37801             } else {
37802                 el.setSize(size.width, size.height);
37803             }
37804             var touch = el.dom.offsetWidth;
37805             this.layout.layout();
37806             // ie requires a double layout on the first pass
37807             if(Roo.isIE && !this.initialized){
37808                 this.initialized = true;
37809                 this.layout.layout();
37810             }
37811         }
37812     },
37813     
37814     // activate all subpanels if not currently active..
37815     
37816     setActiveState : function(active){
37817         this.active = active;
37818         this.setActiveClass(active);
37819         
37820         if(!active){
37821             this.fireEvent("deactivate", this);
37822             return;
37823         }
37824         
37825         this.fireEvent("activate", this);
37826         // not sure if this should happen before or after..
37827         if (!this.layout) {
37828             return; // should not happen..
37829         }
37830         var reg = false;
37831         for (var r in this.layout.regions) {
37832             reg = this.layout.getRegion(r);
37833             if (reg.getActivePanel()) {
37834                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37835                 reg.setActivePanel(reg.getActivePanel());
37836                 continue;
37837             }
37838             if (!reg.panels.length) {
37839                 continue;
37840             }
37841             reg.showPanel(reg.getPanel(0));
37842         }
37843         
37844         
37845         
37846         
37847     },
37848     
37849     /**
37850      * Returns the nested BorderLayout for this panel
37851      * @return {Roo.BorderLayout} 
37852      */
37853     getLayout : function(){
37854         return this.layout;
37855     },
37856     
37857      /**
37858      * Adds a xtype elements to the layout of the nested panel
37859      * <pre><code>
37860
37861 panel.addxtype({
37862        xtype : 'ContentPanel',
37863        region: 'west',
37864        items: [ .... ]
37865    }
37866 );
37867
37868 panel.addxtype({
37869         xtype : 'NestedLayoutPanel',
37870         region: 'west',
37871         layout: {
37872            center: { },
37873            west: { }   
37874         },
37875         items : [ ... list of content panels or nested layout panels.. ]
37876    }
37877 );
37878 </code></pre>
37879      * @param {Object} cfg Xtype definition of item to add.
37880      */
37881     addxtype : function(cfg) {
37882         return this.layout.addxtype(cfg);
37883     
37884     }
37885 });        /*
37886  * Based on:
37887  * Ext JS Library 1.1.1
37888  * Copyright(c) 2006-2007, Ext JS, LLC.
37889  *
37890  * Originally Released Under LGPL - original licence link has changed is not relivant.
37891  *
37892  * Fork - LGPL
37893  * <script type="text/javascript">
37894  */
37895 /**
37896  * @class Roo.TabPanel
37897  * @extends Roo.util.Observable
37898  * A lightweight tab container.
37899  * <br><br>
37900  * Usage:
37901  * <pre><code>
37902 // basic tabs 1, built from existing content
37903 var tabs = new Roo.TabPanel("tabs1");
37904 tabs.addTab("script", "View Script");
37905 tabs.addTab("markup", "View Markup");
37906 tabs.activate("script");
37907
37908 // more advanced tabs, built from javascript
37909 var jtabs = new Roo.TabPanel("jtabs");
37910 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37911
37912 // set up the UpdateManager
37913 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37914 var updater = tab2.getUpdateManager();
37915 updater.setDefaultUrl("ajax1.htm");
37916 tab2.on('activate', updater.refresh, updater, true);
37917
37918 // Use setUrl for Ajax loading
37919 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37920 tab3.setUrl("ajax2.htm", null, true);
37921
37922 // Disabled tab
37923 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37924 tab4.disable();
37925
37926 jtabs.activate("jtabs-1");
37927  * </code></pre>
37928  * @constructor
37929  * Create a new TabPanel.
37930  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37931  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37932  */
37933 Roo.bootstrap.panel.Tabs = function(config){
37934     /**
37935     * The container element for this TabPanel.
37936     * @type Roo.Element
37937     */
37938     this.el = Roo.get(config.el);
37939     delete config.el;
37940     if(config){
37941         if(typeof config == "boolean"){
37942             this.tabPosition = config ? "bottom" : "top";
37943         }else{
37944             Roo.apply(this, config);
37945         }
37946     }
37947     
37948     if(this.tabPosition == "bottom"){
37949         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37950         this.el.addClass("roo-tabs-bottom");
37951     }
37952     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37953     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37954     this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
37955     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37956     if(Roo.isIE){
37957         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37958     }
37959     if(this.tabPosition != "bottom"){
37960         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37961          * @type Roo.Element
37962          */
37963         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37964         this.el.addClass("roo-tabs-top");
37965     }
37966     this.items = [];
37967
37968     this.bodyEl.setStyle("position", "relative");
37969
37970     this.active = null;
37971     this.activateDelegate = this.activate.createDelegate(this);
37972
37973     this.addEvents({
37974         /**
37975          * @event tabchange
37976          * Fires when the active tab changes
37977          * @param {Roo.TabPanel} this
37978          * @param {Roo.TabPanelItem} activePanel The new active tab
37979          */
37980         "tabchange": true,
37981         /**
37982          * @event beforetabchange
37983          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37984          * @param {Roo.TabPanel} this
37985          * @param {Object} e Set cancel to true on this object to cancel the tab change
37986          * @param {Roo.TabPanelItem} tab The tab being changed to
37987          */
37988         "beforetabchange" : true
37989     });
37990
37991     Roo.EventManager.onWindowResize(this.onResize, this);
37992     this.cpad = this.el.getPadding("lr");
37993     this.hiddenCount = 0;
37994
37995
37996     // toolbar on the tabbar support...
37997     if (this.toolbar) {
37998         alert("no toolbar support yet");
37999         this.toolbar  = false;
38000         /*
38001         var tcfg = this.toolbar;
38002         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38003         this.toolbar = new Roo.Toolbar(tcfg);
38004         if (Roo.isSafari) {
38005             var tbl = tcfg.container.child('table', true);
38006             tbl.setAttribute('width', '100%');
38007         }
38008         */
38009         
38010     }
38011    
38012
38013
38014     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38015 };
38016
38017 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38018     /*
38019      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38020      */
38021     tabPosition : "top",
38022     /*
38023      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38024      */
38025     currentTabWidth : 0,
38026     /*
38027      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38028      */
38029     minTabWidth : 40,
38030     /*
38031      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38032      */
38033     maxTabWidth : 250,
38034     /*
38035      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38036      */
38037     preferredTabWidth : 175,
38038     /*
38039      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38040      */
38041     resizeTabs : false,
38042     /*
38043      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38044      */
38045     monitorResize : true,
38046     /*
38047      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38048      */
38049     toolbar : false,
38050
38051     /**
38052      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38053      * @param {String} id The id of the div to use <b>or create</b>
38054      * @param {String} text The text for the tab
38055      * @param {String} content (optional) Content to put in the TabPanelItem body
38056      * @param {Boolean} closable (optional) True to create a close icon on the tab
38057      * @return {Roo.TabPanelItem} The created TabPanelItem
38058      */
38059     addTab : function(id, text, content, closable, tpl)
38060     {
38061         var item = new Roo.bootstrap.panel.TabItem({
38062             panel: this,
38063             id : id,
38064             text : text,
38065             closable : closable,
38066             tpl : tpl
38067         });
38068         this.addTabItem(item);
38069         if(content){
38070             item.setContent(content);
38071         }
38072         return item;
38073     },
38074
38075     /**
38076      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38077      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38078      * @return {Roo.TabPanelItem}
38079      */
38080     getTab : function(id){
38081         return this.items[id];
38082     },
38083
38084     /**
38085      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38086      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38087      */
38088     hideTab : function(id){
38089         var t = this.items[id];
38090         if(!t.isHidden()){
38091            t.setHidden(true);
38092            this.hiddenCount++;
38093            this.autoSizeTabs();
38094         }
38095     },
38096
38097     /**
38098      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38099      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38100      */
38101     unhideTab : function(id){
38102         var t = this.items[id];
38103         if(t.isHidden()){
38104            t.setHidden(false);
38105            this.hiddenCount--;
38106            this.autoSizeTabs();
38107         }
38108     },
38109
38110     /**
38111      * Adds an existing {@link Roo.TabPanelItem}.
38112      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38113      */
38114     addTabItem : function(item)
38115     {
38116         this.items[item.id] = item;
38117         this.items.push(item);
38118         this.autoSizeTabs();
38119       //  if(this.resizeTabs){
38120     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38121   //         this.autoSizeTabs();
38122 //        }else{
38123 //            item.autoSize();
38124        // }
38125     },
38126
38127     /**
38128      * Removes a {@link Roo.TabPanelItem}.
38129      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38130      */
38131     removeTab : function(id){
38132         var items = this.items;
38133         var tab = items[id];
38134         if(!tab) { return; }
38135         var index = items.indexOf(tab);
38136         if(this.active == tab && items.length > 1){
38137             var newTab = this.getNextAvailable(index);
38138             if(newTab) {
38139                 newTab.activate();
38140             }
38141         }
38142         this.stripEl.dom.removeChild(tab.pnode.dom);
38143         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38144             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38145         }
38146         items.splice(index, 1);
38147         delete this.items[tab.id];
38148         tab.fireEvent("close", tab);
38149         tab.purgeListeners();
38150         this.autoSizeTabs();
38151     },
38152
38153     getNextAvailable : function(start){
38154         var items = this.items;
38155         var index = start;
38156         // look for a next tab that will slide over to
38157         // replace the one being removed
38158         while(index < items.length){
38159             var item = items[++index];
38160             if(item && !item.isHidden()){
38161                 return item;
38162             }
38163         }
38164         // if one isn't found select the previous tab (on the left)
38165         index = start;
38166         while(index >= 0){
38167             var item = items[--index];
38168             if(item && !item.isHidden()){
38169                 return item;
38170             }
38171         }
38172         return null;
38173     },
38174
38175     /**
38176      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38177      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38178      */
38179     disableTab : function(id){
38180         var tab = this.items[id];
38181         if(tab && this.active != tab){
38182             tab.disable();
38183         }
38184     },
38185
38186     /**
38187      * Enables a {@link Roo.TabPanelItem} that is disabled.
38188      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38189      */
38190     enableTab : function(id){
38191         var tab = this.items[id];
38192         tab.enable();
38193     },
38194
38195     /**
38196      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38197      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38198      * @return {Roo.TabPanelItem} The TabPanelItem.
38199      */
38200     activate : function(id)
38201     {
38202         var tab = this.items[id];
38203         if(!tab){
38204             return null;
38205         }
38206         if(tab == this.active || tab.disabled){
38207             return tab;
38208         }
38209         var e = {};
38210         this.fireEvent("beforetabchange", this, e, tab);
38211         if(e.cancel !== true && !tab.disabled){
38212             if(this.active){
38213                 this.active.hide();
38214             }
38215             this.active = this.items[id];
38216             this.active.show();
38217             this.fireEvent("tabchange", this, this.active);
38218         }
38219         return tab;
38220     },
38221
38222     /**
38223      * Gets the active {@link Roo.TabPanelItem}.
38224      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38225      */
38226     getActiveTab : function(){
38227         return this.active;
38228     },
38229
38230     /**
38231      * Updates the tab body element to fit the height of the container element
38232      * for overflow scrolling
38233      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38234      */
38235     syncHeight : function(targetHeight){
38236         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38237         var bm = this.bodyEl.getMargins();
38238         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38239         this.bodyEl.setHeight(newHeight);
38240         return newHeight;
38241     },
38242
38243     onResize : function(){
38244         if(this.monitorResize){
38245             this.autoSizeTabs();
38246         }
38247     },
38248
38249     /**
38250      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38251      */
38252     beginUpdate : function(){
38253         this.updating = true;
38254     },
38255
38256     /**
38257      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38258      */
38259     endUpdate : function(){
38260         this.updating = false;
38261         this.autoSizeTabs();
38262     },
38263
38264     /**
38265      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38266      */
38267     autoSizeTabs : function()
38268     {
38269         var count = this.items.length;
38270         var vcount = count - this.hiddenCount;
38271         
38272         if (vcount < 2) {
38273             this.stripEl.hide();
38274         } else {
38275             this.stripEl.show();
38276         }
38277         
38278         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38279             return;
38280         }
38281         
38282         
38283         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38284         var availWidth = Math.floor(w / vcount);
38285         var b = this.stripBody;
38286         if(b.getWidth() > w){
38287             var tabs = this.items;
38288             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38289             if(availWidth < this.minTabWidth){
38290                 /*if(!this.sleft){    // incomplete scrolling code
38291                     this.createScrollButtons();
38292                 }
38293                 this.showScroll();
38294                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38295             }
38296         }else{
38297             if(this.currentTabWidth < this.preferredTabWidth){
38298                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38299             }
38300         }
38301     },
38302
38303     /**
38304      * Returns the number of tabs in this TabPanel.
38305      * @return {Number}
38306      */
38307      getCount : function(){
38308          return this.items.length;
38309      },
38310
38311     /**
38312      * Resizes all the tabs to the passed width
38313      * @param {Number} The new width
38314      */
38315     setTabWidth : function(width){
38316         this.currentTabWidth = width;
38317         for(var i = 0, len = this.items.length; i < len; i++) {
38318                 if(!this.items[i].isHidden()) {
38319                 this.items[i].setWidth(width);
38320             }
38321         }
38322     },
38323
38324     /**
38325      * Destroys this TabPanel
38326      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38327      */
38328     destroy : function(removeEl){
38329         Roo.EventManager.removeResizeListener(this.onResize, this);
38330         for(var i = 0, len = this.items.length; i < len; i++){
38331             this.items[i].purgeListeners();
38332         }
38333         if(removeEl === true){
38334             this.el.update("");
38335             this.el.remove();
38336         }
38337     },
38338     
38339     createStrip : function(container)
38340     {
38341         var strip = document.createElement("nav");
38342         strip.className = Roo.bootstrap.version == 4 ?
38343             "navbar-light bg-light" : 
38344             "navbar navbar-default"; //"x-tabs-wrap";
38345         container.appendChild(strip);
38346         return strip;
38347     },
38348     
38349     createStripList : function(strip)
38350     {
38351         // div wrapper for retard IE
38352         // returns the "tr" element.
38353         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38354         //'<div class="x-tabs-strip-wrap">'+
38355           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38356           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38357         return strip.firstChild; //.firstChild.firstChild.firstChild;
38358     },
38359     createBody : function(container)
38360     {
38361         var body = document.createElement("div");
38362         Roo.id(body, "tab-body");
38363         //Roo.fly(body).addClass("x-tabs-body");
38364         Roo.fly(body).addClass("tab-content");
38365         container.appendChild(body);
38366         return body;
38367     },
38368     createItemBody :function(bodyEl, id){
38369         var body = Roo.getDom(id);
38370         if(!body){
38371             body = document.createElement("div");
38372             body.id = id;
38373         }
38374         //Roo.fly(body).addClass("x-tabs-item-body");
38375         Roo.fly(body).addClass("tab-pane");
38376          bodyEl.insertBefore(body, bodyEl.firstChild);
38377         return body;
38378     },
38379     /** @private */
38380     createStripElements :  function(stripEl, text, closable, tpl)
38381     {
38382         var td = document.createElement("li"); // was td..
38383         td.className = 'nav-item';
38384         
38385         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38386         
38387         
38388         stripEl.appendChild(td);
38389         /*if(closable){
38390             td.className = "x-tabs-closable";
38391             if(!this.closeTpl){
38392                 this.closeTpl = new Roo.Template(
38393                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38394                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38395                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38396                 );
38397             }
38398             var el = this.closeTpl.overwrite(td, {"text": text});
38399             var close = el.getElementsByTagName("div")[0];
38400             var inner = el.getElementsByTagName("em")[0];
38401             return {"el": el, "close": close, "inner": inner};
38402         } else {
38403         */
38404         // not sure what this is..
38405 //            if(!this.tabTpl){
38406                 //this.tabTpl = new Roo.Template(
38407                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38408                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38409                 //);
38410 //                this.tabTpl = new Roo.Template(
38411 //                   '<a href="#">' +
38412 //                   '<span unselectable="on"' +
38413 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38414 //                            ' >{text}</span></a>'
38415 //                );
38416 //                
38417 //            }
38418
38419
38420             var template = tpl || this.tabTpl || false;
38421             
38422             if(!template){
38423                 template =  new Roo.Template(
38424                         Roo.bootstrap.version == 4 ? 
38425                             (
38426                                 '<a class="nav-link" href="#" unselectable="on"' +
38427                                      (this.disableTooltips ? '' : ' title="{text}"') +
38428                                      ' >{text}</a>'
38429                             ) : (
38430                                 '<a class="nav-link" href="#">' +
38431                                 '<span unselectable="on"' +
38432                                          (this.disableTooltips ? '' : ' title="{text}"') +
38433                                     ' >{text}</span></a>'
38434                             )
38435                 );
38436             }
38437             
38438             switch (typeof(template)) {
38439                 case 'object' :
38440                     break;
38441                 case 'string' :
38442                     template = new Roo.Template(template);
38443                     break;
38444                 default :
38445                     break;
38446             }
38447             
38448             var el = template.overwrite(td, {"text": text});
38449             
38450             var inner = el.getElementsByTagName("span")[0];
38451             
38452             return {"el": el, "inner": inner};
38453             
38454     }
38455         
38456     
38457 });
38458
38459 /**
38460  * @class Roo.TabPanelItem
38461  * @extends Roo.util.Observable
38462  * Represents an individual item (tab plus body) in a TabPanel.
38463  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38464  * @param {String} id The id of this TabPanelItem
38465  * @param {String} text The text for the tab of this TabPanelItem
38466  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38467  */
38468 Roo.bootstrap.panel.TabItem = function(config){
38469     /**
38470      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38471      * @type Roo.TabPanel
38472      */
38473     this.tabPanel = config.panel;
38474     /**
38475      * The id for this TabPanelItem
38476      * @type String
38477      */
38478     this.id = config.id;
38479     /** @private */
38480     this.disabled = false;
38481     /** @private */
38482     this.text = config.text;
38483     /** @private */
38484     this.loaded = false;
38485     this.closable = config.closable;
38486
38487     /**
38488      * The body element for this TabPanelItem.
38489      * @type Roo.Element
38490      */
38491     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38492     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38493     this.bodyEl.setStyle("display", "block");
38494     this.bodyEl.setStyle("zoom", "1");
38495     //this.hideAction();
38496
38497     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38498     /** @private */
38499     this.el = Roo.get(els.el);
38500     this.inner = Roo.get(els.inner, true);
38501      this.textEl = Roo.bootstrap.version == 4 ?
38502         this.el : Roo.get(this.el.dom.firstChild, true);
38503
38504     this.linode = Roo.get(els.el.parentNode, true);
38505     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38506
38507     
38508 //    this.el.on("mousedown", this.onTabMouseDown, this);
38509     this.el.on("click", this.onTabClick, this);
38510     /** @private */
38511     if(config.closable){
38512         var c = Roo.get(els.close, true);
38513         c.dom.title = this.closeText;
38514         c.addClassOnOver("close-over");
38515         c.on("click", this.closeClick, this);
38516      }
38517
38518     this.addEvents({
38519          /**
38520          * @event activate
38521          * Fires when this tab becomes the active tab.
38522          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38523          * @param {Roo.TabPanelItem} this
38524          */
38525         "activate": true,
38526         /**
38527          * @event beforeclose
38528          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38529          * @param {Roo.TabPanelItem} this
38530          * @param {Object} e Set cancel to true on this object to cancel the close.
38531          */
38532         "beforeclose": true,
38533         /**
38534          * @event close
38535          * Fires when this tab is closed.
38536          * @param {Roo.TabPanelItem} this
38537          */
38538          "close": true,
38539         /**
38540          * @event deactivate
38541          * Fires when this tab is no longer the active tab.
38542          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38543          * @param {Roo.TabPanelItem} this
38544          */
38545          "deactivate" : true
38546     });
38547     this.hidden = false;
38548
38549     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38550 };
38551
38552 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38553            {
38554     purgeListeners : function(){
38555        Roo.util.Observable.prototype.purgeListeners.call(this);
38556        this.el.removeAllListeners();
38557     },
38558     /**
38559      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38560      */
38561     show : function(){
38562         this.status_node.addClass("active");
38563         this.showAction();
38564         if(Roo.isOpera){
38565             this.tabPanel.stripWrap.repaint();
38566         }
38567         this.fireEvent("activate", this.tabPanel, this);
38568     },
38569
38570     /**
38571      * Returns true if this tab is the active tab.
38572      * @return {Boolean}
38573      */
38574     isActive : function(){
38575         return this.tabPanel.getActiveTab() == this;
38576     },
38577
38578     /**
38579      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38580      */
38581     hide : function(){
38582         this.status_node.removeClass("active");
38583         this.hideAction();
38584         this.fireEvent("deactivate", this.tabPanel, this);
38585     },
38586
38587     hideAction : function(){
38588         this.bodyEl.hide();
38589         this.bodyEl.setStyle("position", "absolute");
38590         this.bodyEl.setLeft("-20000px");
38591         this.bodyEl.setTop("-20000px");
38592     },
38593
38594     showAction : function(){
38595         this.bodyEl.setStyle("position", "relative");
38596         this.bodyEl.setTop("");
38597         this.bodyEl.setLeft("");
38598         this.bodyEl.show();
38599     },
38600
38601     /**
38602      * Set the tooltip for the tab.
38603      * @param {String} tooltip The tab's tooltip
38604      */
38605     setTooltip : function(text){
38606         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38607             this.textEl.dom.qtip = text;
38608             this.textEl.dom.removeAttribute('title');
38609         }else{
38610             this.textEl.dom.title = text;
38611         }
38612     },
38613
38614     onTabClick : function(e){
38615         e.preventDefault();
38616         this.tabPanel.activate(this.id);
38617     },
38618
38619     onTabMouseDown : function(e){
38620         e.preventDefault();
38621         this.tabPanel.activate(this.id);
38622     },
38623 /*
38624     getWidth : function(){
38625         return this.inner.getWidth();
38626     },
38627
38628     setWidth : function(width){
38629         var iwidth = width - this.linode.getPadding("lr");
38630         this.inner.setWidth(iwidth);
38631         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38632         this.linode.setWidth(width);
38633     },
38634 */
38635     /**
38636      * Show or hide the tab
38637      * @param {Boolean} hidden True to hide or false to show.
38638      */
38639     setHidden : function(hidden){
38640         this.hidden = hidden;
38641         this.linode.setStyle("display", hidden ? "none" : "");
38642     },
38643
38644     /**
38645      * Returns true if this tab is "hidden"
38646      * @return {Boolean}
38647      */
38648     isHidden : function(){
38649         return this.hidden;
38650     },
38651
38652     /**
38653      * Returns the text for this tab
38654      * @return {String}
38655      */
38656     getText : function(){
38657         return this.text;
38658     },
38659     /*
38660     autoSize : function(){
38661         //this.el.beginMeasure();
38662         this.textEl.setWidth(1);
38663         /*
38664          *  #2804 [new] Tabs in Roojs
38665          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38666          */
38667         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38668         //this.el.endMeasure();
38669     //},
38670
38671     /**
38672      * Sets the text for the tab (Note: this also sets the tooltip text)
38673      * @param {String} text The tab's text and tooltip
38674      */
38675     setText : function(text){
38676         this.text = text;
38677         this.textEl.update(text);
38678         this.setTooltip(text);
38679         //if(!this.tabPanel.resizeTabs){
38680         //    this.autoSize();
38681         //}
38682     },
38683     /**
38684      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38685      */
38686     activate : function(){
38687         this.tabPanel.activate(this.id);
38688     },
38689
38690     /**
38691      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38692      */
38693     disable : function(){
38694         if(this.tabPanel.active != this){
38695             this.disabled = true;
38696             this.status_node.addClass("disabled");
38697         }
38698     },
38699
38700     /**
38701      * Enables this TabPanelItem if it was previously disabled.
38702      */
38703     enable : function(){
38704         this.disabled = false;
38705         this.status_node.removeClass("disabled");
38706     },
38707
38708     /**
38709      * Sets the content for this TabPanelItem.
38710      * @param {String} content The content
38711      * @param {Boolean} loadScripts true to look for and load scripts
38712      */
38713     setContent : function(content, loadScripts){
38714         this.bodyEl.update(content, loadScripts);
38715     },
38716
38717     /**
38718      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38719      * @return {Roo.UpdateManager} The UpdateManager
38720      */
38721     getUpdateManager : function(){
38722         return this.bodyEl.getUpdateManager();
38723     },
38724
38725     /**
38726      * Set a URL to be used to load the content for this TabPanelItem.
38727      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38728      * @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)
38729      * @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)
38730      * @return {Roo.UpdateManager} The UpdateManager
38731      */
38732     setUrl : function(url, params, loadOnce){
38733         if(this.refreshDelegate){
38734             this.un('activate', this.refreshDelegate);
38735         }
38736         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38737         this.on("activate", this.refreshDelegate);
38738         return this.bodyEl.getUpdateManager();
38739     },
38740
38741     /** @private */
38742     _handleRefresh : function(url, params, loadOnce){
38743         if(!loadOnce || !this.loaded){
38744             var updater = this.bodyEl.getUpdateManager();
38745             updater.update(url, params, this._setLoaded.createDelegate(this));
38746         }
38747     },
38748
38749     /**
38750      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38751      *   Will fail silently if the setUrl method has not been called.
38752      *   This does not activate the panel, just updates its content.
38753      */
38754     refresh : function(){
38755         if(this.refreshDelegate){
38756            this.loaded = false;
38757            this.refreshDelegate();
38758         }
38759     },
38760
38761     /** @private */
38762     _setLoaded : function(){
38763         this.loaded = true;
38764     },
38765
38766     /** @private */
38767     closeClick : function(e){
38768         var o = {};
38769         e.stopEvent();
38770         this.fireEvent("beforeclose", this, o);
38771         if(o.cancel !== true){
38772             this.tabPanel.removeTab(this.id);
38773         }
38774     },
38775     /**
38776      * The text displayed in the tooltip for the close icon.
38777      * @type String
38778      */
38779     closeText : "Close this tab"
38780 });
38781 /**
38782 *    This script refer to:
38783 *    Title: International Telephone Input
38784 *    Author: Jack O'Connor
38785 *    Code version:  v12.1.12
38786 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38787 **/
38788
38789 Roo.bootstrap.PhoneInputData = function() {
38790     var d = [
38791       [
38792         "Afghanistan (‫افغانستان‬‎)",
38793         "af",
38794         "93"
38795       ],
38796       [
38797         "Albania (Shqipëri)",
38798         "al",
38799         "355"
38800       ],
38801       [
38802         "Algeria (‫الجزائر‬‎)",
38803         "dz",
38804         "213"
38805       ],
38806       [
38807         "American Samoa",
38808         "as",
38809         "1684"
38810       ],
38811       [
38812         "Andorra",
38813         "ad",
38814         "376"
38815       ],
38816       [
38817         "Angola",
38818         "ao",
38819         "244"
38820       ],
38821       [
38822         "Anguilla",
38823         "ai",
38824         "1264"
38825       ],
38826       [
38827         "Antigua and Barbuda",
38828         "ag",
38829         "1268"
38830       ],
38831       [
38832         "Argentina",
38833         "ar",
38834         "54"
38835       ],
38836       [
38837         "Armenia (Հայաստան)",
38838         "am",
38839         "374"
38840       ],
38841       [
38842         "Aruba",
38843         "aw",
38844         "297"
38845       ],
38846       [
38847         "Australia",
38848         "au",
38849         "61",
38850         0
38851       ],
38852       [
38853         "Austria (Österreich)",
38854         "at",
38855         "43"
38856       ],
38857       [
38858         "Azerbaijan (Azərbaycan)",
38859         "az",
38860         "994"
38861       ],
38862       [
38863         "Bahamas",
38864         "bs",
38865         "1242"
38866       ],
38867       [
38868         "Bahrain (‫البحرين‬‎)",
38869         "bh",
38870         "973"
38871       ],
38872       [
38873         "Bangladesh (বাংলাদেশ)",
38874         "bd",
38875         "880"
38876       ],
38877       [
38878         "Barbados",
38879         "bb",
38880         "1246"
38881       ],
38882       [
38883         "Belarus (Беларусь)",
38884         "by",
38885         "375"
38886       ],
38887       [
38888         "Belgium (België)",
38889         "be",
38890         "32"
38891       ],
38892       [
38893         "Belize",
38894         "bz",
38895         "501"
38896       ],
38897       [
38898         "Benin (Bénin)",
38899         "bj",
38900         "229"
38901       ],
38902       [
38903         "Bermuda",
38904         "bm",
38905         "1441"
38906       ],
38907       [
38908         "Bhutan (འབྲུག)",
38909         "bt",
38910         "975"
38911       ],
38912       [
38913         "Bolivia",
38914         "bo",
38915         "591"
38916       ],
38917       [
38918         "Bosnia and Herzegovina (Босна и Херцеговина)",
38919         "ba",
38920         "387"
38921       ],
38922       [
38923         "Botswana",
38924         "bw",
38925         "267"
38926       ],
38927       [
38928         "Brazil (Brasil)",
38929         "br",
38930         "55"
38931       ],
38932       [
38933         "British Indian Ocean Territory",
38934         "io",
38935         "246"
38936       ],
38937       [
38938         "British Virgin Islands",
38939         "vg",
38940         "1284"
38941       ],
38942       [
38943         "Brunei",
38944         "bn",
38945         "673"
38946       ],
38947       [
38948         "Bulgaria (България)",
38949         "bg",
38950         "359"
38951       ],
38952       [
38953         "Burkina Faso",
38954         "bf",
38955         "226"
38956       ],
38957       [
38958         "Burundi (Uburundi)",
38959         "bi",
38960         "257"
38961       ],
38962       [
38963         "Cambodia (កម្ពុជា)",
38964         "kh",
38965         "855"
38966       ],
38967       [
38968         "Cameroon (Cameroun)",
38969         "cm",
38970         "237"
38971       ],
38972       [
38973         "Canada",
38974         "ca",
38975         "1",
38976         1,
38977         ["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"]
38978       ],
38979       [
38980         "Cape Verde (Kabu Verdi)",
38981         "cv",
38982         "238"
38983       ],
38984       [
38985         "Caribbean Netherlands",
38986         "bq",
38987         "599",
38988         1
38989       ],
38990       [
38991         "Cayman Islands",
38992         "ky",
38993         "1345"
38994       ],
38995       [
38996         "Central African Republic (République centrafricaine)",
38997         "cf",
38998         "236"
38999       ],
39000       [
39001         "Chad (Tchad)",
39002         "td",
39003         "235"
39004       ],
39005       [
39006         "Chile",
39007         "cl",
39008         "56"
39009       ],
39010       [
39011         "China (中国)",
39012         "cn",
39013         "86"
39014       ],
39015       [
39016         "Christmas Island",
39017         "cx",
39018         "61",
39019         2
39020       ],
39021       [
39022         "Cocos (Keeling) Islands",
39023         "cc",
39024         "61",
39025         1
39026       ],
39027       [
39028         "Colombia",
39029         "co",
39030         "57"
39031       ],
39032       [
39033         "Comoros (‫جزر القمر‬‎)",
39034         "km",
39035         "269"
39036       ],
39037       [
39038         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39039         "cd",
39040         "243"
39041       ],
39042       [
39043         "Congo (Republic) (Congo-Brazzaville)",
39044         "cg",
39045         "242"
39046       ],
39047       [
39048         "Cook Islands",
39049         "ck",
39050         "682"
39051       ],
39052       [
39053         "Costa Rica",
39054         "cr",
39055         "506"
39056       ],
39057       [
39058         "Côte d’Ivoire",
39059         "ci",
39060         "225"
39061       ],
39062       [
39063         "Croatia (Hrvatska)",
39064         "hr",
39065         "385"
39066       ],
39067       [
39068         "Cuba",
39069         "cu",
39070         "53"
39071       ],
39072       [
39073         "Curaçao",
39074         "cw",
39075         "599",
39076         0
39077       ],
39078       [
39079         "Cyprus (Κύπρος)",
39080         "cy",
39081         "357"
39082       ],
39083       [
39084         "Czech Republic (Česká republika)",
39085         "cz",
39086         "420"
39087       ],
39088       [
39089         "Denmark (Danmark)",
39090         "dk",
39091         "45"
39092       ],
39093       [
39094         "Djibouti",
39095         "dj",
39096         "253"
39097       ],
39098       [
39099         "Dominica",
39100         "dm",
39101         "1767"
39102       ],
39103       [
39104         "Dominican Republic (República Dominicana)",
39105         "do",
39106         "1",
39107         2,
39108         ["809", "829", "849"]
39109       ],
39110       [
39111         "Ecuador",
39112         "ec",
39113         "593"
39114       ],
39115       [
39116         "Egypt (‫مصر‬‎)",
39117         "eg",
39118         "20"
39119       ],
39120       [
39121         "El Salvador",
39122         "sv",
39123         "503"
39124       ],
39125       [
39126         "Equatorial Guinea (Guinea Ecuatorial)",
39127         "gq",
39128         "240"
39129       ],
39130       [
39131         "Eritrea",
39132         "er",
39133         "291"
39134       ],
39135       [
39136         "Estonia (Eesti)",
39137         "ee",
39138         "372"
39139       ],
39140       [
39141         "Ethiopia",
39142         "et",
39143         "251"
39144       ],
39145       [
39146         "Falkland Islands (Islas Malvinas)",
39147         "fk",
39148         "500"
39149       ],
39150       [
39151         "Faroe Islands (Føroyar)",
39152         "fo",
39153         "298"
39154       ],
39155       [
39156         "Fiji",
39157         "fj",
39158         "679"
39159       ],
39160       [
39161         "Finland (Suomi)",
39162         "fi",
39163         "358",
39164         0
39165       ],
39166       [
39167         "France",
39168         "fr",
39169         "33"
39170       ],
39171       [
39172         "French Guiana (Guyane française)",
39173         "gf",
39174         "594"
39175       ],
39176       [
39177         "French Polynesia (Polynésie française)",
39178         "pf",
39179         "689"
39180       ],
39181       [
39182         "Gabon",
39183         "ga",
39184         "241"
39185       ],
39186       [
39187         "Gambia",
39188         "gm",
39189         "220"
39190       ],
39191       [
39192         "Georgia (საქართველო)",
39193         "ge",
39194         "995"
39195       ],
39196       [
39197         "Germany (Deutschland)",
39198         "de",
39199         "49"
39200       ],
39201       [
39202         "Ghana (Gaana)",
39203         "gh",
39204         "233"
39205       ],
39206       [
39207         "Gibraltar",
39208         "gi",
39209         "350"
39210       ],
39211       [
39212         "Greece (Ελλάδα)",
39213         "gr",
39214         "30"
39215       ],
39216       [
39217         "Greenland (Kalaallit Nunaat)",
39218         "gl",
39219         "299"
39220       ],
39221       [
39222         "Grenada",
39223         "gd",
39224         "1473"
39225       ],
39226       [
39227         "Guadeloupe",
39228         "gp",
39229         "590",
39230         0
39231       ],
39232       [
39233         "Guam",
39234         "gu",
39235         "1671"
39236       ],
39237       [
39238         "Guatemala",
39239         "gt",
39240         "502"
39241       ],
39242       [
39243         "Guernsey",
39244         "gg",
39245         "44",
39246         1
39247       ],
39248       [
39249         "Guinea (Guinée)",
39250         "gn",
39251         "224"
39252       ],
39253       [
39254         "Guinea-Bissau (Guiné Bissau)",
39255         "gw",
39256         "245"
39257       ],
39258       [
39259         "Guyana",
39260         "gy",
39261         "592"
39262       ],
39263       [
39264         "Haiti",
39265         "ht",
39266         "509"
39267       ],
39268       [
39269         "Honduras",
39270         "hn",
39271         "504"
39272       ],
39273       [
39274         "Hong Kong (香港)",
39275         "hk",
39276         "852"
39277       ],
39278       [
39279         "Hungary (Magyarország)",
39280         "hu",
39281         "36"
39282       ],
39283       [
39284         "Iceland (Ísland)",
39285         "is",
39286         "354"
39287       ],
39288       [
39289         "India (भारत)",
39290         "in",
39291         "91"
39292       ],
39293       [
39294         "Indonesia",
39295         "id",
39296         "62"
39297       ],
39298       [
39299         "Iran (‫ایران‬‎)",
39300         "ir",
39301         "98"
39302       ],
39303       [
39304         "Iraq (‫العراق‬‎)",
39305         "iq",
39306         "964"
39307       ],
39308       [
39309         "Ireland",
39310         "ie",
39311         "353"
39312       ],
39313       [
39314         "Isle of Man",
39315         "im",
39316         "44",
39317         2
39318       ],
39319       [
39320         "Israel (‫ישראל‬‎)",
39321         "il",
39322         "972"
39323       ],
39324       [
39325         "Italy (Italia)",
39326         "it",
39327         "39",
39328         0
39329       ],
39330       [
39331         "Jamaica",
39332         "jm",
39333         "1876"
39334       ],
39335       [
39336         "Japan (日本)",
39337         "jp",
39338         "81"
39339       ],
39340       [
39341         "Jersey",
39342         "je",
39343         "44",
39344         3
39345       ],
39346       [
39347         "Jordan (‫الأردن‬‎)",
39348         "jo",
39349         "962"
39350       ],
39351       [
39352         "Kazakhstan (Казахстан)",
39353         "kz",
39354         "7",
39355         1
39356       ],
39357       [
39358         "Kenya",
39359         "ke",
39360         "254"
39361       ],
39362       [
39363         "Kiribati",
39364         "ki",
39365         "686"
39366       ],
39367       [
39368         "Kosovo",
39369         "xk",
39370         "383"
39371       ],
39372       [
39373         "Kuwait (‫الكويت‬‎)",
39374         "kw",
39375         "965"
39376       ],
39377       [
39378         "Kyrgyzstan (Кыргызстан)",
39379         "kg",
39380         "996"
39381       ],
39382       [
39383         "Laos (ລາວ)",
39384         "la",
39385         "856"
39386       ],
39387       [
39388         "Latvia (Latvija)",
39389         "lv",
39390         "371"
39391       ],
39392       [
39393         "Lebanon (‫لبنان‬‎)",
39394         "lb",
39395         "961"
39396       ],
39397       [
39398         "Lesotho",
39399         "ls",
39400         "266"
39401       ],
39402       [
39403         "Liberia",
39404         "lr",
39405         "231"
39406       ],
39407       [
39408         "Libya (‫ليبيا‬‎)",
39409         "ly",
39410         "218"
39411       ],
39412       [
39413         "Liechtenstein",
39414         "li",
39415         "423"
39416       ],
39417       [
39418         "Lithuania (Lietuva)",
39419         "lt",
39420         "370"
39421       ],
39422       [
39423         "Luxembourg",
39424         "lu",
39425         "352"
39426       ],
39427       [
39428         "Macau (澳門)",
39429         "mo",
39430         "853"
39431       ],
39432       [
39433         "Macedonia (FYROM) (Македонија)",
39434         "mk",
39435         "389"
39436       ],
39437       [
39438         "Madagascar (Madagasikara)",
39439         "mg",
39440         "261"
39441       ],
39442       [
39443         "Malawi",
39444         "mw",
39445         "265"
39446       ],
39447       [
39448         "Malaysia",
39449         "my",
39450         "60"
39451       ],
39452       [
39453         "Maldives",
39454         "mv",
39455         "960"
39456       ],
39457       [
39458         "Mali",
39459         "ml",
39460         "223"
39461       ],
39462       [
39463         "Malta",
39464         "mt",
39465         "356"
39466       ],
39467       [
39468         "Marshall Islands",
39469         "mh",
39470         "692"
39471       ],
39472       [
39473         "Martinique",
39474         "mq",
39475         "596"
39476       ],
39477       [
39478         "Mauritania (‫موريتانيا‬‎)",
39479         "mr",
39480         "222"
39481       ],
39482       [
39483         "Mauritius (Moris)",
39484         "mu",
39485         "230"
39486       ],
39487       [
39488         "Mayotte",
39489         "yt",
39490         "262",
39491         1
39492       ],
39493       [
39494         "Mexico (México)",
39495         "mx",
39496         "52"
39497       ],
39498       [
39499         "Micronesia",
39500         "fm",
39501         "691"
39502       ],
39503       [
39504         "Moldova (Republica Moldova)",
39505         "md",
39506         "373"
39507       ],
39508       [
39509         "Monaco",
39510         "mc",
39511         "377"
39512       ],
39513       [
39514         "Mongolia (Монгол)",
39515         "mn",
39516         "976"
39517       ],
39518       [
39519         "Montenegro (Crna Gora)",
39520         "me",
39521         "382"
39522       ],
39523       [
39524         "Montserrat",
39525         "ms",
39526         "1664"
39527       ],
39528       [
39529         "Morocco (‫المغرب‬‎)",
39530         "ma",
39531         "212",
39532         0
39533       ],
39534       [
39535         "Mozambique (Moçambique)",
39536         "mz",
39537         "258"
39538       ],
39539       [
39540         "Myanmar (Burma) (မြန်မာ)",
39541         "mm",
39542         "95"
39543       ],
39544       [
39545         "Namibia (Namibië)",
39546         "na",
39547         "264"
39548       ],
39549       [
39550         "Nauru",
39551         "nr",
39552         "674"
39553       ],
39554       [
39555         "Nepal (नेपाल)",
39556         "np",
39557         "977"
39558       ],
39559       [
39560         "Netherlands (Nederland)",
39561         "nl",
39562         "31"
39563       ],
39564       [
39565         "New Caledonia (Nouvelle-Calédonie)",
39566         "nc",
39567         "687"
39568       ],
39569       [
39570         "New Zealand",
39571         "nz",
39572         "64"
39573       ],
39574       [
39575         "Nicaragua",
39576         "ni",
39577         "505"
39578       ],
39579       [
39580         "Niger (Nijar)",
39581         "ne",
39582         "227"
39583       ],
39584       [
39585         "Nigeria",
39586         "ng",
39587         "234"
39588       ],
39589       [
39590         "Niue",
39591         "nu",
39592         "683"
39593       ],
39594       [
39595         "Norfolk Island",
39596         "nf",
39597         "672"
39598       ],
39599       [
39600         "North Korea (조선 민주주의 인민 공화국)",
39601         "kp",
39602         "850"
39603       ],
39604       [
39605         "Northern Mariana Islands",
39606         "mp",
39607         "1670"
39608       ],
39609       [
39610         "Norway (Norge)",
39611         "no",
39612         "47",
39613         0
39614       ],
39615       [
39616         "Oman (‫عُمان‬‎)",
39617         "om",
39618         "968"
39619       ],
39620       [
39621         "Pakistan (‫پاکستان‬‎)",
39622         "pk",
39623         "92"
39624       ],
39625       [
39626         "Palau",
39627         "pw",
39628         "680"
39629       ],
39630       [
39631         "Palestine (‫فلسطين‬‎)",
39632         "ps",
39633         "970"
39634       ],
39635       [
39636         "Panama (Panamá)",
39637         "pa",
39638         "507"
39639       ],
39640       [
39641         "Papua New Guinea",
39642         "pg",
39643         "675"
39644       ],
39645       [
39646         "Paraguay",
39647         "py",
39648         "595"
39649       ],
39650       [
39651         "Peru (Perú)",
39652         "pe",
39653         "51"
39654       ],
39655       [
39656         "Philippines",
39657         "ph",
39658         "63"
39659       ],
39660       [
39661         "Poland (Polska)",
39662         "pl",
39663         "48"
39664       ],
39665       [
39666         "Portugal",
39667         "pt",
39668         "351"
39669       ],
39670       [
39671         "Puerto Rico",
39672         "pr",
39673         "1",
39674         3,
39675         ["787", "939"]
39676       ],
39677       [
39678         "Qatar (‫قطر‬‎)",
39679         "qa",
39680         "974"
39681       ],
39682       [
39683         "Réunion (La Réunion)",
39684         "re",
39685         "262",
39686         0
39687       ],
39688       [
39689         "Romania (România)",
39690         "ro",
39691         "40"
39692       ],
39693       [
39694         "Russia (Россия)",
39695         "ru",
39696         "7",
39697         0
39698       ],
39699       [
39700         "Rwanda",
39701         "rw",
39702         "250"
39703       ],
39704       [
39705         "Saint Barthélemy",
39706         "bl",
39707         "590",
39708         1
39709       ],
39710       [
39711         "Saint Helena",
39712         "sh",
39713         "290"
39714       ],
39715       [
39716         "Saint Kitts and Nevis",
39717         "kn",
39718         "1869"
39719       ],
39720       [
39721         "Saint Lucia",
39722         "lc",
39723         "1758"
39724       ],
39725       [
39726         "Saint Martin (Saint-Martin (partie française))",
39727         "mf",
39728         "590",
39729         2
39730       ],
39731       [
39732         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39733         "pm",
39734         "508"
39735       ],
39736       [
39737         "Saint Vincent and the Grenadines",
39738         "vc",
39739         "1784"
39740       ],
39741       [
39742         "Samoa",
39743         "ws",
39744         "685"
39745       ],
39746       [
39747         "San Marino",
39748         "sm",
39749         "378"
39750       ],
39751       [
39752         "São Tomé and Príncipe (São Tomé e Príncipe)",
39753         "st",
39754         "239"
39755       ],
39756       [
39757         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39758         "sa",
39759         "966"
39760       ],
39761       [
39762         "Senegal (Sénégal)",
39763         "sn",
39764         "221"
39765       ],
39766       [
39767         "Serbia (Србија)",
39768         "rs",
39769         "381"
39770       ],
39771       [
39772         "Seychelles",
39773         "sc",
39774         "248"
39775       ],
39776       [
39777         "Sierra Leone",
39778         "sl",
39779         "232"
39780       ],
39781       [
39782         "Singapore",
39783         "sg",
39784         "65"
39785       ],
39786       [
39787         "Sint Maarten",
39788         "sx",
39789         "1721"
39790       ],
39791       [
39792         "Slovakia (Slovensko)",
39793         "sk",
39794         "421"
39795       ],
39796       [
39797         "Slovenia (Slovenija)",
39798         "si",
39799         "386"
39800       ],
39801       [
39802         "Solomon Islands",
39803         "sb",
39804         "677"
39805       ],
39806       [
39807         "Somalia (Soomaaliya)",
39808         "so",
39809         "252"
39810       ],
39811       [
39812         "South Africa",
39813         "za",
39814         "27"
39815       ],
39816       [
39817         "South Korea (대한민국)",
39818         "kr",
39819         "82"
39820       ],
39821       [
39822         "South Sudan (‫جنوب السودان‬‎)",
39823         "ss",
39824         "211"
39825       ],
39826       [
39827         "Spain (España)",
39828         "es",
39829         "34"
39830       ],
39831       [
39832         "Sri Lanka (ශ්‍රී ලංකාව)",
39833         "lk",
39834         "94"
39835       ],
39836       [
39837         "Sudan (‫السودان‬‎)",
39838         "sd",
39839         "249"
39840       ],
39841       [
39842         "Suriname",
39843         "sr",
39844         "597"
39845       ],
39846       [
39847         "Svalbard and Jan Mayen",
39848         "sj",
39849         "47",
39850         1
39851       ],
39852       [
39853         "Swaziland",
39854         "sz",
39855         "268"
39856       ],
39857       [
39858         "Sweden (Sverige)",
39859         "se",
39860         "46"
39861       ],
39862       [
39863         "Switzerland (Schweiz)",
39864         "ch",
39865         "41"
39866       ],
39867       [
39868         "Syria (‫سوريا‬‎)",
39869         "sy",
39870         "963"
39871       ],
39872       [
39873         "Taiwan (台灣)",
39874         "tw",
39875         "886"
39876       ],
39877       [
39878         "Tajikistan",
39879         "tj",
39880         "992"
39881       ],
39882       [
39883         "Tanzania",
39884         "tz",
39885         "255"
39886       ],
39887       [
39888         "Thailand (ไทย)",
39889         "th",
39890         "66"
39891       ],
39892       [
39893         "Timor-Leste",
39894         "tl",
39895         "670"
39896       ],
39897       [
39898         "Togo",
39899         "tg",
39900         "228"
39901       ],
39902       [
39903         "Tokelau",
39904         "tk",
39905         "690"
39906       ],
39907       [
39908         "Tonga",
39909         "to",
39910         "676"
39911       ],
39912       [
39913         "Trinidad and Tobago",
39914         "tt",
39915         "1868"
39916       ],
39917       [
39918         "Tunisia (‫تونس‬‎)",
39919         "tn",
39920         "216"
39921       ],
39922       [
39923         "Turkey (Türkiye)",
39924         "tr",
39925         "90"
39926       ],
39927       [
39928         "Turkmenistan",
39929         "tm",
39930         "993"
39931       ],
39932       [
39933         "Turks and Caicos Islands",
39934         "tc",
39935         "1649"
39936       ],
39937       [
39938         "Tuvalu",
39939         "tv",
39940         "688"
39941       ],
39942       [
39943         "U.S. Virgin Islands",
39944         "vi",
39945         "1340"
39946       ],
39947       [
39948         "Uganda",
39949         "ug",
39950         "256"
39951       ],
39952       [
39953         "Ukraine (Україна)",
39954         "ua",
39955         "380"
39956       ],
39957       [
39958         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39959         "ae",
39960         "971"
39961       ],
39962       [
39963         "United Kingdom",
39964         "gb",
39965         "44",
39966         0
39967       ],
39968       [
39969         "United States",
39970         "us",
39971         "1",
39972         0
39973       ],
39974       [
39975         "Uruguay",
39976         "uy",
39977         "598"
39978       ],
39979       [
39980         "Uzbekistan (Oʻzbekiston)",
39981         "uz",
39982         "998"
39983       ],
39984       [
39985         "Vanuatu",
39986         "vu",
39987         "678"
39988       ],
39989       [
39990         "Vatican City (Città del Vaticano)",
39991         "va",
39992         "39",
39993         1
39994       ],
39995       [
39996         "Venezuela",
39997         "ve",
39998         "58"
39999       ],
40000       [
40001         "Vietnam (Việt Nam)",
40002         "vn",
40003         "84"
40004       ],
40005       [
40006         "Wallis and Futuna (Wallis-et-Futuna)",
40007         "wf",
40008         "681"
40009       ],
40010       [
40011         "Western Sahara (‫الصحراء الغربية‬‎)",
40012         "eh",
40013         "212",
40014         1
40015       ],
40016       [
40017         "Yemen (‫اليمن‬‎)",
40018         "ye",
40019         "967"
40020       ],
40021       [
40022         "Zambia",
40023         "zm",
40024         "260"
40025       ],
40026       [
40027         "Zimbabwe",
40028         "zw",
40029         "263"
40030       ],
40031       [
40032         "Åland Islands",
40033         "ax",
40034         "358",
40035         1
40036       ]
40037   ];
40038   
40039   return d;
40040 }/**
40041 *    This script refer to:
40042 *    Title: International Telephone Input
40043 *    Author: Jack O'Connor
40044 *    Code version:  v12.1.12
40045 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40046 **/
40047
40048 /**
40049  * @class Roo.bootstrap.PhoneInput
40050  * @extends Roo.bootstrap.TriggerField
40051  * An input with International dial-code selection
40052  
40053  * @cfg {String} defaultDialCode default '+852'
40054  * @cfg {Array} preferedCountries default []
40055   
40056  * @constructor
40057  * Create a new PhoneInput.
40058  * @param {Object} config Configuration options
40059  */
40060
40061 Roo.bootstrap.PhoneInput = function(config) {
40062     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40063 };
40064
40065 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40066         
40067         listWidth: undefined,
40068         
40069         selectedClass: 'active',
40070         
40071         invalidClass : "has-warning",
40072         
40073         validClass: 'has-success',
40074         
40075         allowed: '0123456789',
40076         
40077         max_length: 15,
40078         
40079         /**
40080          * @cfg {String} defaultDialCode The default dial code when initializing the input
40081          */
40082         defaultDialCode: '+852',
40083         
40084         /**
40085          * @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
40086          */
40087         preferedCountries: false,
40088         
40089         getAutoCreate : function()
40090         {
40091             var data = Roo.bootstrap.PhoneInputData();
40092             var align = this.labelAlign || this.parentLabelAlign();
40093             var id = Roo.id();
40094             
40095             this.allCountries = [];
40096             this.dialCodeMapping = [];
40097             
40098             for (var i = 0; i < data.length; i++) {
40099               var c = data[i];
40100               this.allCountries[i] = {
40101                 name: c[0],
40102                 iso2: c[1],
40103                 dialCode: c[2],
40104                 priority: c[3] || 0,
40105                 areaCodes: c[4] || null
40106               };
40107               this.dialCodeMapping[c[2]] = {
40108                   name: c[0],
40109                   iso2: c[1],
40110                   priority: c[3] || 0,
40111                   areaCodes: c[4] || null
40112               };
40113             }
40114             
40115             var cfg = {
40116                 cls: 'form-group',
40117                 cn: []
40118             };
40119             
40120             var input =  {
40121                 tag: 'input',
40122                 id : id,
40123                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40124                 maxlength: this.max_length,
40125                 cls : 'form-control tel-input',
40126                 autocomplete: 'new-password'
40127             };
40128             
40129             var hiddenInput = {
40130                 tag: 'input',
40131                 type: 'hidden',
40132                 cls: 'hidden-tel-input'
40133             };
40134             
40135             if (this.name) {
40136                 hiddenInput.name = this.name;
40137             }
40138             
40139             if (this.disabled) {
40140                 input.disabled = true;
40141             }
40142             
40143             var flag_container = {
40144                 tag: 'div',
40145                 cls: 'flag-box',
40146                 cn: [
40147                     {
40148                         tag: 'div',
40149                         cls: 'flag'
40150                     },
40151                     {
40152                         tag: 'div',
40153                         cls: 'caret'
40154                     }
40155                 ]
40156             };
40157             
40158             var box = {
40159                 tag: 'div',
40160                 cls: this.hasFeedback ? 'has-feedback' : '',
40161                 cn: [
40162                     hiddenInput,
40163                     input,
40164                     {
40165                         tag: 'input',
40166                         cls: 'dial-code-holder',
40167                         disabled: true
40168                     }
40169                 ]
40170             };
40171             
40172             var container = {
40173                 cls: 'roo-select2-container input-group',
40174                 cn: [
40175                     flag_container,
40176                     box
40177                 ]
40178             };
40179             
40180             if (this.fieldLabel.length) {
40181                 var indicator = {
40182                     tag: 'i',
40183                     tooltip: 'This field is required'
40184                 };
40185                 
40186                 var label = {
40187                     tag: 'label',
40188                     'for':  id,
40189                     cls: 'control-label',
40190                     cn: []
40191                 };
40192                 
40193                 var label_text = {
40194                     tag: 'span',
40195                     html: this.fieldLabel
40196                 };
40197                 
40198                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40199                 label.cn = [
40200                     indicator,
40201                     label_text
40202                 ];
40203                 
40204                 if(this.indicatorpos == 'right') {
40205                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40206                     label.cn = [
40207                         label_text,
40208                         indicator
40209                     ];
40210                 }
40211                 
40212                 if(align == 'left') {
40213                     container = {
40214                         tag: 'div',
40215                         cn: [
40216                             container
40217                         ]
40218                     };
40219                     
40220                     if(this.labelWidth > 12){
40221                         label.style = "width: " + this.labelWidth + 'px';
40222                     }
40223                     if(this.labelWidth < 13 && this.labelmd == 0){
40224                         this.labelmd = this.labelWidth;
40225                     }
40226                     if(this.labellg > 0){
40227                         label.cls += ' col-lg-' + this.labellg;
40228                         input.cls += ' col-lg-' + (12 - this.labellg);
40229                     }
40230                     if(this.labelmd > 0){
40231                         label.cls += ' col-md-' + this.labelmd;
40232                         container.cls += ' col-md-' + (12 - this.labelmd);
40233                     }
40234                     if(this.labelsm > 0){
40235                         label.cls += ' col-sm-' + this.labelsm;
40236                         container.cls += ' col-sm-' + (12 - this.labelsm);
40237                     }
40238                     if(this.labelxs > 0){
40239                         label.cls += ' col-xs-' + this.labelxs;
40240                         container.cls += ' col-xs-' + (12 - this.labelxs);
40241                     }
40242                 }
40243             }
40244             
40245             cfg.cn = [
40246                 label,
40247                 container
40248             ];
40249             
40250             var settings = this;
40251             
40252             ['xs','sm','md','lg'].map(function(size){
40253                 if (settings[size]) {
40254                     cfg.cls += ' col-' + size + '-' + settings[size];
40255                 }
40256             });
40257             
40258             this.store = new Roo.data.Store({
40259                 proxy : new Roo.data.MemoryProxy({}),
40260                 reader : new Roo.data.JsonReader({
40261                     fields : [
40262                         {
40263                             'name' : 'name',
40264                             'type' : 'string'
40265                         },
40266                         {
40267                             'name' : 'iso2',
40268                             'type' : 'string'
40269                         },
40270                         {
40271                             'name' : 'dialCode',
40272                             'type' : 'string'
40273                         },
40274                         {
40275                             'name' : 'priority',
40276                             'type' : 'string'
40277                         },
40278                         {
40279                             'name' : 'areaCodes',
40280                             'type' : 'string'
40281                         }
40282                     ]
40283                 })
40284             });
40285             
40286             if(!this.preferedCountries) {
40287                 this.preferedCountries = [
40288                     'hk',
40289                     'gb',
40290                     'us'
40291                 ];
40292             }
40293             
40294             var p = this.preferedCountries.reverse();
40295             
40296             if(p) {
40297                 for (var i = 0; i < p.length; i++) {
40298                     for (var j = 0; j < this.allCountries.length; j++) {
40299                         if(this.allCountries[j].iso2 == p[i]) {
40300                             var t = this.allCountries[j];
40301                             this.allCountries.splice(j,1);
40302                             this.allCountries.unshift(t);
40303                         }
40304                     } 
40305                 }
40306             }
40307             
40308             this.store.proxy.data = {
40309                 success: true,
40310                 data: this.allCountries
40311             };
40312             
40313             return cfg;
40314         },
40315         
40316         initEvents : function()
40317         {
40318             this.createList();
40319             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40320             
40321             this.indicator = this.indicatorEl();
40322             this.flag = this.flagEl();
40323             this.dialCodeHolder = this.dialCodeHolderEl();
40324             
40325             this.trigger = this.el.select('div.flag-box',true).first();
40326             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40327             
40328             var _this = this;
40329             
40330             (function(){
40331                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40332                 _this.list.setWidth(lw);
40333             }).defer(100);
40334             
40335             this.list.on('mouseover', this.onViewOver, this);
40336             this.list.on('mousemove', this.onViewMove, this);
40337             this.inputEl().on("keyup", this.onKeyUp, this);
40338             this.inputEl().on("keypress", this.onKeyPress, this);
40339             
40340             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40341
40342             this.view = new Roo.View(this.list, this.tpl, {
40343                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40344             });
40345             
40346             this.view.on('click', this.onViewClick, this);
40347             this.setValue(this.defaultDialCode);
40348         },
40349         
40350         onTriggerClick : function(e)
40351         {
40352             Roo.log('trigger click');
40353             if(this.disabled){
40354                 return;
40355             }
40356             
40357             if(this.isExpanded()){
40358                 this.collapse();
40359                 this.hasFocus = false;
40360             }else {
40361                 this.store.load({});
40362                 this.hasFocus = true;
40363                 this.expand();
40364             }
40365         },
40366         
40367         isExpanded : function()
40368         {
40369             return this.list.isVisible();
40370         },
40371         
40372         collapse : function()
40373         {
40374             if(!this.isExpanded()){
40375                 return;
40376             }
40377             this.list.hide();
40378             Roo.get(document).un('mousedown', this.collapseIf, this);
40379             Roo.get(document).un('mousewheel', this.collapseIf, this);
40380             this.fireEvent('collapse', this);
40381             this.validate();
40382         },
40383         
40384         expand : function()
40385         {
40386             Roo.log('expand');
40387
40388             if(this.isExpanded() || !this.hasFocus){
40389                 return;
40390             }
40391             
40392             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40393             this.list.setWidth(lw);
40394             
40395             this.list.show();
40396             this.restrictHeight();
40397             
40398             Roo.get(document).on('mousedown', this.collapseIf, this);
40399             Roo.get(document).on('mousewheel', this.collapseIf, this);
40400             
40401             this.fireEvent('expand', this);
40402         },
40403         
40404         restrictHeight : function()
40405         {
40406             this.list.alignTo(this.inputEl(), this.listAlign);
40407             this.list.alignTo(this.inputEl(), this.listAlign);
40408         },
40409         
40410         onViewOver : function(e, t)
40411         {
40412             if(this.inKeyMode){
40413                 return;
40414             }
40415             var item = this.view.findItemFromChild(t);
40416             
40417             if(item){
40418                 var index = this.view.indexOf(item);
40419                 this.select(index, false);
40420             }
40421         },
40422
40423         // private
40424         onViewClick : function(view, doFocus, el, e)
40425         {
40426             var index = this.view.getSelectedIndexes()[0];
40427             
40428             var r = this.store.getAt(index);
40429             
40430             if(r){
40431                 this.onSelect(r, index);
40432             }
40433             if(doFocus !== false && !this.blockFocus){
40434                 this.inputEl().focus();
40435             }
40436         },
40437         
40438         onViewMove : function(e, t)
40439         {
40440             this.inKeyMode = false;
40441         },
40442         
40443         select : function(index, scrollIntoView)
40444         {
40445             this.selectedIndex = index;
40446             this.view.select(index);
40447             if(scrollIntoView !== false){
40448                 var el = this.view.getNode(index);
40449                 if(el){
40450                     this.list.scrollChildIntoView(el, false);
40451                 }
40452             }
40453         },
40454         
40455         createList : function()
40456         {
40457             this.list = Roo.get(document.body).createChild({
40458                 tag: 'ul',
40459                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40460                 style: 'display:none'
40461             });
40462             
40463             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40464         },
40465         
40466         collapseIf : function(e)
40467         {
40468             var in_combo  = e.within(this.el);
40469             var in_list =  e.within(this.list);
40470             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40471             
40472             if (in_combo || in_list || is_list) {
40473                 return;
40474             }
40475             this.collapse();
40476         },
40477         
40478         onSelect : function(record, index)
40479         {
40480             if(this.fireEvent('beforeselect', this, record, index) !== false){
40481                 
40482                 this.setFlagClass(record.data.iso2);
40483                 this.setDialCode(record.data.dialCode);
40484                 this.hasFocus = false;
40485                 this.collapse();
40486                 this.fireEvent('select', this, record, index);
40487             }
40488         },
40489         
40490         flagEl : function()
40491         {
40492             var flag = this.el.select('div.flag',true).first();
40493             if(!flag){
40494                 return false;
40495             }
40496             return flag;
40497         },
40498         
40499         dialCodeHolderEl : function()
40500         {
40501             var d = this.el.select('input.dial-code-holder',true).first();
40502             if(!d){
40503                 return false;
40504             }
40505             return d;
40506         },
40507         
40508         setDialCode : function(v)
40509         {
40510             this.dialCodeHolder.dom.value = '+'+v;
40511         },
40512         
40513         setFlagClass : function(n)
40514         {
40515             this.flag.dom.className = 'flag '+n;
40516         },
40517         
40518         getValue : function()
40519         {
40520             var v = this.inputEl().getValue();
40521             if(this.dialCodeHolder) {
40522                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40523             }
40524             return v;
40525         },
40526         
40527         setValue : function(v)
40528         {
40529             var d = this.getDialCode(v);
40530             
40531             //invalid dial code
40532             if(v.length == 0 || !d || d.length == 0) {
40533                 if(this.rendered){
40534                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40535                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40536                 }
40537                 return;
40538             }
40539             
40540             //valid dial code
40541             this.setFlagClass(this.dialCodeMapping[d].iso2);
40542             this.setDialCode(d);
40543             this.inputEl().dom.value = v.replace('+'+d,'');
40544             this.hiddenEl().dom.value = this.getValue();
40545             
40546             this.validate();
40547         },
40548         
40549         getDialCode : function(v)
40550         {
40551             v = v ||  '';
40552             
40553             if (v.length == 0) {
40554                 return this.dialCodeHolder.dom.value;
40555             }
40556             
40557             var dialCode = "";
40558             if (v.charAt(0) != "+") {
40559                 return false;
40560             }
40561             var numericChars = "";
40562             for (var i = 1; i < v.length; i++) {
40563               var c = v.charAt(i);
40564               if (!isNaN(c)) {
40565                 numericChars += c;
40566                 if (this.dialCodeMapping[numericChars]) {
40567                   dialCode = v.substr(1, i);
40568                 }
40569                 if (numericChars.length == 4) {
40570                   break;
40571                 }
40572               }
40573             }
40574             return dialCode;
40575         },
40576         
40577         reset : function()
40578         {
40579             this.setValue(this.defaultDialCode);
40580             this.validate();
40581         },
40582         
40583         hiddenEl : function()
40584         {
40585             return this.el.select('input.hidden-tel-input',true).first();
40586         },
40587         
40588         // after setting val
40589         onKeyUp : function(e){
40590             this.setValue(this.getValue());
40591         },
40592         
40593         onKeyPress : function(e){
40594             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40595                 e.stopEvent();
40596             }
40597         }
40598         
40599 });
40600 /**
40601  * @class Roo.bootstrap.MoneyField
40602  * @extends Roo.bootstrap.ComboBox
40603  * Bootstrap MoneyField class
40604  * 
40605  * @constructor
40606  * Create a new MoneyField.
40607  * @param {Object} config Configuration options
40608  */
40609
40610 Roo.bootstrap.MoneyField = function(config) {
40611     
40612     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40613     
40614 };
40615
40616 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40617     
40618     /**
40619      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40620      */
40621     allowDecimals : true,
40622     /**
40623      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40624      */
40625     decimalSeparator : ".",
40626     /**
40627      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40628      */
40629     decimalPrecision : 0,
40630     /**
40631      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40632      */
40633     allowNegative : true,
40634     /**
40635      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40636      */
40637     allowZero: true,
40638     /**
40639      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40640      */
40641     minValue : Number.NEGATIVE_INFINITY,
40642     /**
40643      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40644      */
40645     maxValue : Number.MAX_VALUE,
40646     /**
40647      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40648      */
40649     minText : "The minimum value for this field is {0}",
40650     /**
40651      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40652      */
40653     maxText : "The maximum value for this field is {0}",
40654     /**
40655      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40656      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40657      */
40658     nanText : "{0} is not a valid number",
40659     /**
40660      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40661      */
40662     castInt : true,
40663     /**
40664      * @cfg {String} defaults currency of the MoneyField
40665      * value should be in lkey
40666      */
40667     defaultCurrency : false,
40668     /**
40669      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40670      */
40671     thousandsDelimiter : false,
40672     /**
40673      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40674      */
40675     max_length: false,
40676     
40677     inputlg : 9,
40678     inputmd : 9,
40679     inputsm : 9,
40680     inputxs : 6,
40681     
40682     store : false,
40683     
40684     getAutoCreate : function()
40685     {
40686         var align = this.labelAlign || this.parentLabelAlign();
40687         
40688         var id = Roo.id();
40689
40690         var cfg = {
40691             cls: 'form-group',
40692             cn: []
40693         };
40694
40695         var input =  {
40696             tag: 'input',
40697             id : id,
40698             cls : 'form-control roo-money-amount-input',
40699             autocomplete: 'new-password'
40700         };
40701         
40702         var hiddenInput = {
40703             tag: 'input',
40704             type: 'hidden',
40705             id: Roo.id(),
40706             cls: 'hidden-number-input'
40707         };
40708         
40709         if(this.max_length) {
40710             input.maxlength = this.max_length; 
40711         }
40712         
40713         if (this.name) {
40714             hiddenInput.name = this.name;
40715         }
40716
40717         if (this.disabled) {
40718             input.disabled = true;
40719         }
40720
40721         var clg = 12 - this.inputlg;
40722         var cmd = 12 - this.inputmd;
40723         var csm = 12 - this.inputsm;
40724         var cxs = 12 - this.inputxs;
40725         
40726         var container = {
40727             tag : 'div',
40728             cls : 'row roo-money-field',
40729             cn : [
40730                 {
40731                     tag : 'div',
40732                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40733                     cn : [
40734                         {
40735                             tag : 'div',
40736                             cls: 'roo-select2-container input-group',
40737                             cn: [
40738                                 {
40739                                     tag : 'input',
40740                                     cls : 'form-control roo-money-currency-input',
40741                                     autocomplete: 'new-password',
40742                                     readOnly : 1,
40743                                     name : this.currencyName
40744                                 },
40745                                 {
40746                                     tag :'span',
40747                                     cls : 'input-group-addon',
40748                                     cn : [
40749                                         {
40750                                             tag: 'span',
40751                                             cls: 'caret'
40752                                         }
40753                                     ]
40754                                 }
40755                             ]
40756                         }
40757                     ]
40758                 },
40759                 {
40760                     tag : 'div',
40761                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40762                     cn : [
40763                         {
40764                             tag: 'div',
40765                             cls: this.hasFeedback ? 'has-feedback' : '',
40766                             cn: [
40767                                 input
40768                             ]
40769                         }
40770                     ]
40771                 }
40772             ]
40773             
40774         };
40775         
40776         if (this.fieldLabel.length) {
40777             var indicator = {
40778                 tag: 'i',
40779                 tooltip: 'This field is required'
40780             };
40781
40782             var label = {
40783                 tag: 'label',
40784                 'for':  id,
40785                 cls: 'control-label',
40786                 cn: []
40787             };
40788
40789             var label_text = {
40790                 tag: 'span',
40791                 html: this.fieldLabel
40792             };
40793
40794             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40795             label.cn = [
40796                 indicator,
40797                 label_text
40798             ];
40799
40800             if(this.indicatorpos == 'right') {
40801                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40802                 label.cn = [
40803                     label_text,
40804                     indicator
40805                 ];
40806             }
40807
40808             if(align == 'left') {
40809                 container = {
40810                     tag: 'div',
40811                     cn: [
40812                         container
40813                     ]
40814                 };
40815
40816                 if(this.labelWidth > 12){
40817                     label.style = "width: " + this.labelWidth + 'px';
40818                 }
40819                 if(this.labelWidth < 13 && this.labelmd == 0){
40820                     this.labelmd = this.labelWidth;
40821                 }
40822                 if(this.labellg > 0){
40823                     label.cls += ' col-lg-' + this.labellg;
40824                     input.cls += ' col-lg-' + (12 - this.labellg);
40825                 }
40826                 if(this.labelmd > 0){
40827                     label.cls += ' col-md-' + this.labelmd;
40828                     container.cls += ' col-md-' + (12 - this.labelmd);
40829                 }
40830                 if(this.labelsm > 0){
40831                     label.cls += ' col-sm-' + this.labelsm;
40832                     container.cls += ' col-sm-' + (12 - this.labelsm);
40833                 }
40834                 if(this.labelxs > 0){
40835                     label.cls += ' col-xs-' + this.labelxs;
40836                     container.cls += ' col-xs-' + (12 - this.labelxs);
40837                 }
40838             }
40839         }
40840
40841         cfg.cn = [
40842             label,
40843             container,
40844             hiddenInput
40845         ];
40846         
40847         var settings = this;
40848
40849         ['xs','sm','md','lg'].map(function(size){
40850             if (settings[size]) {
40851                 cfg.cls += ' col-' + size + '-' + settings[size];
40852             }
40853         });
40854         
40855         return cfg;
40856     },
40857     
40858     initEvents : function()
40859     {
40860         this.indicator = this.indicatorEl();
40861         
40862         this.initCurrencyEvent();
40863         
40864         this.initNumberEvent();
40865     },
40866     
40867     initCurrencyEvent : function()
40868     {
40869         if (!this.store) {
40870             throw "can not find store for combo";
40871         }
40872         
40873         this.store = Roo.factory(this.store, Roo.data);
40874         this.store.parent = this;
40875         
40876         this.createList();
40877         
40878         this.triggerEl = this.el.select('.input-group-addon', true).first();
40879         
40880         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40881         
40882         var _this = this;
40883         
40884         (function(){
40885             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40886             _this.list.setWidth(lw);
40887         }).defer(100);
40888         
40889         this.list.on('mouseover', this.onViewOver, this);
40890         this.list.on('mousemove', this.onViewMove, this);
40891         this.list.on('scroll', this.onViewScroll, this);
40892         
40893         if(!this.tpl){
40894             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40895         }
40896         
40897         this.view = new Roo.View(this.list, this.tpl, {
40898             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40899         });
40900         
40901         this.view.on('click', this.onViewClick, this);
40902         
40903         this.store.on('beforeload', this.onBeforeLoad, this);
40904         this.store.on('load', this.onLoad, this);
40905         this.store.on('loadexception', this.onLoadException, this);
40906         
40907         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40908             "up" : function(e){
40909                 this.inKeyMode = true;
40910                 this.selectPrev();
40911             },
40912
40913             "down" : function(e){
40914                 if(!this.isExpanded()){
40915                     this.onTriggerClick();
40916                 }else{
40917                     this.inKeyMode = true;
40918                     this.selectNext();
40919                 }
40920             },
40921
40922             "enter" : function(e){
40923                 this.collapse();
40924                 
40925                 if(this.fireEvent("specialkey", this, e)){
40926                     this.onViewClick(false);
40927                 }
40928                 
40929                 return true;
40930             },
40931
40932             "esc" : function(e){
40933                 this.collapse();
40934             },
40935
40936             "tab" : function(e){
40937                 this.collapse();
40938                 
40939                 if(this.fireEvent("specialkey", this, e)){
40940                     this.onViewClick(false);
40941                 }
40942                 
40943                 return true;
40944             },
40945
40946             scope : this,
40947
40948             doRelay : function(foo, bar, hname){
40949                 if(hname == 'down' || this.scope.isExpanded()){
40950                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40951                 }
40952                 return true;
40953             },
40954
40955             forceKeyDown: true
40956         });
40957         
40958         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40959         
40960     },
40961     
40962     initNumberEvent : function(e)
40963     {
40964         this.inputEl().on("keydown" , this.fireKey,  this);
40965         this.inputEl().on("focus", this.onFocus,  this);
40966         this.inputEl().on("blur", this.onBlur,  this);
40967         
40968         this.inputEl().relayEvent('keyup', this);
40969         
40970         if(this.indicator){
40971             this.indicator.addClass('invisible');
40972         }
40973  
40974         this.originalValue = this.getValue();
40975         
40976         if(this.validationEvent == 'keyup'){
40977             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40978             this.inputEl().on('keyup', this.filterValidation, this);
40979         }
40980         else if(this.validationEvent !== false){
40981             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40982         }
40983         
40984         if(this.selectOnFocus){
40985             this.on("focus", this.preFocus, this);
40986             
40987         }
40988         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40989             this.inputEl().on("keypress", this.filterKeys, this);
40990         } else {
40991             this.inputEl().relayEvent('keypress', this);
40992         }
40993         
40994         var allowed = "0123456789";
40995         
40996         if(this.allowDecimals){
40997             allowed += this.decimalSeparator;
40998         }
40999         
41000         if(this.allowNegative){
41001             allowed += "-";
41002         }
41003         
41004         if(this.thousandsDelimiter) {
41005             allowed += ",";
41006         }
41007         
41008         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41009         
41010         var keyPress = function(e){
41011             
41012             var k = e.getKey();
41013             
41014             var c = e.getCharCode();
41015             
41016             if(
41017                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41018                     allowed.indexOf(String.fromCharCode(c)) === -1
41019             ){
41020                 e.stopEvent();
41021                 return;
41022             }
41023             
41024             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41025                 return;
41026             }
41027             
41028             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41029                 e.stopEvent();
41030             }
41031         };
41032         
41033         this.inputEl().on("keypress", keyPress, this);
41034         
41035     },
41036     
41037     onTriggerClick : function(e)
41038     {   
41039         if(this.disabled){
41040             return;
41041         }
41042         
41043         this.page = 0;
41044         this.loadNext = false;
41045         
41046         if(this.isExpanded()){
41047             this.collapse();
41048             return;
41049         }
41050         
41051         this.hasFocus = true;
41052         
41053         if(this.triggerAction == 'all') {
41054             this.doQuery(this.allQuery, true);
41055             return;
41056         }
41057         
41058         this.doQuery(this.getRawValue());
41059     },
41060     
41061     getCurrency : function()
41062     {   
41063         var v = this.currencyEl().getValue();
41064         
41065         return v;
41066     },
41067     
41068     restrictHeight : function()
41069     {
41070         this.list.alignTo(this.currencyEl(), this.listAlign);
41071         this.list.alignTo(this.currencyEl(), this.listAlign);
41072     },
41073     
41074     onViewClick : function(view, doFocus, el, e)
41075     {
41076         var index = this.view.getSelectedIndexes()[0];
41077         
41078         var r = this.store.getAt(index);
41079         
41080         if(r){
41081             this.onSelect(r, index);
41082         }
41083     },
41084     
41085     onSelect : function(record, index){
41086         
41087         if(this.fireEvent('beforeselect', this, record, index) !== false){
41088         
41089             this.setFromCurrencyData(index > -1 ? record.data : false);
41090             
41091             this.collapse();
41092             
41093             this.fireEvent('select', this, record, index);
41094         }
41095     },
41096     
41097     setFromCurrencyData : function(o)
41098     {
41099         var currency = '';
41100         
41101         this.lastCurrency = o;
41102         
41103         if (this.currencyField) {
41104             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41105         } else {
41106             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41107         }
41108         
41109         this.lastSelectionText = currency;
41110         
41111         //setting default currency
41112         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41113             this.setCurrency(this.defaultCurrency);
41114             return;
41115         }
41116         
41117         this.setCurrency(currency);
41118     },
41119     
41120     setFromData : function(o)
41121     {
41122         var c = {};
41123         
41124         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41125         
41126         this.setFromCurrencyData(c);
41127         
41128         var value = '';
41129         
41130         if (this.name) {
41131             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41132         } else {
41133             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41134         }
41135         
41136         this.setValue(value);
41137         
41138     },
41139     
41140     setCurrency : function(v)
41141     {   
41142         this.currencyValue = v;
41143         
41144         if(this.rendered){
41145             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41146             this.validate();
41147         }
41148     },
41149     
41150     setValue : function(v)
41151     {
41152         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41153         
41154         this.value = v;
41155         
41156         if(this.rendered){
41157             
41158             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41159             
41160             this.inputEl().dom.value = (v == '') ? '' :
41161                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41162             
41163             if(!this.allowZero && v === '0') {
41164                 this.hiddenEl().dom.value = '';
41165                 this.inputEl().dom.value = '';
41166             }
41167             
41168             this.validate();
41169         }
41170     },
41171     
41172     getRawValue : function()
41173     {
41174         var v = this.inputEl().getValue();
41175         
41176         return v;
41177     },
41178     
41179     getValue : function()
41180     {
41181         return this.fixPrecision(this.parseValue(this.getRawValue()));
41182     },
41183     
41184     parseValue : function(value)
41185     {
41186         if(this.thousandsDelimiter) {
41187             value += "";
41188             r = new RegExp(",", "g");
41189             value = value.replace(r, "");
41190         }
41191         
41192         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41193         return isNaN(value) ? '' : value;
41194         
41195     },
41196     
41197     fixPrecision : function(value)
41198     {
41199         if(this.thousandsDelimiter) {
41200             value += "";
41201             r = new RegExp(",", "g");
41202             value = value.replace(r, "");
41203         }
41204         
41205         var nan = isNaN(value);
41206         
41207         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41208             return nan ? '' : value;
41209         }
41210         return parseFloat(value).toFixed(this.decimalPrecision);
41211     },
41212     
41213     decimalPrecisionFcn : function(v)
41214     {
41215         return Math.floor(v);
41216     },
41217     
41218     validateValue : function(value)
41219     {
41220         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41221             return false;
41222         }
41223         
41224         var num = this.parseValue(value);
41225         
41226         if(isNaN(num)){
41227             this.markInvalid(String.format(this.nanText, value));
41228             return false;
41229         }
41230         
41231         if(num < this.minValue){
41232             this.markInvalid(String.format(this.minText, this.minValue));
41233             return false;
41234         }
41235         
41236         if(num > this.maxValue){
41237             this.markInvalid(String.format(this.maxText, this.maxValue));
41238             return false;
41239         }
41240         
41241         return true;
41242     },
41243     
41244     validate : function()
41245     {
41246         if(this.disabled || this.allowBlank){
41247             this.markValid();
41248             return true;
41249         }
41250         
41251         var currency = this.getCurrency();
41252         
41253         if(this.validateValue(this.getRawValue()) && currency.length){
41254             this.markValid();
41255             return true;
41256         }
41257         
41258         this.markInvalid();
41259         return false;
41260     },
41261     
41262     getName: function()
41263     {
41264         return this.name;
41265     },
41266     
41267     beforeBlur : function()
41268     {
41269         if(!this.castInt){
41270             return;
41271         }
41272         
41273         var v = this.parseValue(this.getRawValue());
41274         
41275         if(v || v == 0){
41276             this.setValue(v);
41277         }
41278     },
41279     
41280     onBlur : function()
41281     {
41282         this.beforeBlur();
41283         
41284         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41285             //this.el.removeClass(this.focusClass);
41286         }
41287         
41288         this.hasFocus = false;
41289         
41290         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41291             this.validate();
41292         }
41293         
41294         var v = this.getValue();
41295         
41296         if(String(v) !== String(this.startValue)){
41297             this.fireEvent('change', this, v, this.startValue);
41298         }
41299         
41300         this.fireEvent("blur", this);
41301     },
41302     
41303     inputEl : function()
41304     {
41305         return this.el.select('.roo-money-amount-input', true).first();
41306     },
41307     
41308     currencyEl : function()
41309     {
41310         return this.el.select('.roo-money-currency-input', true).first();
41311     },
41312     
41313     hiddenEl : function()
41314     {
41315         return this.el.select('input.hidden-number-input',true).first();
41316     }
41317     
41318 });