2b98f6df237b0470bfcee1bb4f84306e89238cf5
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets[0], function(s) {
10                     if (s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })();/*
16  * - LGPL
17  *
18  * base class for bootstrap elements.
19  * 
20  */
21
22 Roo.bootstrap = Roo.bootstrap || {};
23 /**
24  * @class Roo.bootstrap.Component
25  * @extends Roo.Component
26  * Bootstrap Component base class
27  * @cfg {String} cls css class
28  * @cfg {String} style any extra css
29  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
31  * @cfg {string} dataId cutomer id
32  * @cfg {string} name Specifies name attribute
33  * @cfg {string} tooltip  Text for the tooltip
34  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
35  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
36  
37  * @constructor
38  * Do not use directly - it does not do anything..
39  * @param {Object} config The config object
40  */
41
42
43
44 Roo.bootstrap.Component = function(config){
45     Roo.bootstrap.Component.superclass.constructor.call(this, config);
46        
47     this.addEvents({
48         /**
49          * @event childrenrendered
50          * Fires when the children have been rendered..
51          * @param {Roo.bootstrap.Component} this
52          */
53         "childrenrendered" : true
54         
55         
56         
57     });
58     
59     
60 };
61
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
63     
64     
65     allowDomMove : false, // to stop relocations in parent onRender...
66     
67     cls : false,
68     
69     style : false,
70     
71     autoCreate : false,
72     
73     tooltip : null,
74     /**
75      * Initialize Events for the element
76      */
77     initEvents : function() { },
78     
79     xattr : false,
80     
81     parentId : false,
82     
83     can_build_overlaid : true,
84     
85     container_method : false,
86     
87     dataId : false,
88     
89     name : false,
90     
91     parent: function() {
92         // returns the parent component..
93         return Roo.ComponentMgr.get(this.parentId)
94         
95         
96     },
97     
98     // private
99     onRender : function(ct, position)
100     {
101        // Roo.log("Call onRender: " + this.xtype);
102         
103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
104         
105         if(this.el){
106             if (this.el.attr('xtype')) {
107                 this.el.attr('xtypex', this.el.attr('xtype'));
108                 this.el.dom.removeAttribute('xtype');
109                 
110                 this.initEvents();
111             }
112             
113             return;
114         }
115         
116          
117         
118         var cfg = Roo.apply({},  this.getAutoCreate());
119         
120         cfg.id = this.id || Roo.id();
121         
122         // fill in the extra attributes 
123         if (this.xattr && typeof(this.xattr) =='object') {
124             for (var i in this.xattr) {
125                 cfg[i] = this.xattr[i];
126             }
127         }
128         
129         if(this.dataId){
130             cfg.dataId = this.dataId;
131         }
132         
133         if (this.cls) {
134             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
135         }
136         
137         if (this.style) { // fixme needs to support more complex style data.
138             cfg.style = this.style;
139         }
140         
141         if(this.name){
142             cfg.name = this.name;
143         }
144         
145         this.el = ct.createChild(cfg, position);
146         
147         if (this.tooltip) {
148             this.tooltipEl().attr('tooltip', this.tooltip);
149         }
150         
151         if(this.tabIndex !== undefined){
152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
153         }
154         
155         this.initEvents();
156         
157     },
158     /**
159      * Fetch the element to add children to
160      * @return {Roo.Element} defaults to this.el
161      */
162     getChildContainer : function()
163     {
164         return this.el;
165     },
166     /**
167      * Fetch the element to display the tooltip on.
168      * @return {Roo.Element} defaults to this.el
169      */
170     tooltipEl : function()
171     {
172         return this.el;
173     },
174         
175     addxtype  : function(tree,cntr)
176     {
177         var cn = this;
178         
179         cn = Roo.factory(tree);
180         //Roo.log(['addxtype', cn]);
181            
182         cn.parentType = this.xtype; //??
183         cn.parentId = this.id;
184         
185         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186         if (typeof(cn.container_method) == 'string') {
187             cntr = cn.container_method;
188         }
189         
190         
191         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
192         
193         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
194         
195         var build_from_html =  Roo.XComponent.build_from_html;
196           
197         var is_body  = (tree.xtype == 'Body') ;
198           
199         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
200           
201         var self_cntr_el = Roo.get(this[cntr](false));
202         
203         // do not try and build conditional elements 
204         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
205             return false;
206         }
207         
208         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210                 return this.addxtypeChild(tree,cntr, is_body);
211             }
212             
213             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
214                 
215             if(echild){
216                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
217             }
218             
219             Roo.log('skipping render');
220             return cn;
221             
222         }
223         
224         var ret = false;
225         if (!build_from_html) {
226             return false;
227         }
228         
229         // this i think handles overlaying multiple children of the same type
230         // with the sam eelement.. - which might be buggy..
231         while (true) {
232             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
233             
234             if (!echild) {
235                 break;
236             }
237             
238             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
239                 break;
240             }
241             
242             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
243         }
244        
245         return ret;
246     },
247     
248     
249     addxtypeChild : function (tree, cntr, is_body)
250     {
251         Roo.debug && Roo.log('addxtypeChild:' + cntr);
252         var cn = this;
253         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
254         
255         
256         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257                     (typeof(tree['flexy:foreach']) != 'undefined');
258           
259     
260         
261         skip_children = false;
262         // render the element if it's not BODY.
263         if (!is_body) {
264             
265             // if parent was disabled, then do not try and create the children..
266             if(!this[cntr](true)){
267                 tree.items = [];
268                 return tree;
269             }
270            
271             cn = Roo.factory(tree);
272            
273             cn.parentType = this.xtype; //??
274             cn.parentId = this.id;
275             
276             var build_from_html =  Roo.XComponent.build_from_html;
277             
278             
279             // does the container contain child eleemnts with 'xtype' attributes.
280             // that match this xtype..
281             // note - when we render we create these as well..
282             // so we should check to see if body has xtype set.
283             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
284                
285                 var self_cntr_el = Roo.get(this[cntr](false));
286                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
287                 if (echild) { 
288                     //Roo.log(Roo.XComponent.build_from_html);
289                     //Roo.log("got echild:");
290                     //Roo.log(echild);
291                 }
292                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293                 // and are not displayed -this causes this to use up the wrong element when matching.
294                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
295                 
296                 
297                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
299                   
300                   
301                   
302                     cn.el = echild;
303                   //  Roo.log("GOT");
304                     //echild.dom.removeAttribute('xtype');
305                 } else {
306                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307                     Roo.debug && Roo.log(self_cntr_el);
308                     Roo.debug && Roo.log(echild);
309                     Roo.debug && Roo.log(cn);
310                 }
311             }
312            
313             
314            
315             // if object has flexy:if - then it may or may not be rendered.
316             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
317                 // skip a flexy if element.
318                 Roo.debug && Roo.log('skipping render');
319                 Roo.debug && Roo.log(tree);
320                 if (!cn.el) {
321                     Roo.debug && Roo.log('skipping all children');
322                     skip_children = true;
323                 }
324                 
325              } else {
326                  
327                 // actually if flexy:foreach is found, we really want to create 
328                 // multiple copies here...
329                 //Roo.log('render');
330                 //Roo.log(this[cntr]());
331                 // some elements do not have render methods.. like the layouts...
332                 /*
333                 if(this[cntr](true) === false){
334                     cn.items = [];
335                     return cn;
336                 }
337                 */
338                 cn.render && cn.render(this[cntr](true));
339                 
340              }
341             // then add the element..
342         }
343          
344         // handle the kids..
345         
346         var nitems = [];
347         /*
348         if (typeof (tree.menu) != 'undefined') {
349             tree.menu.parentType = cn.xtype;
350             tree.menu.triggerEl = cn.el;
351             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
352             
353         }
354         */
355         if (!tree.items || !tree.items.length) {
356             cn.items = nitems;
357             //Roo.log(["no children", this]);
358             
359             return cn;
360         }
361          
362         var items = tree.items;
363         delete tree.items;
364         
365         //Roo.log(items.length);
366             // add the items..
367         if (!skip_children) {    
368             for(var i =0;i < items.length;i++) {
369               //  Roo.log(['add child', items[i]]);
370                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
371             }
372         }
373         
374         cn.items = nitems;
375         
376         //Roo.log("fire childrenrendered");
377         
378         cn.fireEvent('childrenrendered', this);
379         
380         return cn;
381     },
382     
383     /**
384      * Set the element that will be used to show or hide
385      */
386     setVisibilityEl : function(el)
387     {
388         this.visibilityEl = el;
389     },
390     
391      /**
392      * Get the element that will be used to show or hide
393      */
394     getVisibilityEl : function()
395     {
396         if (typeof(this.visibilityEl) == 'object') {
397             return this.visibilityEl;
398         }
399         
400         if (typeof(this.visibilityEl) == 'string') {
401             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
402         }
403         
404         return this.getEl();
405     },
406     
407     /**
408      * Show a component - removes 'hidden' class
409      */
410     show : function()
411     {
412         if(!this.getVisibilityEl()){
413             return;
414         }
415          
416         this.getVisibilityEl().removeClass(['hidden','d-none']);
417         
418         this.fireEvent('show', this);
419         
420         
421     },
422     /**
423      * Hide a component - adds 'hidden' class
424      */
425     hide: function()
426     {
427         if(!this.getVisibilityEl()){
428             return;
429         }
430         
431         this.getVisibilityEl().addClass(['hidden','d-none']);
432         
433         this.fireEvent('hide', this);
434         
435     }
436 });
437
438  /*
439  * - LGPL
440  *
441  * Body
442  *
443  */
444
445 /**
446  * @class Roo.bootstrap.Body
447  * @extends Roo.bootstrap.Component
448  * Bootstrap Body class
449  *
450  * @constructor
451  * Create a new body
452  * @param {Object} config The config object
453  */
454
455 Roo.bootstrap.Body = function(config){
456
457     config = config || {};
458
459     Roo.bootstrap.Body.superclass.constructor.call(this, config);
460     this.el = Roo.get(config.el ? config.el : document.body );
461     if (this.cls && this.cls.length) {
462         Roo.get(document.body).addClass(this.cls);
463     }
464 };
465
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
467
468     is_body : true,// just to make sure it's constructed?
469
470         autoCreate : {
471         cls: 'container'
472     },
473     onRender : function(ct, position)
474     {
475        /* Roo.log("Roo.bootstrap.Body - onRender");
476         if (this.cls && this.cls.length) {
477             Roo.get(document.body).addClass(this.cls);
478         }
479         // style??? xttr???
480         */
481     }
482
483
484
485
486 });
487 /*
488  * - LGPL
489  *
490  * button group
491  * 
492  */
493
494
495 /**
496  * @class Roo.bootstrap.ButtonGroup
497  * @extends Roo.bootstrap.Component
498  * Bootstrap ButtonGroup class
499  * @cfg {String} size lg | sm | xs (default empty normal)
500  * @cfg {String} align vertical | justified  (default none)
501  * @cfg {String} direction up | down (default down)
502  * @cfg {Boolean} toolbar false | true
503  * @cfg {Boolean} btn true | false
504  * 
505  * 
506  * @constructor
507  * Create a new Input
508  * @param {Object} config The config object
509  */
510
511 Roo.bootstrap.ButtonGroup = function(config){
512     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
513 };
514
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
516     
517     size: '',
518     align: '',
519     direction: '',
520     toolbar: false,
521     btn: true,
522
523     getAutoCreate : function(){
524         var cfg = {
525             cls: 'btn-group',
526             html : null
527         };
528         
529         cfg.html = this.html || cfg.html;
530         
531         if (this.toolbar) {
532             cfg = {
533                 cls: 'btn-toolbar',
534                 html: null
535             };
536             
537             return cfg;
538         }
539         
540         if (['vertical','justified'].indexOf(this.align)!==-1) {
541             cfg.cls = 'btn-group-' + this.align;
542             
543             if (this.align == 'justified') {
544                 console.log(this.items);
545             }
546         }
547         
548         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549             cfg.cls += ' btn-group-' + this.size;
550         }
551         
552         if (this.direction == 'up') {
553             cfg.cls += ' dropup' ;
554         }
555         
556         return cfg;
557     },
558     /**
559      * Add a button to the group (similar to NavItem API.)
560      */
561     addItem : function(cfg)
562     {
563         var cn = new Roo.bootstrap.Button(cfg);
564         //this.register(cn);
565         cn.parentId = this.id;
566         cn.onRender(this.el, null);
567         return cn;
568     }
569    
570 });
571
572  /*
573  * - LGPL
574  *
575  * button
576  * 
577  */
578
579 /**
580  * @class Roo.bootstrap.Button
581  * @extends Roo.bootstrap.Component
582  * Bootstrap Button class
583  * @cfg {String} html The button content
584  * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
585  * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
586  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
587  * @cfg {String} size ( lg | sm | xs)
588  * @cfg {String} tag ( a | input | submit)
589  * @cfg {String} href empty or href
590  * @cfg {Boolean} disabled default false;
591  * @cfg {Boolean} isClose default false;
592  * @cfg {String} glyphicon depricated - use fa
593  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
594  * @cfg {String} badge text for badge
595  * @cfg {String} theme (default|glow)  
596  * @cfg {Boolean} inverse dark themed version
597  * @cfg {Boolean} toggle is it a slidy toggle button
598  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
599  * @cfg {String} ontext text for on slidy toggle state
600  * @cfg {String} offtext text for off slidy toggle state
601  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
602  * @cfg {Boolean} removeClass remove the standard class..
603  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
604  * 
605  * @constructor
606  * Create a new button
607  * @param {Object} config The config object
608  */
609
610
611 Roo.bootstrap.Button = function(config){
612     Roo.bootstrap.Button.superclass.constructor.call(this, config);
613     this.weightClass = ["btn-default btn-outline-secondary", 
614                        "btn-primary", 
615                        "btn-success", 
616                        "btn-info", 
617                        "btn-warning",
618                        "btn-danger",
619                        "btn-link"
620                       ],  
621     this.addEvents({
622         // raw events
623         /**
624          * @event click
625          * When a butotn is pressed
626          * @param {Roo.bootstrap.Button} btn
627          * @param {Roo.EventObject} e
628          */
629         "click" : true,
630          /**
631          * @event toggle
632          * After the button has been toggles
633          * @param {Roo.bootstrap.Button} btn
634          * @param {Roo.EventObject} e
635          * @param {boolean} pressed (also available as button.pressed)
636          */
637         "toggle" : true
638     });
639 };
640
641 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
642     html: false,
643     active: false,
644     weight: '',
645     badge_weight: '',
646     outline : false,
647     size: '',
648     tag: 'button',
649     href: '',
650     disabled: false,
651     isClose: false,
652     glyphicon: '',
653     fa: '',
654     badge: '',
655     theme: 'default',
656     inverse: false,
657     
658     toggle: false,
659     ontext: 'ON',
660     offtext: 'OFF',
661     defaulton: true,
662     preventDefault: true,
663     removeClass: false,
664     name: false,
665     target: false,
666      
667     pressed : null,
668      
669     
670     getAutoCreate : function(){
671         
672         var cfg = {
673             tag : 'button',
674             cls : 'roo-button',
675             html: ''
676         };
677         
678         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
679             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
680             this.tag = 'button';
681         } else {
682             cfg.tag = this.tag;
683         }
684         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
685         
686         if (this.toggle == true) {
687             cfg={
688                 tag: 'div',
689                 cls: 'slider-frame roo-button',
690                 cn: [
691                     {
692                         tag: 'span',
693                         'data-on-text':'ON',
694                         'data-off-text':'OFF',
695                         cls: 'slider-button',
696                         html: this.offtext
697                     }
698                 ]
699             };
700             
701             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
702                 cfg.cls += ' '+this.weight;
703             }
704             
705             return cfg;
706         }
707         
708         if (this.isClose) {
709             cfg.cls += ' close';
710             
711             cfg["aria-hidden"] = true;
712             
713             cfg.html = "&times;";
714             
715             return cfg;
716         }
717         
718          
719         if (this.theme==='default') {
720             cfg.cls = 'btn roo-button';
721             
722             //if (this.parentType != 'Navbar') {
723             this.weight = this.weight.length ?  this.weight : 'default';
724             //}
725             if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
726                 
727                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
728                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
729                 cfg.cls += ' btn-' + outline + weight;
730                 if (this.weight == 'default') {
731                     // BC
732                     cfg.cls += ' btn-' + this.weight;
733                 }
734             }
735         } else if (this.theme==='glow') {
736             
737             cfg.tag = 'a';
738             cfg.cls = 'btn-glow roo-button';
739             
740             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
741                 
742                 cfg.cls += ' ' + this.weight;
743             }
744         }
745    
746         
747         if (this.inverse) {
748             this.cls += ' inverse';
749         }
750         
751         
752         if (this.active || this.pressed === true) {
753             cfg.cls += ' active';
754         }
755         
756         if (this.disabled) {
757             cfg.disabled = 'disabled';
758         }
759         
760         if (this.items) {
761             Roo.log('changing to ul' );
762             cfg.tag = 'ul';
763             this.glyphicon = 'caret';
764             if (Roo.bootstrap.version == 4) {
765                 this.fa = 'caret-down';
766             }
767             
768         }
769         
770         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
771          
772         //gsRoo.log(this.parentType);
773         if (this.parentType === 'Navbar' && !this.parent().bar) {
774             Roo.log('changing to li?');
775             
776             cfg.tag = 'li';
777             
778             cfg.cls = '';
779             cfg.cn =  [{
780                 tag : 'a',
781                 cls : 'roo-button',
782                 html : this.html,
783                 href : this.href || '#'
784             }];
785             if (this.menu) {
786                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
787                 cfg.cls += ' dropdown';
788             }   
789             
790             delete cfg.html;
791             
792         }
793         
794        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
795         
796         if (this.glyphicon) {
797             cfg.html = ' ' + cfg.html;
798             
799             cfg.cn = [
800                 {
801                     tag: 'span',
802                     cls: 'glyphicon glyphicon-' + this.glyphicon
803                 }
804             ];
805         }
806         if (this.fa) {
807             cfg.html = ' ' + cfg.html;
808             
809             cfg.cn = [
810                 {
811                     tag: 'i',
812                     cls: 'fa fas fa-' + this.fa
813                 }
814             ];
815         }
816         
817         if (this.badge) {
818             cfg.html += ' ';
819             
820             cfg.tag = 'a';
821             
822 //            cfg.cls='btn roo-button';
823             
824             cfg.href=this.href;
825             
826             var value = cfg.html;
827             
828             if(this.glyphicon){
829                 value = {
830                     tag: 'span',
831                     cls: 'glyphicon glyphicon-' + this.glyphicon,
832                     html: this.html
833                 };
834             }
835             if(this.fa){
836                 value = {
837                     tag: 'i',
838                     cls: 'fa fas fa-' + this.fa,
839                     html: this.html
840                 };
841             }
842             
843             var bw = this.badge_weight.length ? this.badge_weight :
844                 (this.weight.length ? this.weight : 'secondary');
845             bw = bw == 'default' ? 'secondary' : bw;
846             
847             cfg.cn = [
848                 value,
849                 {
850                     tag: 'span',
851                     cls: 'badge badge-' + bw,
852                     html: this.badge
853                 }
854             ];
855             
856             cfg.html='';
857         }
858         
859         if (this.menu) {
860             cfg.cls += ' dropdown';
861             cfg.html = typeof(cfg.html) != 'undefined' ?
862                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
863         }
864         
865         if (cfg.tag !== 'a' && this.href !== '') {
866             throw "Tag must be a to set href.";
867         } else if (this.href.length > 0) {
868             cfg.href = this.href;
869         }
870         
871         if(this.removeClass){
872             cfg.cls = '';
873         }
874         
875         if(this.target){
876             cfg.target = this.target;
877         }
878         
879         return cfg;
880     },
881     initEvents: function() {
882        // Roo.log('init events?');
883 //        Roo.log(this.el.dom);
884         // add the menu...
885         
886         if (typeof (this.menu) != 'undefined') {
887             this.menu.parentType = this.xtype;
888             this.menu.triggerEl = this.el;
889             this.addxtype(Roo.apply({}, this.menu));
890         }
891
892
893        if (this.el.hasClass('roo-button')) {
894             this.el.on('click', this.onClick, this);
895        } else {
896             this.el.select('.roo-button').on('click', this.onClick, this);
897        }
898        
899        if(this.removeClass){
900            this.el.on('click', this.onClick, this);
901        }
902        
903        this.el.enableDisplayMode();
904         
905     },
906     onClick : function(e)
907     {
908         if (this.disabled) {
909             return;
910         }
911         
912         Roo.log('button on click ');
913         if(this.preventDefault){
914             e.preventDefault();
915         }
916         
917         if (this.pressed === true || this.pressed === false) {
918             this.toggleActive(e);
919         }
920         
921         
922         this.fireEvent('click', this, e);
923     },
924     
925     /**
926      * Enables this button
927      */
928     enable : function()
929     {
930         this.disabled = false;
931         this.el.removeClass('disabled');
932     },
933     
934     /**
935      * Disable this button
936      */
937     disable : function()
938     {
939         this.disabled = true;
940         this.el.addClass('disabled');
941     },
942      /**
943      * sets the active state on/off, 
944      * @param {Boolean} state (optional) Force a particular state
945      */
946     setActive : function(v) {
947         
948         this.el[v ? 'addClass' : 'removeClass']('active');
949         this.pressed = v;
950     },
951      /**
952      * toggles the current active state 
953      */
954     toggleActive : function(e)
955     {
956         this.setActive(!this.pressed);
957         this.fireEvent('toggle', this, e, !this.pressed);
958     },
959      /**
960      * get the current active state
961      * @return {boolean} true if it's active
962      */
963     isActive : function()
964     {
965         return this.el.hasClass('active');
966     },
967     /**
968      * set the text of the first selected button
969      */
970     setText : function(str)
971     {
972         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
973     },
974     /**
975      * get the text of the first selected button
976      */
977     getText : function()
978     {
979         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
980     },
981     
982     setWeight : function(str)
983     {
984         this.el.removeClass(this.weightClass);
985         this.weight = str;
986         var outline = this.outline ? 'outline-' : '';
987         if (str == 'default') {
988             this.el.addClass('btn-default btn-outline-secondary');        
989             return;
990         }
991         this.el.addClass('btn-' + outline + str);        
992     }
993     
994     
995 });
996
997  /*
998  * - LGPL
999  *
1000  * column
1001  * 
1002  */
1003
1004 /**
1005  * @class Roo.bootstrap.Column
1006  * @extends Roo.bootstrap.Component
1007  * Bootstrap Column class
1008  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1009  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1010  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1011  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1012  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1013  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1014  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1015  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1016  *
1017  * 
1018  * @cfg {Boolean} hidden (true|false) hide the element
1019  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1020  * @cfg {String} fa (ban|check|...) font awesome icon
1021  * @cfg {Number} fasize (1|2|....) font awsome size
1022
1023  * @cfg {String} icon (info-sign|check|...) glyphicon name
1024
1025  * @cfg {String} html content of column.
1026  * 
1027  * @constructor
1028  * Create a new Column
1029  * @param {Object} config The config object
1030  */
1031
1032 Roo.bootstrap.Column = function(config){
1033     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1034 };
1035
1036 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1037     
1038     xs: false,
1039     sm: false,
1040     md: false,
1041     lg: false,
1042     xsoff: false,
1043     smoff: false,
1044     mdoff: false,
1045     lgoff: false,
1046     html: '',
1047     offset: 0,
1048     alert: false,
1049     fa: false,
1050     icon : false,
1051     hidden : false,
1052     fasize : 1,
1053     
1054     getAutoCreate : function(){
1055         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1056         
1057         cfg = {
1058             tag: 'div',
1059             cls: 'column'
1060         };
1061         
1062         var settings=this;
1063         ['xs','sm','md','lg'].map(function(size){
1064             //Roo.log( size + ':' + settings[size]);
1065             
1066             if (settings[size+'off'] !== false) {
1067                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1068             }
1069             
1070             if (settings[size] === false) {
1071                 return;
1072             }
1073             
1074             if (!settings[size]) { // 0 = hidden
1075                 cfg.cls += ' hidden-' + size;
1076                 return;
1077             }
1078             cfg.cls += ' col-' + size + '-' + settings[size];
1079             
1080         });
1081         
1082         if (this.hidden) {
1083             cfg.cls += ' hidden';
1084         }
1085         
1086         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1087             cfg.cls +=' alert alert-' + this.alert;
1088         }
1089         
1090         
1091         if (this.html.length) {
1092             cfg.html = this.html;
1093         }
1094         if (this.fa) {
1095             var fasize = '';
1096             if (this.fasize > 1) {
1097                 fasize = ' fa-' + this.fasize + 'x';
1098             }
1099             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1100             
1101             
1102         }
1103         if (this.icon) {
1104             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1105         }
1106         
1107         return cfg;
1108     }
1109    
1110 });
1111
1112  
1113
1114  /*
1115  * - LGPL
1116  *
1117  * page container.
1118  * 
1119  */
1120
1121
1122 /**
1123  * @class Roo.bootstrap.Container
1124  * @extends Roo.bootstrap.Component
1125  * Bootstrap Container class
1126  * @cfg {Boolean} jumbotron is it a jumbotron element
1127  * @cfg {String} html content of element
1128  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1129  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1130  * @cfg {String} header content of header (for panel)
1131  * @cfg {String} footer content of footer (for panel)
1132  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1133  * @cfg {String} tag (header|aside|section) type of HTML tag.
1134  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1135  * @cfg {String} fa font awesome icon
1136  * @cfg {String} icon (info-sign|check|...) glyphicon name
1137  * @cfg {Boolean} hidden (true|false) hide the element
1138  * @cfg {Boolean} expandable (true|false) default false
1139  * @cfg {Boolean} expanded (true|false) default true
1140  * @cfg {String} rheader contet on the right of header
1141  * @cfg {Boolean} clickable (true|false) default false
1142
1143  *     
1144  * @constructor
1145  * Create a new Container
1146  * @param {Object} config The config object
1147  */
1148
1149 Roo.bootstrap.Container = function(config){
1150     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1151     
1152     this.addEvents({
1153         // raw events
1154          /**
1155          * @event expand
1156          * After the panel has been expand
1157          * 
1158          * @param {Roo.bootstrap.Container} this
1159          */
1160         "expand" : true,
1161         /**
1162          * @event collapse
1163          * After the panel has been collapsed
1164          * 
1165          * @param {Roo.bootstrap.Container} this
1166          */
1167         "collapse" : true,
1168         /**
1169          * @event click
1170          * When a element is chick
1171          * @param {Roo.bootstrap.Container} this
1172          * @param {Roo.EventObject} e
1173          */
1174         "click" : true
1175     });
1176 };
1177
1178 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1179     
1180     jumbotron : false,
1181     well: '',
1182     panel : '',
1183     header: '',
1184     footer : '',
1185     sticky: '',
1186     tag : false,
1187     alert : false,
1188     fa: false,
1189     icon : false,
1190     expandable : false,
1191     rheader : '',
1192     expanded : true,
1193     clickable: false,
1194   
1195      
1196     getChildContainer : function() {
1197         
1198         if(!this.el){
1199             return false;
1200         }
1201         
1202         if (this.panel.length) {
1203             return this.el.select('.panel-body',true).first();
1204         }
1205         
1206         return this.el;
1207     },
1208     
1209     
1210     getAutoCreate : function(){
1211         
1212         var cfg = {
1213             tag : this.tag || 'div',
1214             html : '',
1215             cls : ''
1216         };
1217         if (this.jumbotron) {
1218             cfg.cls = 'jumbotron';
1219         }
1220         
1221         
1222         
1223         // - this is applied by the parent..
1224         //if (this.cls) {
1225         //    cfg.cls = this.cls + '';
1226         //}
1227         
1228         if (this.sticky.length) {
1229             
1230             var bd = Roo.get(document.body);
1231             if (!bd.hasClass('bootstrap-sticky')) {
1232                 bd.addClass('bootstrap-sticky');
1233                 Roo.select('html',true).setStyle('height', '100%');
1234             }
1235              
1236             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1237         }
1238         
1239         
1240         if (this.well.length) {
1241             switch (this.well) {
1242                 case 'lg':
1243                 case 'sm':
1244                     cfg.cls +=' well well-' +this.well;
1245                     break;
1246                 default:
1247                     cfg.cls +=' well';
1248                     break;
1249             }
1250         }
1251         
1252         if (this.hidden) {
1253             cfg.cls += ' hidden';
1254         }
1255         
1256         
1257         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1258             cfg.cls +=' alert alert-' + this.alert;
1259         }
1260         
1261         var body = cfg;
1262         
1263         if (this.panel.length) {
1264             cfg.cls += ' panel panel-' + this.panel;
1265             cfg.cn = [];
1266             if (this.header.length) {
1267                 
1268                 var h = [];
1269                 
1270                 if(this.expandable){
1271                     
1272                     cfg.cls = cfg.cls + ' expandable';
1273                     
1274                     h.push({
1275                         tag: 'i',
1276                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1277                     });
1278                     
1279                 }
1280                 
1281                 h.push(
1282                     {
1283                         tag: 'span',
1284                         cls : 'panel-title',
1285                         html : (this.expandable ? '&nbsp;' : '') + this.header
1286                     },
1287                     {
1288                         tag: 'span',
1289                         cls: 'panel-header-right',
1290                         html: this.rheader
1291                     }
1292                 );
1293                 
1294                 cfg.cn.push({
1295                     cls : 'panel-heading',
1296                     style : this.expandable ? 'cursor: pointer' : '',
1297                     cn : h
1298                 });
1299                 
1300             }
1301             
1302             body = false;
1303             cfg.cn.push({
1304                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1305                 html : this.html
1306             });
1307             
1308             
1309             if (this.footer.length) {
1310                 cfg.cn.push({
1311                     cls : 'panel-footer',
1312                     html : this.footer
1313                     
1314                 });
1315             }
1316             
1317         }
1318         
1319         if (body) {
1320             body.html = this.html || cfg.html;
1321             // prefix with the icons..
1322             if (this.fa) {
1323                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1324             }
1325             if (this.icon) {
1326                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1327             }
1328             
1329             
1330         }
1331         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1332             cfg.cls =  'container';
1333         }
1334         
1335         return cfg;
1336     },
1337     
1338     initEvents: function() 
1339     {
1340         if(this.expandable){
1341             var headerEl = this.headerEl();
1342         
1343             if(headerEl){
1344                 headerEl.on('click', this.onToggleClick, this);
1345             }
1346         }
1347         
1348         if(this.clickable){
1349             this.el.on('click', this.onClick, this);
1350         }
1351         
1352     },
1353     
1354     onToggleClick : function()
1355     {
1356         var headerEl = this.headerEl();
1357         
1358         if(!headerEl){
1359             return;
1360         }
1361         
1362         if(this.expanded){
1363             this.collapse();
1364             return;
1365         }
1366         
1367         this.expand();
1368     },
1369     
1370     expand : function()
1371     {
1372         if(this.fireEvent('expand', this)) {
1373             
1374             this.expanded = true;
1375             
1376             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1377             
1378             this.el.select('.panel-body',true).first().removeClass('hide');
1379             
1380             var toggleEl = this.toggleEl();
1381
1382             if(!toggleEl){
1383                 return;
1384             }
1385
1386             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1387         }
1388         
1389     },
1390     
1391     collapse : function()
1392     {
1393         if(this.fireEvent('collapse', this)) {
1394             
1395             this.expanded = false;
1396             
1397             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1398             this.el.select('.panel-body',true).first().addClass('hide');
1399         
1400             var toggleEl = this.toggleEl();
1401
1402             if(!toggleEl){
1403                 return;
1404             }
1405
1406             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1407         }
1408     },
1409     
1410     toggleEl : function()
1411     {
1412         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1413             return;
1414         }
1415         
1416         return this.el.select('.panel-heading .fa',true).first();
1417     },
1418     
1419     headerEl : function()
1420     {
1421         if(!this.el || !this.panel.length || !this.header.length){
1422             return;
1423         }
1424         
1425         return this.el.select('.panel-heading',true).first()
1426     },
1427     
1428     bodyEl : function()
1429     {
1430         if(!this.el || !this.panel.length){
1431             return;
1432         }
1433         
1434         return this.el.select('.panel-body',true).first()
1435     },
1436     
1437     titleEl : function()
1438     {
1439         if(!this.el || !this.panel.length || !this.header.length){
1440             return;
1441         }
1442         
1443         return this.el.select('.panel-title',true).first();
1444     },
1445     
1446     setTitle : function(v)
1447     {
1448         var titleEl = this.titleEl();
1449         
1450         if(!titleEl){
1451             return;
1452         }
1453         
1454         titleEl.dom.innerHTML = v;
1455     },
1456     
1457     getTitle : function()
1458     {
1459         
1460         var titleEl = this.titleEl();
1461         
1462         if(!titleEl){
1463             return '';
1464         }
1465         
1466         return titleEl.dom.innerHTML;
1467     },
1468     
1469     setRightTitle : function(v)
1470     {
1471         var t = this.el.select('.panel-header-right',true).first();
1472         
1473         if(!t){
1474             return;
1475         }
1476         
1477         t.dom.innerHTML = v;
1478     },
1479     
1480     onClick : function(e)
1481     {
1482         e.preventDefault();
1483         
1484         this.fireEvent('click', this, e);
1485     }
1486 });
1487
1488  /*
1489  * - LGPL
1490  *
1491  * image
1492  * 
1493  */
1494
1495
1496 /**
1497  * @class Roo.bootstrap.Img
1498  * @extends Roo.bootstrap.Component
1499  * Bootstrap Img class
1500  * @cfg {Boolean} imgResponsive false | true
1501  * @cfg {String} border rounded | circle | thumbnail
1502  * @cfg {String} src image source
1503  * @cfg {String} alt image alternative text
1504  * @cfg {String} href a tag href
1505  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1506  * @cfg {String} xsUrl xs image source
1507  * @cfg {String} smUrl sm image source
1508  * @cfg {String} mdUrl md image source
1509  * @cfg {String} lgUrl lg image source
1510  * 
1511  * @constructor
1512  * Create a new Input
1513  * @param {Object} config The config object
1514  */
1515
1516 Roo.bootstrap.Img = function(config){
1517     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1518     
1519     this.addEvents({
1520         // img events
1521         /**
1522          * @event click
1523          * The img click event for the img.
1524          * @param {Roo.EventObject} e
1525          */
1526         "click" : true
1527     });
1528 };
1529
1530 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1531     
1532     imgResponsive: true,
1533     border: '',
1534     src: 'about:blank',
1535     href: false,
1536     target: false,
1537     xsUrl: '',
1538     smUrl: '',
1539     mdUrl: '',
1540     lgUrl: '',
1541
1542     getAutoCreate : function()
1543     {   
1544         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1545             return this.createSingleImg();
1546         }
1547         
1548         var cfg = {
1549             tag: 'div',
1550             cls: 'roo-image-responsive-group',
1551             cn: []
1552         };
1553         var _this = this;
1554         
1555         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1556             
1557             if(!_this[size + 'Url']){
1558                 return;
1559             }
1560             
1561             var img = {
1562                 tag: 'img',
1563                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1564                 html: _this.html || cfg.html,
1565                 src: _this[size + 'Url']
1566             };
1567             
1568             img.cls += ' roo-image-responsive-' + size;
1569             
1570             var s = ['xs', 'sm', 'md', 'lg'];
1571             
1572             s.splice(s.indexOf(size), 1);
1573             
1574             Roo.each(s, function(ss){
1575                 img.cls += ' hidden-' + ss;
1576             });
1577             
1578             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1579                 cfg.cls += ' img-' + _this.border;
1580             }
1581             
1582             if(_this.alt){
1583                 cfg.alt = _this.alt;
1584             }
1585             
1586             if(_this.href){
1587                 var a = {
1588                     tag: 'a',
1589                     href: _this.href,
1590                     cn: [
1591                         img
1592                     ]
1593                 };
1594
1595                 if(this.target){
1596                     a.target = _this.target;
1597                 }
1598             }
1599             
1600             cfg.cn.push((_this.href) ? a : img);
1601             
1602         });
1603         
1604         return cfg;
1605     },
1606     
1607     createSingleImg : function()
1608     {
1609         var cfg = {
1610             tag: 'img',
1611             cls: (this.imgResponsive) ? 'img-responsive' : '',
1612             html : null,
1613             src : 'about:blank'  // just incase src get's set to undefined?!?
1614         };
1615         
1616         cfg.html = this.html || cfg.html;
1617         
1618         cfg.src = this.src || cfg.src;
1619         
1620         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1621             cfg.cls += ' img-' + this.border;
1622         }
1623         
1624         if(this.alt){
1625             cfg.alt = this.alt;
1626         }
1627         
1628         if(this.href){
1629             var a = {
1630                 tag: 'a',
1631                 href: this.href,
1632                 cn: [
1633                     cfg
1634                 ]
1635             };
1636             
1637             if(this.target){
1638                 a.target = this.target;
1639             }
1640             
1641         }
1642         
1643         return (this.href) ? a : cfg;
1644     },
1645     
1646     initEvents: function() 
1647     {
1648         if(!this.href){
1649             this.el.on('click', this.onClick, this);
1650         }
1651         
1652     },
1653     
1654     onClick : function(e)
1655     {
1656         Roo.log('img onclick');
1657         this.fireEvent('click', this, e);
1658     },
1659     /**
1660      * Sets the url of the image - used to update it
1661      * @param {String} url the url of the image
1662      */
1663     
1664     setSrc : function(url)
1665     {
1666         this.src =  url;
1667         
1668         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1669             this.el.dom.src =  url;
1670             return;
1671         }
1672         
1673         this.el.select('img', true).first().dom.src =  url;
1674     }
1675     
1676     
1677    
1678 });
1679
1680  /*
1681  * - LGPL
1682  *
1683  * image
1684  * 
1685  */
1686
1687
1688 /**
1689  * @class Roo.bootstrap.Link
1690  * @extends Roo.bootstrap.Component
1691  * Bootstrap Link Class
1692  * @cfg {String} alt image alternative text
1693  * @cfg {String} href a tag href
1694  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1695  * @cfg {String} html the content of the link.
1696  * @cfg {String} anchor name for the anchor link
1697  * @cfg {String} fa - favicon
1698
1699  * @cfg {Boolean} preventDefault (true | false) default false
1700
1701  * 
1702  * @constructor
1703  * Create a new Input
1704  * @param {Object} config The config object
1705  */
1706
1707 Roo.bootstrap.Link = function(config){
1708     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1709     
1710     this.addEvents({
1711         // img events
1712         /**
1713          * @event click
1714          * The img click event for the img.
1715          * @param {Roo.EventObject} e
1716          */
1717         "click" : true
1718     });
1719 };
1720
1721 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1722     
1723     href: false,
1724     target: false,
1725     preventDefault: false,
1726     anchor : false,
1727     alt : false,
1728     fa: false,
1729
1730
1731     getAutoCreate : function()
1732     {
1733         var html = this.html || '';
1734         
1735         if (this.fa !== false) {
1736             html = '<i class="fa fa-' + this.fa + '"></i>';
1737         }
1738         var cfg = {
1739             tag: 'a'
1740         };
1741         // anchor's do not require html/href...
1742         if (this.anchor === false) {
1743             cfg.html = html;
1744             cfg.href = this.href || '#';
1745         } else {
1746             cfg.name = this.anchor;
1747             if (this.html !== false || this.fa !== false) {
1748                 cfg.html = html;
1749             }
1750             if (this.href !== false) {
1751                 cfg.href = this.href;
1752             }
1753         }
1754         
1755         if(this.alt !== false){
1756             cfg.alt = this.alt;
1757         }
1758         
1759         
1760         if(this.target !== false) {
1761             cfg.target = this.target;
1762         }
1763         
1764         return cfg;
1765     },
1766     
1767     initEvents: function() {
1768         
1769         if(!this.href || this.preventDefault){
1770             this.el.on('click', this.onClick, this);
1771         }
1772     },
1773     
1774     onClick : function(e)
1775     {
1776         if(this.preventDefault){
1777             e.preventDefault();
1778         }
1779         //Roo.log('img onclick');
1780         this.fireEvent('click', this, e);
1781     }
1782    
1783 });
1784
1785  /*
1786  * - LGPL
1787  *
1788  * header
1789  * 
1790  */
1791
1792 /**
1793  * @class Roo.bootstrap.Header
1794  * @extends Roo.bootstrap.Component
1795  * Bootstrap Header class
1796  * @cfg {String} html content of header
1797  * @cfg {Number} level (1|2|3|4|5|6) default 1
1798  * 
1799  * @constructor
1800  * Create a new Header
1801  * @param {Object} config The config object
1802  */
1803
1804
1805 Roo.bootstrap.Header  = function(config){
1806     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1807 };
1808
1809 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1810     
1811     //href : false,
1812     html : false,
1813     level : 1,
1814     
1815     
1816     
1817     getAutoCreate : function(){
1818         
1819         
1820         
1821         var cfg = {
1822             tag: 'h' + (1 *this.level),
1823             html: this.html || ''
1824         } ;
1825         
1826         return cfg;
1827     }
1828    
1829 });
1830
1831  
1832
1833  /*
1834  * Based on:
1835  * Ext JS Library 1.1.1
1836  * Copyright(c) 2006-2007, Ext JS, LLC.
1837  *
1838  * Originally Released Under LGPL - original licence link has changed is not relivant.
1839  *
1840  * Fork - LGPL
1841  * <script type="text/javascript">
1842  */
1843  
1844 /**
1845  * @class Roo.bootstrap.MenuMgr
1846  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1847  * @singleton
1848  */
1849 Roo.bootstrap.MenuMgr = function(){
1850    var menus, active, groups = {}, attached = false, lastShow = new Date();
1851
1852    // private - called when first menu is created
1853    function init(){
1854        menus = {};
1855        active = new Roo.util.MixedCollection();
1856        Roo.get(document).addKeyListener(27, function(){
1857            if(active.length > 0){
1858                hideAll();
1859            }
1860        });
1861    }
1862
1863    // private
1864    function hideAll(){
1865        if(active && active.length > 0){
1866            var c = active.clone();
1867            c.each(function(m){
1868                m.hide();
1869            });
1870        }
1871    }
1872
1873    // private
1874    function onHide(m){
1875        active.remove(m);
1876        if(active.length < 1){
1877            Roo.get(document).un("mouseup", onMouseDown);
1878             
1879            attached = false;
1880        }
1881    }
1882
1883    // private
1884    function onShow(m){
1885        var last = active.last();
1886        lastShow = new Date();
1887        active.add(m);
1888        if(!attached){
1889           Roo.get(document).on("mouseup", onMouseDown);
1890            
1891            attached = true;
1892        }
1893        if(m.parentMenu){
1894           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1895           m.parentMenu.activeChild = m;
1896        }else if(last && last.isVisible()){
1897           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1898        }
1899    }
1900
1901    // private
1902    function onBeforeHide(m){
1903        if(m.activeChild){
1904            m.activeChild.hide();
1905        }
1906        if(m.autoHideTimer){
1907            clearTimeout(m.autoHideTimer);
1908            delete m.autoHideTimer;
1909        }
1910    }
1911
1912    // private
1913    function onBeforeShow(m){
1914        var pm = m.parentMenu;
1915        if(!pm && !m.allowOtherMenus){
1916            hideAll();
1917        }else if(pm && pm.activeChild && active != m){
1918            pm.activeChild.hide();
1919        }
1920    }
1921
1922    // private this should really trigger on mouseup..
1923    function onMouseDown(e){
1924         Roo.log("on Mouse Up");
1925         
1926         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1927             Roo.log("MenuManager hideAll");
1928             hideAll();
1929             e.stopEvent();
1930         }
1931         
1932         
1933    }
1934
1935    // private
1936    function onBeforeCheck(mi, state){
1937        if(state){
1938            var g = groups[mi.group];
1939            for(var i = 0, l = g.length; i < l; i++){
1940                if(g[i] != mi){
1941                    g[i].setChecked(false);
1942                }
1943            }
1944        }
1945    }
1946
1947    return {
1948
1949        /**
1950         * Hides all menus that are currently visible
1951         */
1952        hideAll : function(){
1953             hideAll();  
1954        },
1955
1956        // private
1957        register : function(menu){
1958            if(!menus){
1959                init();
1960            }
1961            menus[menu.id] = menu;
1962            menu.on("beforehide", onBeforeHide);
1963            menu.on("hide", onHide);
1964            menu.on("beforeshow", onBeforeShow);
1965            menu.on("show", onShow);
1966            var g = menu.group;
1967            if(g && menu.events["checkchange"]){
1968                if(!groups[g]){
1969                    groups[g] = [];
1970                }
1971                groups[g].push(menu);
1972                menu.on("checkchange", onCheck);
1973            }
1974        },
1975
1976         /**
1977          * Returns a {@link Roo.menu.Menu} object
1978          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1979          * be used to generate and return a new Menu instance.
1980          */
1981        get : function(menu){
1982            if(typeof menu == "string"){ // menu id
1983                return menus[menu];
1984            }else if(menu.events){  // menu instance
1985                return menu;
1986            }
1987            /*else if(typeof menu.length == 'number'){ // array of menu items?
1988                return new Roo.bootstrap.Menu({items:menu});
1989            }else{ // otherwise, must be a config
1990                return new Roo.bootstrap.Menu(menu);
1991            }
1992            */
1993            return false;
1994        },
1995
1996        // private
1997        unregister : function(menu){
1998            delete menus[menu.id];
1999            menu.un("beforehide", onBeforeHide);
2000            menu.un("hide", onHide);
2001            menu.un("beforeshow", onBeforeShow);
2002            menu.un("show", onShow);
2003            var g = menu.group;
2004            if(g && menu.events["checkchange"]){
2005                groups[g].remove(menu);
2006                menu.un("checkchange", onCheck);
2007            }
2008        },
2009
2010        // private
2011        registerCheckable : function(menuItem){
2012            var g = menuItem.group;
2013            if(g){
2014                if(!groups[g]){
2015                    groups[g] = [];
2016                }
2017                groups[g].push(menuItem);
2018                menuItem.on("beforecheckchange", onBeforeCheck);
2019            }
2020        },
2021
2022        // private
2023        unregisterCheckable : function(menuItem){
2024            var g = menuItem.group;
2025            if(g){
2026                groups[g].remove(menuItem);
2027                menuItem.un("beforecheckchange", onBeforeCheck);
2028            }
2029        }
2030    };
2031 }();/*
2032  * - LGPL
2033  *
2034  * menu
2035  * 
2036  */
2037
2038 /**
2039  * @class Roo.bootstrap.Menu
2040  * @extends Roo.bootstrap.Component
2041  * Bootstrap Menu class - container for MenuItems
2042  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2043  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2044  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2045  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2046  * 
2047  * @constructor
2048  * Create a new Menu
2049  * @param {Object} config The config object
2050  */
2051
2052
2053 Roo.bootstrap.Menu = function(config){
2054     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2055     if (this.registerMenu && this.type != 'treeview')  {
2056         Roo.bootstrap.MenuMgr.register(this);
2057     }
2058     
2059     
2060     this.addEvents({
2061         /**
2062          * @event beforeshow
2063          * Fires before this menu is displayed
2064          * @param {Roo.menu.Menu} this
2065          */
2066         beforeshow : true,
2067         /**
2068          * @event beforehide
2069          * Fires before this menu is hidden
2070          * @param {Roo.menu.Menu} this
2071          */
2072         beforehide : true,
2073         /**
2074          * @event show
2075          * Fires after this menu is displayed
2076          * @param {Roo.menu.Menu} this
2077          */
2078         show : true,
2079         /**
2080          * @event hide
2081          * Fires after this menu is hidden
2082          * @param {Roo.menu.Menu} this
2083          */
2084         hide : true,
2085         /**
2086          * @event click
2087          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2088          * @param {Roo.menu.Menu} this
2089          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2090          * @param {Roo.EventObject} e
2091          */
2092         click : true,
2093         /**
2094          * @event mouseover
2095          * Fires when the mouse is hovering over this menu
2096          * @param {Roo.menu.Menu} this
2097          * @param {Roo.EventObject} e
2098          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2099          */
2100         mouseover : true,
2101         /**
2102          * @event mouseout
2103          * Fires when the mouse exits this menu
2104          * @param {Roo.menu.Menu} this
2105          * @param {Roo.EventObject} e
2106          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2107          */
2108         mouseout : true,
2109         /**
2110          * @event itemclick
2111          * Fires when a menu item contained in this menu is clicked
2112          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2113          * @param {Roo.EventObject} e
2114          */
2115         itemclick: true
2116     });
2117     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2118 };
2119
2120 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2121     
2122    /// html : false,
2123     //align : '',
2124     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2125     type: false,
2126     /**
2127      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2128      */
2129     registerMenu : true,
2130     
2131     menuItems :false, // stores the menu items..
2132     
2133     hidden:true,
2134         
2135     parentMenu : false,
2136     
2137     stopEvent : true,
2138     
2139     isLink : false,
2140     
2141     getChildContainer : function() {
2142         return this.el;  
2143     },
2144     
2145     getAutoCreate : function(){
2146          
2147         //if (['right'].indexOf(this.align)!==-1) {
2148         //    cfg.cn[1].cls += ' pull-right'
2149         //}
2150         
2151         
2152         var cfg = {
2153             tag : 'ul',
2154             cls : 'dropdown-menu' ,
2155             style : 'z-index:1000'
2156             
2157         };
2158         
2159         if (this.type === 'submenu') {
2160             cfg.cls = 'submenu active';
2161         }
2162         if (this.type === 'treeview') {
2163             cfg.cls = 'treeview-menu';
2164         }
2165         
2166         return cfg;
2167     },
2168     initEvents : function() {
2169         
2170        // Roo.log("ADD event");
2171        // Roo.log(this.triggerEl.dom);
2172         
2173         this.triggerEl.on('click', this.onTriggerClick, this);
2174         
2175         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2176         
2177         
2178         if (this.triggerEl.hasClass('nav-item')) {
2179             // dropdown toggle on the 'a' in BS4?
2180             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2181         } else {
2182             this.triggerEl.addClass('dropdown-toggle');
2183         }
2184         if (Roo.isTouch) {
2185             this.el.on('touchstart'  , this.onTouch, this);
2186         }
2187         this.el.on('click' , this.onClick, this);
2188
2189         this.el.on("mouseover", this.onMouseOver, this);
2190         this.el.on("mouseout", this.onMouseOut, this);
2191         
2192     },
2193     
2194     findTargetItem : function(e)
2195     {
2196         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2197         if(!t){
2198             return false;
2199         }
2200         //Roo.log(t);         Roo.log(t.id);
2201         if(t && t.id){
2202             //Roo.log(this.menuitems);
2203             return this.menuitems.get(t.id);
2204             
2205             //return this.items.get(t.menuItemId);
2206         }
2207         
2208         return false;
2209     },
2210     
2211     onTouch : function(e) 
2212     {
2213         Roo.log("menu.onTouch");
2214         //e.stopEvent(); this make the user popdown broken
2215         this.onClick(e);
2216     },
2217     
2218     onClick : function(e)
2219     {
2220         Roo.log("menu.onClick");
2221         
2222         var t = this.findTargetItem(e);
2223         if(!t || t.isContainer){
2224             return;
2225         }
2226         Roo.log(e);
2227         /*
2228         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2229             if(t == this.activeItem && t.shouldDeactivate(e)){
2230                 this.activeItem.deactivate();
2231                 delete this.activeItem;
2232                 return;
2233             }
2234             if(t.canActivate){
2235                 this.setActiveItem(t, true);
2236             }
2237             return;
2238             
2239             
2240         }
2241         */
2242        
2243         Roo.log('pass click event');
2244         
2245         t.onClick(e);
2246         
2247         this.fireEvent("click", this, t, e);
2248         
2249         var _this = this;
2250         
2251         if(!t.href.length || t.href == '#'){
2252             (function() { _this.hide(); }).defer(100);
2253         }
2254         
2255     },
2256     
2257     onMouseOver : function(e){
2258         var t  = this.findTargetItem(e);
2259         //Roo.log(t);
2260         //if(t){
2261         //    if(t.canActivate && !t.disabled){
2262         //        this.setActiveItem(t, true);
2263         //    }
2264         //}
2265         
2266         this.fireEvent("mouseover", this, e, t);
2267     },
2268     isVisible : function(){
2269         return !this.hidden;
2270     },
2271      onMouseOut : function(e){
2272         var t  = this.findTargetItem(e);
2273         
2274         //if(t ){
2275         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2276         //        this.activeItem.deactivate();
2277         //        delete this.activeItem;
2278         //    }
2279         //}
2280         this.fireEvent("mouseout", this, e, t);
2281     },
2282     
2283     
2284     /**
2285      * Displays this menu relative to another element
2286      * @param {String/HTMLElement/Roo.Element} element The element to align to
2287      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2288      * the element (defaults to this.defaultAlign)
2289      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2290      */
2291     show : function(el, pos, parentMenu){
2292         this.parentMenu = parentMenu;
2293         if(!this.el){
2294             this.render();
2295         }
2296         this.fireEvent("beforeshow", this);
2297         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2298     },
2299      /**
2300      * Displays this menu at a specific xy position
2301      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2302      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2303      */
2304     showAt : function(xy, parentMenu, /* private: */_e){
2305         this.parentMenu = parentMenu;
2306         if(!this.el){
2307             this.render();
2308         }
2309         if(_e !== false){
2310             this.fireEvent("beforeshow", this);
2311             //xy = this.el.adjustForConstraints(xy);
2312         }
2313         
2314         //this.el.show();
2315         this.hideMenuItems();
2316         this.hidden = false;
2317         this.triggerEl.addClass('open');
2318         this.el.addClass('show');
2319         
2320         // reassign x when hitting right
2321         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2322             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2323         }
2324         
2325         // reassign y when hitting bottom
2326         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2327             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2328         }
2329         
2330         // but the list may align on trigger left or trigger top... should it be a properity?
2331         
2332         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2333             this.el.setXY(xy);
2334         }
2335         
2336         this.focus();
2337         this.fireEvent("show", this);
2338     },
2339     
2340     focus : function(){
2341         return;
2342         if(!this.hidden){
2343             this.doFocus.defer(50, this);
2344         }
2345     },
2346
2347     doFocus : function(){
2348         if(!this.hidden){
2349             this.focusEl.focus();
2350         }
2351     },
2352
2353     /**
2354      * Hides this menu and optionally all parent menus
2355      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2356      */
2357     hide : function(deep)
2358     {
2359         
2360         this.hideMenuItems();
2361         if(this.el && this.isVisible()){
2362             this.fireEvent("beforehide", this);
2363             if(this.activeItem){
2364                 this.activeItem.deactivate();
2365                 this.activeItem = null;
2366             }
2367             this.triggerEl.removeClass('open');;
2368             this.el.removeClass('show');
2369             this.hidden = true;
2370             this.fireEvent("hide", this);
2371         }
2372         if(deep === true && this.parentMenu){
2373             this.parentMenu.hide(true);
2374         }
2375     },
2376     
2377     onTriggerClick : function(e)
2378     {
2379         Roo.log('trigger click');
2380         
2381         var target = e.getTarget();
2382         
2383         Roo.log(target.nodeName.toLowerCase());
2384         
2385         if(target.nodeName.toLowerCase() === 'i'){
2386             e.preventDefault();
2387         }
2388         
2389     },
2390     
2391     onTriggerPress  : function(e)
2392     {
2393         Roo.log('trigger press');
2394         //Roo.log(e.getTarget());
2395        // Roo.log(this.triggerEl.dom);
2396        
2397         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2398         var pel = Roo.get(e.getTarget());
2399         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2400             Roo.log('is treeview or dropdown?');
2401             return;
2402         }
2403         
2404         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2405             return;
2406         }
2407         
2408         if (this.isVisible()) {
2409             Roo.log('hide');
2410             this.hide();
2411         } else {
2412             Roo.log('show');
2413             this.show(this.triggerEl, '?', false);
2414         }
2415         
2416         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2417             e.stopEvent();
2418         }
2419         
2420     },
2421        
2422     
2423     hideMenuItems : function()
2424     {
2425         Roo.log("hide Menu Items");
2426         if (!this.el) { 
2427             return;
2428         }
2429         //$(backdrop).remove()
2430         this.el.select('.open',true).each(function(aa) {
2431             
2432             aa.removeClass('open');
2433           //var parent = getParent($(this))
2434           //var relatedTarget = { relatedTarget: this }
2435           
2436            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2437           //if (e.isDefaultPrevented()) return
2438            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2439         });
2440     },
2441     addxtypeChild : function (tree, cntr) {
2442         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2443           
2444         this.menuitems.add(comp);
2445         return comp;
2446
2447     },
2448     getEl : function()
2449     {
2450         Roo.log(this.el);
2451         return this.el;
2452     },
2453     
2454     clear : function()
2455     {
2456         this.getEl().dom.innerHTML = '';
2457         this.menuitems.clear();
2458     }
2459 });
2460
2461  
2462  /*
2463  * - LGPL
2464  *
2465  * menu item
2466  * 
2467  */
2468
2469
2470 /**
2471  * @class Roo.bootstrap.MenuItem
2472  * @extends Roo.bootstrap.Component
2473  * Bootstrap MenuItem class
2474  * @cfg {String} html the menu label
2475  * @cfg {String} href the link
2476  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2477  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2478  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2479  * @cfg {String} fa favicon to show on left of menu item.
2480  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2481  * 
2482  * 
2483  * @constructor
2484  * Create a new MenuItem
2485  * @param {Object} config The config object
2486  */
2487
2488
2489 Roo.bootstrap.MenuItem = function(config){
2490     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2491     this.addEvents({
2492         // raw events
2493         /**
2494          * @event click
2495          * The raw click event for the entire grid.
2496          * @param {Roo.bootstrap.MenuItem} this
2497          * @param {Roo.EventObject} e
2498          */
2499         "click" : true
2500     });
2501 };
2502
2503 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2504     
2505     href : false,
2506     html : false,
2507     preventDefault: false,
2508     isContainer : false,
2509     active : false,
2510     fa: false,
2511     
2512     getAutoCreate : function(){
2513         
2514         if(this.isContainer){
2515             return {
2516                 tag: 'li',
2517                 cls: 'dropdown-menu-item '
2518             };
2519         }
2520         var ctag = {
2521             tag: 'span',
2522             html: 'Link'
2523         };
2524         
2525         var anc = {
2526             tag : 'a',
2527             cls : 'dropdown-item',
2528             href : '#',
2529             cn : [  ]
2530         };
2531         
2532         if (this.fa !== false) {
2533             anc.cn.push({
2534                 tag : 'i',
2535                 cls : 'fa fa-' + this.fa
2536             });
2537         }
2538         
2539         anc.cn.push(ctag);
2540         
2541         
2542         var cfg= {
2543             tag: 'li',
2544             cls: 'dropdown-menu-item',
2545             cn: [ anc ]
2546         };
2547         if (this.parent().type == 'treeview') {
2548             cfg.cls = 'treeview-menu';
2549         }
2550         if (this.active) {
2551             cfg.cls += ' active';
2552         }
2553         
2554         
2555         
2556         anc.href = this.href || cfg.cn[0].href ;
2557         ctag.html = this.html || cfg.cn[0].html ;
2558         return cfg;
2559     },
2560     
2561     initEvents: function()
2562     {
2563         if (this.parent().type == 'treeview') {
2564             this.el.select('a').on('click', this.onClick, this);
2565         }
2566         
2567         if (this.menu) {
2568             this.menu.parentType = this.xtype;
2569             this.menu.triggerEl = this.el;
2570             this.menu = this.addxtype(Roo.apply({}, this.menu));
2571         }
2572         
2573     },
2574     onClick : function(e)
2575     {
2576         Roo.log('item on click ');
2577         
2578         if(this.preventDefault){
2579             e.preventDefault();
2580         }
2581         //this.parent().hideMenuItems();
2582         
2583         this.fireEvent('click', this, e);
2584     },
2585     getEl : function()
2586     {
2587         return this.el;
2588     } 
2589 });
2590
2591  
2592
2593  /*
2594  * - LGPL
2595  *
2596  * menu separator
2597  * 
2598  */
2599
2600
2601 /**
2602  * @class Roo.bootstrap.MenuSeparator
2603  * @extends Roo.bootstrap.Component
2604  * Bootstrap MenuSeparator class
2605  * 
2606  * @constructor
2607  * Create a new MenuItem
2608  * @param {Object} config The config object
2609  */
2610
2611
2612 Roo.bootstrap.MenuSeparator = function(config){
2613     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2614 };
2615
2616 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2617     
2618     getAutoCreate : function(){
2619         var cfg = {
2620             cls: 'divider',
2621             tag : 'li'
2622         };
2623         
2624         return cfg;
2625     }
2626    
2627 });
2628
2629  
2630
2631  
2632 /*
2633 * Licence: LGPL
2634 */
2635
2636 /**
2637  * @class Roo.bootstrap.Modal
2638  * @extends Roo.bootstrap.Component
2639  * Bootstrap Modal class
2640  * @cfg {String} title Title of dialog
2641  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2642  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2643  * @cfg {Boolean} specificTitle default false
2644  * @cfg {Array} buttons Array of buttons or standard button set..
2645  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2646  * @cfg {Boolean} animate default true
2647  * @cfg {Boolean} allow_close default true
2648  * @cfg {Boolean} fitwindow default false
2649  * @cfg {String} size (sm|lg) default empty
2650  * @cfg {Number} max_width set the max width of modal
2651  *
2652  *
2653  * @constructor
2654  * Create a new Modal Dialog
2655  * @param {Object} config The config object
2656  */
2657
2658 Roo.bootstrap.Modal = function(config){
2659     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2660     this.addEvents({
2661         // raw events
2662         /**
2663          * @event btnclick
2664          * The raw btnclick event for the button
2665          * @param {Roo.EventObject} e
2666          */
2667         "btnclick" : true,
2668         /**
2669          * @event resize
2670          * Fire when dialog resize
2671          * @param {Roo.bootstrap.Modal} this
2672          * @param {Roo.EventObject} e
2673          */
2674         "resize" : true
2675     });
2676     this.buttons = this.buttons || [];
2677
2678     if (this.tmpl) {
2679         this.tmpl = Roo.factory(this.tmpl);
2680     }
2681
2682 };
2683
2684 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2685
2686     title : 'test dialog',
2687
2688     buttons : false,
2689
2690     // set on load...
2691
2692     html: false,
2693
2694     tmp: false,
2695
2696     specificTitle: false,
2697
2698     buttonPosition: 'right',
2699
2700     allow_close : true,
2701
2702     animate : true,
2703
2704     fitwindow: false,
2705     
2706      // private
2707     dialogEl: false,
2708     bodyEl:  false,
2709     footerEl:  false,
2710     titleEl:  false,
2711     closeEl:  false,
2712
2713     size: '',
2714     
2715     max_width: 0,
2716     
2717     max_height: 0,
2718     
2719     fit_content: false,
2720
2721     onRender : function(ct, position)
2722     {
2723         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2724
2725         if(!this.el){
2726             var cfg = Roo.apply({},  this.getAutoCreate());
2727             cfg.id = Roo.id();
2728             //if(!cfg.name){
2729             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2730             //}
2731             //if (!cfg.name.length) {
2732             //    delete cfg.name;
2733            // }
2734             if (this.cls) {
2735                 cfg.cls += ' ' + this.cls;
2736             }
2737             if (this.style) {
2738                 cfg.style = this.style;
2739             }
2740             this.el = Roo.get(document.body).createChild(cfg, position);
2741         }
2742         //var type = this.el.dom.type;
2743
2744
2745         if(this.tabIndex !== undefined){
2746             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2747         }
2748
2749         this.dialogEl = this.el.select('.modal-dialog',true).first();
2750         this.bodyEl = this.el.select('.modal-body',true).first();
2751         this.closeEl = this.el.select('.modal-header .close', true).first();
2752         this.headerEl = this.el.select('.modal-header',true).first();
2753         this.titleEl = this.el.select('.modal-title',true).first();
2754         this.footerEl = this.el.select('.modal-footer',true).first();
2755
2756         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2757         
2758         //this.el.addClass("x-dlg-modal");
2759
2760         if (this.buttons.length) {
2761             Roo.each(this.buttons, function(bb) {
2762                 var b = Roo.apply({}, bb);
2763                 b.xns = b.xns || Roo.bootstrap;
2764                 b.xtype = b.xtype || 'Button';
2765                 if (typeof(b.listeners) == 'undefined') {
2766                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2767                 }
2768
2769                 var btn = Roo.factory(b);
2770
2771                 btn.render(this.getButtonContainer());
2772
2773             },this);
2774         }
2775         // render the children.
2776         var nitems = [];
2777
2778         if(typeof(this.items) != 'undefined'){
2779             var items = this.items;
2780             delete this.items;
2781
2782             for(var i =0;i < items.length;i++) {
2783                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2784             }
2785         }
2786
2787         this.items = nitems;
2788
2789         // where are these used - they used to be body/close/footer
2790
2791
2792         this.initEvents();
2793         //this.el.addClass([this.fieldClass, this.cls]);
2794
2795     },
2796
2797     getAutoCreate : function()
2798     {
2799         var bdy = {
2800                 cls : 'modal-body',
2801                 html : this.html || ''
2802         };
2803
2804         var title = {
2805             tag: 'h4',
2806             cls : 'modal-title',
2807             html : this.title
2808         };
2809
2810         if(this.specificTitle){
2811             title = this.title;
2812
2813         };
2814
2815         var header = [];
2816         if (this.allow_close && Roo.bootstrap.version == 3) {
2817             header.push({
2818                 tag: 'button',
2819                 cls : 'close',
2820                 html : '&times'
2821             });
2822         }
2823
2824         header.push(title);
2825
2826         if (this.allow_close && Roo.bootstrap.version == 4) {
2827             header.push({
2828                 tag: 'button',
2829                 cls : 'close',
2830                 html : '&times'
2831             });
2832         }
2833         
2834         var size = '';
2835
2836         if(this.size.length){
2837             size = 'modal-' + this.size;
2838         }
2839         
2840         var footer = Roo.bootstrap.version == 3 ?
2841             {
2842                 cls : 'modal-footer',
2843                 cn : [
2844                     {
2845                         tag: 'div',
2846                         cls: 'btn-' + this.buttonPosition
2847                     }
2848                 ]
2849
2850             } :
2851             {  // BS4 uses mr-auto on left buttons....
2852                 cls : 'modal-footer'
2853             };
2854
2855             
2856
2857         
2858         
2859         var modal = {
2860             cls: "modal",
2861              cn : [
2862                 {
2863                     cls: "modal-dialog " + size,
2864                     cn : [
2865                         {
2866                             cls : "modal-content",
2867                             cn : [
2868                                 {
2869                                     cls : 'modal-header',
2870                                     cn : header
2871                                 },
2872                                 bdy,
2873                                 footer
2874                             ]
2875
2876                         }
2877                     ]
2878
2879                 }
2880             ]
2881         };
2882
2883         if(this.animate){
2884             modal.cls += ' fade';
2885         }
2886
2887         return modal;
2888
2889     },
2890     getChildContainer : function() {
2891
2892          return this.bodyEl;
2893
2894     },
2895     getButtonContainer : function() {
2896         
2897          return Roo.bootstrap.version == 4 ?
2898             this.el.select('.modal-footer',true).first()
2899             : this.el.select('.modal-footer div',true).first();
2900
2901     },
2902     initEvents : function()
2903     {
2904         if (this.allow_close) {
2905             this.closeEl.on('click', this.hide, this);
2906         }
2907         Roo.EventManager.onWindowResize(this.resize, this, true);
2908
2909
2910     },
2911
2912     resize : function()
2913     {
2914         this.maskEl.setSize(
2915             Roo.lib.Dom.getViewWidth(true),
2916             Roo.lib.Dom.getViewHeight(true)
2917         );
2918         
2919         if (this.fitwindow) {
2920             this.setSize(
2921                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2922                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2923             );
2924             return;
2925         }
2926         
2927         if(this.max_width !== 0) {
2928             
2929             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2930             
2931             if(this.height) {
2932                 this.setSize(w, this.height);
2933                 return;
2934             }
2935             
2936             if(this.max_height) {
2937                 this.setSize(w,Math.min(
2938                     this.max_height,
2939                     Roo.lib.Dom.getViewportHeight(true) - 60
2940                 ));
2941                 
2942                 return;
2943             }
2944             
2945             if(!this.fit_content) {
2946                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2947                 return;
2948             }
2949             
2950             this.setSize(w, Math.min(
2951                 60 +
2952                 this.headerEl.getHeight() + 
2953                 this.footerEl.getHeight() + 
2954                 this.getChildHeight(this.bodyEl.dom.childNodes),
2955                 Roo.lib.Dom.getViewportHeight(true) - 60)
2956             );
2957         }
2958         
2959     },
2960
2961     setSize : function(w,h)
2962     {
2963         if (!w && !h) {
2964             return;
2965         }
2966         
2967         this.resizeTo(w,h);
2968     },
2969
2970     show : function() {
2971
2972         if (!this.rendered) {
2973             this.render();
2974         }
2975
2976         //this.el.setStyle('display', 'block');
2977         this.el.removeClass('hideing');
2978         this.el.dom.style.display='block';
2979         
2980         Roo.get(document.body).addClass('modal-open');
2981  
2982         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2983             var _this = this;
2984             (function(){
2985                 this.el.addClass('show');
2986                 this.el.addClass('in');
2987             }).defer(50, this);
2988         }else{
2989             this.el.addClass('show');
2990             this.el.addClass('in');
2991         }
2992
2993         // not sure how we can show data in here..
2994         //if (this.tmpl) {
2995         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2996         //}
2997
2998         Roo.get(document.body).addClass("x-body-masked");
2999         
3000         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
3001         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3002         this.maskEl.dom.style.display = 'block';
3003         this.maskEl.addClass('show');
3004         
3005         
3006         this.resize();
3007         
3008         this.fireEvent('show', this);
3009
3010         // set zindex here - otherwise it appears to be ignored...
3011         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3012
3013         (function () {
3014             this.items.forEach( function(e) {
3015                 e.layout ? e.layout() : false;
3016
3017             });
3018         }).defer(100,this);
3019
3020     },
3021     hide : function()
3022     {
3023         if(this.fireEvent("beforehide", this) !== false){
3024             
3025             this.maskEl.removeClass('show');
3026             
3027             this.maskEl.dom.style.display = '';
3028             Roo.get(document.body).removeClass("x-body-masked");
3029             this.el.removeClass('in');
3030             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3031
3032             if(this.animate){ // why
3033                 this.el.addClass('hideing');
3034                 this.el.removeClass('show');
3035                 (function(){
3036                     if (!this.el.hasClass('hideing')) {
3037                         return; // it's been shown again...
3038                     }
3039                     
3040                     this.el.dom.style.display='';
3041
3042                     Roo.get(document.body).removeClass('modal-open');
3043                     this.el.removeClass('hideing');
3044                 }).defer(150,this);
3045                 
3046             }else{
3047                 this.el.removeClass('show');
3048                 this.el.dom.style.display='';
3049                 Roo.get(document.body).removeClass('modal-open');
3050
3051             }
3052             this.fireEvent('hide', this);
3053         }
3054     },
3055     isVisible : function()
3056     {
3057         
3058         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3059         
3060     },
3061
3062     addButton : function(str, cb)
3063     {
3064
3065
3066         var b = Roo.apply({}, { html : str } );
3067         b.xns = b.xns || Roo.bootstrap;
3068         b.xtype = b.xtype || 'Button';
3069         if (typeof(b.listeners) == 'undefined') {
3070             b.listeners = { click : cb.createDelegate(this)  };
3071         }
3072
3073         var btn = Roo.factory(b);
3074
3075         btn.render(this.getButtonContainer());
3076
3077         return btn;
3078
3079     },
3080
3081     setDefaultButton : function(btn)
3082     {
3083         //this.el.select('.modal-footer').()
3084     },
3085     diff : false,
3086
3087     resizeTo: function(w,h)
3088     {
3089         // skip.. ?? why??
3090
3091         this.dialogEl.setWidth(w);
3092         if (this.diff === false) {
3093             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
3094         }
3095
3096         this.bodyEl.setHeight(h - this.diff);
3097
3098         this.fireEvent('resize', this);
3099
3100     },
3101     setContentSize  : function(w, h)
3102     {
3103
3104     },
3105     onButtonClick: function(btn,e)
3106     {
3107         //Roo.log([a,b,c]);
3108         this.fireEvent('btnclick', btn.name, e);
3109     },
3110      /**
3111      * Set the title of the Dialog
3112      * @param {String} str new Title
3113      */
3114     setTitle: function(str) {
3115         this.titleEl.dom.innerHTML = str;
3116     },
3117     /**
3118      * Set the body of the Dialog
3119      * @param {String} str new Title
3120      */
3121     setBody: function(str) {
3122         this.bodyEl.dom.innerHTML = str;
3123     },
3124     /**
3125      * Set the body of the Dialog using the template
3126      * @param {Obj} data - apply this data to the template and replace the body contents.
3127      */
3128     applyBody: function(obj)
3129     {
3130         if (!this.tmpl) {
3131             Roo.log("Error - using apply Body without a template");
3132             //code
3133         }
3134         this.tmpl.overwrite(this.bodyEl, obj);
3135     },
3136     
3137     getChildHeight : function(child_nodes)
3138     {
3139         if(
3140             !child_nodes ||
3141             child_nodes.length == 0
3142         ) {
3143             return;
3144         }
3145         
3146         var child_height = 0;
3147         
3148         for(var i = 0; i < child_nodes.length; i++) {
3149             
3150             /*
3151             * for modal with tabs...
3152             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3153                 
3154                 var layout_childs = child_nodes[i].childNodes;
3155                 
3156                 for(var j = 0; j < layout_childs.length; j++) {
3157                     
3158                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3159                         
3160                         var layout_body_childs = layout_childs[j].childNodes;
3161                         
3162                         for(var k = 0; k < layout_body_childs.length; k++) {
3163                             
3164                             if(layout_body_childs[k].classList.contains('navbar')) {
3165                                 child_height += layout_body_childs[k].offsetHeight;
3166                                 continue;
3167                             }
3168                             
3169                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3170                                 
3171                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3172                                 
3173                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3174                                     
3175                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3176                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3177                                         continue;
3178                                     }
3179                                     
3180                                 }
3181                                 
3182                             }
3183                             
3184                         }
3185                     }
3186                 }
3187                 continue;
3188             }
3189             */
3190             
3191             child_height += child_nodes[i].offsetHeight;
3192             // Roo.log(child_nodes[i].offsetHeight);
3193         }
3194         
3195         return child_height;
3196     }
3197
3198 });
3199
3200
3201 Roo.apply(Roo.bootstrap.Modal,  {
3202     /**
3203          * Button config that displays a single OK button
3204          * @type Object
3205          */
3206         OK :  [{
3207             name : 'ok',
3208             weight : 'primary',
3209             html : 'OK'
3210         }],
3211         /**
3212          * Button config that displays Yes and No buttons
3213          * @type Object
3214          */
3215         YESNO : [
3216             {
3217                 name  : 'no',
3218                 html : 'No'
3219             },
3220             {
3221                 name  :'yes',
3222                 weight : 'primary',
3223                 html : 'Yes'
3224             }
3225         ],
3226
3227         /**
3228          * Button config that displays OK and Cancel buttons
3229          * @type Object
3230          */
3231         OKCANCEL : [
3232             {
3233                name : 'cancel',
3234                 html : 'Cancel'
3235             },
3236             {
3237                 name : 'ok',
3238                 weight : 'primary',
3239                 html : 'OK'
3240             }
3241         ],
3242         /**
3243          * Button config that displays Yes, No and Cancel buttons
3244          * @type Object
3245          */
3246         YESNOCANCEL : [
3247             {
3248                 name : 'yes',
3249                 weight : 'primary',
3250                 html : 'Yes'
3251             },
3252             {
3253                 name : 'no',
3254                 html : 'No'
3255             },
3256             {
3257                 name : 'cancel',
3258                 html : 'Cancel'
3259             }
3260         ],
3261         
3262         zIndex : 10001
3263 });
3264 /*
3265  * - LGPL
3266  *
3267  * messagebox - can be used as a replace
3268  * 
3269  */
3270 /**
3271  * @class Roo.MessageBox
3272  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3273  * Example usage:
3274  *<pre><code>
3275 // Basic alert:
3276 Roo.Msg.alert('Status', 'Changes saved successfully.');
3277
3278 // Prompt for user data:
3279 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3280     if (btn == 'ok'){
3281         // process text value...
3282     }
3283 });
3284
3285 // Show a dialog using config options:
3286 Roo.Msg.show({
3287    title:'Save Changes?',
3288    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3289    buttons: Roo.Msg.YESNOCANCEL,
3290    fn: processResult,
3291    animEl: 'elId'
3292 });
3293 </code></pre>
3294  * @singleton
3295  */
3296 Roo.bootstrap.MessageBox = function(){
3297     var dlg, opt, mask, waitTimer;
3298     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3299     var buttons, activeTextEl, bwidth;
3300
3301     
3302     // private
3303     var handleButton = function(button){
3304         dlg.hide();
3305         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3306     };
3307
3308     // private
3309     var handleHide = function(){
3310         if(opt && opt.cls){
3311             dlg.el.removeClass(opt.cls);
3312         }
3313         //if(waitTimer){
3314         //    Roo.TaskMgr.stop(waitTimer);
3315         //    waitTimer = null;
3316         //}
3317     };
3318
3319     // private
3320     var updateButtons = function(b){
3321         var width = 0;
3322         if(!b){
3323             buttons["ok"].hide();
3324             buttons["cancel"].hide();
3325             buttons["yes"].hide();
3326             buttons["no"].hide();
3327             dlg.footerEl.hide();
3328             
3329             return width;
3330         }
3331         dlg.footerEl.show();
3332         for(var k in buttons){
3333             if(typeof buttons[k] != "function"){
3334                 if(b[k]){
3335                     buttons[k].show();
3336                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3337                     width += buttons[k].el.getWidth()+15;
3338                 }else{
3339                     buttons[k].hide();
3340                 }
3341             }
3342         }
3343         return width;
3344     };
3345
3346     // private
3347     var handleEsc = function(d, k, e){
3348         if(opt && opt.closable !== false){
3349             dlg.hide();
3350         }
3351         if(e){
3352             e.stopEvent();
3353         }
3354     };
3355
3356     return {
3357         /**
3358          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3359          * @return {Roo.BasicDialog} The BasicDialog element
3360          */
3361         getDialog : function(){
3362            if(!dlg){
3363                 dlg = new Roo.bootstrap.Modal( {
3364                     //draggable: true,
3365                     //resizable:false,
3366                     //constraintoviewport:false,
3367                     //fixedcenter:true,
3368                     //collapsible : false,
3369                     //shim:true,
3370                     //modal: true,
3371                 //    width: 'auto',
3372                   //  height:100,
3373                     //buttonAlign:"center",
3374                     closeClick : function(){
3375                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3376                             handleButton("no");
3377                         }else{
3378                             handleButton("cancel");
3379                         }
3380                     }
3381                 });
3382                 dlg.render();
3383                 dlg.on("hide", handleHide);
3384                 mask = dlg.mask;
3385                 //dlg.addKeyListener(27, handleEsc);
3386                 buttons = {};
3387                 this.buttons = buttons;
3388                 var bt = this.buttonText;
3389                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3390                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3391                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3392                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3393                 //Roo.log(buttons);
3394                 bodyEl = dlg.bodyEl.createChild({
3395
3396                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3397                         '<textarea class="roo-mb-textarea"></textarea>' +
3398                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3399                 });
3400                 msgEl = bodyEl.dom.firstChild;
3401                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3402                 textboxEl.enableDisplayMode();
3403                 textboxEl.addKeyListener([10,13], function(){
3404                     if(dlg.isVisible() && opt && opt.buttons){
3405                         if(opt.buttons.ok){
3406                             handleButton("ok");
3407                         }else if(opt.buttons.yes){
3408                             handleButton("yes");
3409                         }
3410                     }
3411                 });
3412                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3413                 textareaEl.enableDisplayMode();
3414                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3415                 progressEl.enableDisplayMode();
3416                 
3417                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3418                 var pf = progressEl.dom.firstChild;
3419                 if (pf) {
3420                     pp = Roo.get(pf.firstChild);
3421                     pp.setHeight(pf.offsetHeight);
3422                 }
3423                 
3424             }
3425             return dlg;
3426         },
3427
3428         /**
3429          * Updates the message box body text
3430          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3431          * the XHTML-compliant non-breaking space character '&amp;#160;')
3432          * @return {Roo.MessageBox} This message box
3433          */
3434         updateText : function(text)
3435         {
3436             if(!dlg.isVisible() && !opt.width){
3437                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3438                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3439             }
3440             msgEl.innerHTML = text || '&#160;';
3441       
3442             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3443             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3444             var w = Math.max(
3445                     Math.min(opt.width || cw , this.maxWidth), 
3446                     Math.max(opt.minWidth || this.minWidth, bwidth)
3447             );
3448             if(opt.prompt){
3449                 activeTextEl.setWidth(w);
3450             }
3451             if(dlg.isVisible()){
3452                 dlg.fixedcenter = false;
3453             }
3454             // to big, make it scroll. = But as usual stupid IE does not support
3455             // !important..
3456             
3457             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3458                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3459                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3460             } else {
3461                 bodyEl.dom.style.height = '';
3462                 bodyEl.dom.style.overflowY = '';
3463             }
3464             if (cw > w) {
3465                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3466             } else {
3467                 bodyEl.dom.style.overflowX = '';
3468             }
3469             
3470             dlg.setContentSize(w, bodyEl.getHeight());
3471             if(dlg.isVisible()){
3472                 dlg.fixedcenter = true;
3473             }
3474             return this;
3475         },
3476
3477         /**
3478          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3479          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3480          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3481          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3482          * @return {Roo.MessageBox} This message box
3483          */
3484         updateProgress : function(value, text){
3485             if(text){
3486                 this.updateText(text);
3487             }
3488             
3489             if (pp) { // weird bug on my firefox - for some reason this is not defined
3490                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3491                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3492             }
3493             return this;
3494         },        
3495
3496         /**
3497          * Returns true if the message box is currently displayed
3498          * @return {Boolean} True if the message box is visible, else false
3499          */
3500         isVisible : function(){
3501             return dlg && dlg.isVisible();  
3502         },
3503
3504         /**
3505          * Hides the message box if it is displayed
3506          */
3507         hide : function(){
3508             if(this.isVisible()){
3509                 dlg.hide();
3510             }  
3511         },
3512
3513         /**
3514          * Displays a new message box, or reinitializes an existing message box, based on the config options
3515          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3516          * The following config object properties are supported:
3517          * <pre>
3518 Property    Type             Description
3519 ----------  ---------------  ------------------------------------------------------------------------------------
3520 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3521                                    closes (defaults to undefined)
3522 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3523                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3524 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3525                                    progress and wait dialogs will ignore this property and always hide the
3526                                    close button as they can only be closed programmatically.
3527 cls               String           A custom CSS class to apply to the message box element
3528 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3529                                    displayed (defaults to 75)
3530 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3531                                    function will be btn (the name of the button that was clicked, if applicable,
3532                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3533                                    Progress and wait dialogs will ignore this option since they do not respond to
3534                                    user actions and can only be closed programmatically, so any required function
3535                                    should be called by the same code after it closes the dialog.
3536 icon              String           A CSS class that provides a background image to be used as an icon for
3537                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3538 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3539 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3540 modal             Boolean          False to allow user interaction with the page while the message box is
3541                                    displayed (defaults to true)
3542 msg               String           A string that will replace the existing message box body text (defaults
3543                                    to the XHTML-compliant non-breaking space character '&#160;')
3544 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3545 progress          Boolean          True to display a progress bar (defaults to false)
3546 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3547 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3548 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3549 title             String           The title text
3550 value             String           The string value to set into the active textbox element if displayed
3551 wait              Boolean          True to display a progress bar (defaults to false)
3552 width             Number           The width of the dialog in pixels
3553 </pre>
3554          *
3555          * Example usage:
3556          * <pre><code>
3557 Roo.Msg.show({
3558    title: 'Address',
3559    msg: 'Please enter your address:',
3560    width: 300,
3561    buttons: Roo.MessageBox.OKCANCEL,
3562    multiline: true,
3563    fn: saveAddress,
3564    animEl: 'addAddressBtn'
3565 });
3566 </code></pre>
3567          * @param {Object} config Configuration options
3568          * @return {Roo.MessageBox} This message box
3569          */
3570         show : function(options)
3571         {
3572             
3573             // this causes nightmares if you show one dialog after another
3574             // especially on callbacks..
3575              
3576             if(this.isVisible()){
3577                 
3578                 this.hide();
3579                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3580                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3581                 Roo.log("New Dialog Message:" +  options.msg )
3582                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3583                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3584                 
3585             }
3586             var d = this.getDialog();
3587             opt = options;
3588             d.setTitle(opt.title || "&#160;");
3589             d.closeEl.setDisplayed(opt.closable !== false);
3590             activeTextEl = textboxEl;
3591             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3592             if(opt.prompt){
3593                 if(opt.multiline){
3594                     textboxEl.hide();
3595                     textareaEl.show();
3596                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3597                         opt.multiline : this.defaultTextHeight);
3598                     activeTextEl = textareaEl;
3599                 }else{
3600                     textboxEl.show();
3601                     textareaEl.hide();
3602                 }
3603             }else{
3604                 textboxEl.hide();
3605                 textareaEl.hide();
3606             }
3607             progressEl.setDisplayed(opt.progress === true);
3608             if (opt.progress) {
3609                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3610             }
3611             this.updateProgress(0);
3612             activeTextEl.dom.value = opt.value || "";
3613             if(opt.prompt){
3614                 dlg.setDefaultButton(activeTextEl);
3615             }else{
3616                 var bs = opt.buttons;
3617                 var db = null;
3618                 if(bs && bs.ok){
3619                     db = buttons["ok"];
3620                 }else if(bs && bs.yes){
3621                     db = buttons["yes"];
3622                 }
3623                 dlg.setDefaultButton(db);
3624             }
3625             bwidth = updateButtons(opt.buttons);
3626             this.updateText(opt.msg);
3627             if(opt.cls){
3628                 d.el.addClass(opt.cls);
3629             }
3630             d.proxyDrag = opt.proxyDrag === true;
3631             d.modal = opt.modal !== false;
3632             d.mask = opt.modal !== false ? mask : false;
3633             if(!d.isVisible()){
3634                 // force it to the end of the z-index stack so it gets a cursor in FF
3635                 document.body.appendChild(dlg.el.dom);
3636                 d.animateTarget = null;
3637                 d.show(options.animEl);
3638             }
3639             return this;
3640         },
3641
3642         /**
3643          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3644          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3645          * and closing the message box when the process is complete.
3646          * @param {String} title The title bar text
3647          * @param {String} msg The message box body text
3648          * @return {Roo.MessageBox} This message box
3649          */
3650         progress : function(title, msg){
3651             this.show({
3652                 title : title,
3653                 msg : msg,
3654                 buttons: false,
3655                 progress:true,
3656                 closable:false,
3657                 minWidth: this.minProgressWidth,
3658                 modal : true
3659             });
3660             return this;
3661         },
3662
3663         /**
3664          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3665          * If a callback function is passed it will be called after the user clicks the button, and the
3666          * id of the button that was clicked will be passed as the only parameter to the callback
3667          * (could also be the top-right close button).
3668          * @param {String} title The title bar text
3669          * @param {String} msg The message box body text
3670          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3671          * @param {Object} scope (optional) The scope of the callback function
3672          * @return {Roo.MessageBox} This message box
3673          */
3674         alert : function(title, msg, fn, scope)
3675         {
3676             this.show({
3677                 title : title,
3678                 msg : msg,
3679                 buttons: this.OK,
3680                 fn: fn,
3681                 closable : false,
3682                 scope : scope,
3683                 modal : true
3684             });
3685             return this;
3686         },
3687
3688         /**
3689          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3690          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3691          * You are responsible for closing the message box when the process is complete.
3692          * @param {String} msg The message box body text
3693          * @param {String} title (optional) The title bar text
3694          * @return {Roo.MessageBox} This message box
3695          */
3696         wait : function(msg, title){
3697             this.show({
3698                 title : title,
3699                 msg : msg,
3700                 buttons: false,
3701                 closable:false,
3702                 progress:true,
3703                 modal:true,
3704                 width:300,
3705                 wait:true
3706             });
3707             waitTimer = Roo.TaskMgr.start({
3708                 run: function(i){
3709                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3710                 },
3711                 interval: 1000
3712             });
3713             return this;
3714         },
3715
3716         /**
3717          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3718          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3719          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3720          * @param {String} title The title bar text
3721          * @param {String} msg The message box body text
3722          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3723          * @param {Object} scope (optional) The scope of the callback function
3724          * @return {Roo.MessageBox} This message box
3725          */
3726         confirm : function(title, msg, fn, scope){
3727             this.show({
3728                 title : title,
3729                 msg : msg,
3730                 buttons: this.YESNO,
3731                 fn: fn,
3732                 scope : scope,
3733                 modal : true
3734             });
3735             return this;
3736         },
3737
3738         /**
3739          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3740          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3741          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3742          * (could also be the top-right close button) and the text that was entered will be passed as the two
3743          * parameters to the callback.
3744          * @param {String} title The title bar text
3745          * @param {String} msg The message box body text
3746          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3747          * @param {Object} scope (optional) The scope of the callback function
3748          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3749          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3750          * @return {Roo.MessageBox} This message box
3751          */
3752         prompt : function(title, msg, fn, scope, multiline){
3753             this.show({
3754                 title : title,
3755                 msg : msg,
3756                 buttons: this.OKCANCEL,
3757                 fn: fn,
3758                 minWidth:250,
3759                 scope : scope,
3760                 prompt:true,
3761                 multiline: multiline,
3762                 modal : true
3763             });
3764             return this;
3765         },
3766
3767         /**
3768          * Button config that displays a single OK button
3769          * @type Object
3770          */
3771         OK : {ok:true},
3772         /**
3773          * Button config that displays Yes and No buttons
3774          * @type Object
3775          */
3776         YESNO : {yes:true, no:true},
3777         /**
3778          * Button config that displays OK and Cancel buttons
3779          * @type Object
3780          */
3781         OKCANCEL : {ok:true, cancel:true},
3782         /**
3783          * Button config that displays Yes, No and Cancel buttons
3784          * @type Object
3785          */
3786         YESNOCANCEL : {yes:true, no:true, cancel:true},
3787
3788         /**
3789          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3790          * @type Number
3791          */
3792         defaultTextHeight : 75,
3793         /**
3794          * The maximum width in pixels of the message box (defaults to 600)
3795          * @type Number
3796          */
3797         maxWidth : 600,
3798         /**
3799          * The minimum width in pixels of the message box (defaults to 100)
3800          * @type Number
3801          */
3802         minWidth : 100,
3803         /**
3804          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3805          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3806          * @type Number
3807          */
3808         minProgressWidth : 250,
3809         /**
3810          * An object containing the default button text strings that can be overriden for localized language support.
3811          * Supported properties are: ok, cancel, yes and no.
3812          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3813          * @type Object
3814          */
3815         buttonText : {
3816             ok : "OK",
3817             cancel : "Cancel",
3818             yes : "Yes",
3819             no : "No"
3820         }
3821     };
3822 }();
3823
3824 /**
3825  * Shorthand for {@link Roo.MessageBox}
3826  */
3827 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3828 Roo.Msg = Roo.Msg || Roo.MessageBox;
3829 /*
3830  * - LGPL
3831  *
3832  * navbar
3833  * 
3834  */
3835
3836 /**
3837  * @class Roo.bootstrap.Navbar
3838  * @extends Roo.bootstrap.Component
3839  * Bootstrap Navbar class
3840
3841  * @constructor
3842  * Create a new Navbar
3843  * @param {Object} config The config object
3844  */
3845
3846
3847 Roo.bootstrap.Navbar = function(config){
3848     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3849     this.addEvents({
3850         // raw events
3851         /**
3852          * @event beforetoggle
3853          * Fire before toggle the menu
3854          * @param {Roo.EventObject} e
3855          */
3856         "beforetoggle" : true
3857     });
3858 };
3859
3860 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3861     
3862     
3863    
3864     // private
3865     navItems : false,
3866     loadMask : false,
3867     
3868     
3869     getAutoCreate : function(){
3870         
3871         
3872         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3873         
3874     },
3875     
3876     initEvents :function ()
3877     {
3878         //Roo.log(this.el.select('.navbar-toggle',true));
3879         this.el.select('.navbar-toggle',true).on('click', function() {
3880             if(this.fireEvent('beforetoggle', this) !== false){
3881                 var ce = this.el.select('.navbar-collapse',true).first();
3882                 ce.toggleClass('in'); // old...
3883                 if (ce.hasClass('collapse')) {
3884                     // show it...
3885                     ce.removeClass('collapse');
3886                     ce.addClass('show');
3887                     var h = ce.getHeight();
3888                     Roo.log(h);
3889                     ce.removeClass('show');
3890                     // at this point we should be able to see it..
3891                     ce.addClass('collapsing');
3892                     
3893                     ce.setHeight(0); // resize it ...
3894                     ce.on('transitionend', function() {
3895                         Roo.log('done transition');
3896                         ce.removeClass('collapsing');
3897                         ce.addClass('show');
3898                         ce.removeClass('collapse');
3899
3900                         ce.dom.style.height = '';
3901                     }, this, { single: true} );
3902                     ce.setHeight(h);
3903                     
3904                 } else {
3905                     ce.setHeight(ce.getHeight());
3906                     ce.removeClass('show');
3907                     ce.addClass('collapsing');
3908                     
3909                     ce.on('transitionend', function() {
3910                         ce.dom.style.height = '';
3911                         ce.removeClass('collapsing');
3912                         ce.addClass('collapse');
3913                     }, this, { single: true} );
3914                     ce.setHeight(0);
3915                 }
3916             }
3917             
3918         }, this);
3919         
3920         var mark = {
3921             tag: "div",
3922             cls:"x-dlg-mask"
3923         };
3924         
3925         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3926         
3927         var size = this.el.getSize();
3928         this.maskEl.setSize(size.width, size.height);
3929         this.maskEl.enableDisplayMode("block");
3930         this.maskEl.hide();
3931         
3932         if(this.loadMask){
3933             this.maskEl.show();
3934         }
3935     },
3936     
3937     
3938     getChildContainer : function()
3939     {
3940         if (this.el.select('.collapse').getCount()) {
3941             return this.el.select('.collapse',true).first();
3942         }
3943         
3944         return this.el;
3945     },
3946     
3947     mask : function()
3948     {
3949         this.maskEl.show();
3950     },
3951     
3952     unmask : function()
3953     {
3954         this.maskEl.hide();
3955     } 
3956     
3957     
3958     
3959     
3960 });
3961
3962
3963
3964  
3965
3966  /*
3967  * - LGPL
3968  *
3969  * navbar
3970  * 
3971  */
3972
3973 /**
3974  * @class Roo.bootstrap.NavSimplebar
3975  * @extends Roo.bootstrap.Navbar
3976  * Bootstrap Sidebar class
3977  *
3978  * @cfg {Boolean} inverse is inverted color
3979  * 
3980  * @cfg {String} type (nav | pills | tabs)
3981  * @cfg {Boolean} arrangement stacked | justified
3982  * @cfg {String} align (left | right) alignment
3983  * 
3984  * @cfg {Boolean} main (true|false) main nav bar? default false
3985  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3986  * 
3987  * @cfg {String} tag (header|footer|nav|div) default is nav 
3988
3989  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3990  * 
3991  * 
3992  * @constructor
3993  * Create a new Sidebar
3994  * @param {Object} config The config object
3995  */
3996
3997
3998 Roo.bootstrap.NavSimplebar = function(config){
3999     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4000 };
4001
4002 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
4003     
4004     inverse: false,
4005     
4006     type: false,
4007     arrangement: '',
4008     align : false,
4009     
4010     weight : 'light',
4011     
4012     main : false,
4013     
4014     
4015     tag : false,
4016     
4017     
4018     getAutoCreate : function(){
4019         
4020         
4021         var cfg = {
4022             tag : this.tag || 'div',
4023             cls : 'navbar navbar-expand-lg'
4024         };
4025         if (['light','white'].indexOf(this.weight) > -1) {
4026             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4027         }
4028         cfg.cls += ' bg-' + this.weight;
4029         
4030         if (this.inverse) {
4031             cfg.cls += ' navbar-inverse';
4032             
4033         }
4034         
4035         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4036         
4037         if (Roo.bootstrap.version == 4) {
4038             return cfg;
4039         }
4040         
4041         cfg.cn = [
4042             {
4043                 cls: 'nav',
4044                 tag : 'ul'
4045             }
4046         ];
4047         
4048          
4049         this.type = this.type || 'nav';
4050         if (['tabs','pills'].indexOf(this.type)!==-1) {
4051             cfg.cn[0].cls += ' nav-' + this.type
4052         
4053         
4054         } else {
4055             if (this.type!=='nav') {
4056                 Roo.log('nav type must be nav/tabs/pills')
4057             }
4058             cfg.cn[0].cls += ' navbar-nav'
4059         }
4060         
4061         
4062         
4063         
4064         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
4065             cfg.cn[0].cls += ' nav-' + this.arrangement;
4066         }
4067         
4068         
4069         if (this.align === 'right') {
4070             cfg.cn[0].cls += ' navbar-right';
4071         }
4072         
4073         
4074         
4075         
4076         return cfg;
4077     
4078         
4079     }
4080     
4081     
4082     
4083 });
4084
4085
4086
4087  
4088
4089  
4090        /*
4091  * - LGPL
4092  *
4093  * navbar
4094  * navbar-fixed-top
4095  * navbar-expand-md  fixed-top 
4096  */
4097
4098 /**
4099  * @class Roo.bootstrap.NavHeaderbar
4100  * @extends Roo.bootstrap.NavSimplebar
4101  * Bootstrap Sidebar class
4102  *
4103  * @cfg {String} brand what is brand
4104  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4105  * @cfg {String} brand_href href of the brand
4106  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4107  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4108  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4109  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4110  * 
4111  * @constructor
4112  * Create a new Sidebar
4113  * @param {Object} config The config object
4114  */
4115
4116
4117 Roo.bootstrap.NavHeaderbar = function(config){
4118     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4119       
4120 };
4121
4122 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4123     
4124     position: '',
4125     brand: '',
4126     brand_href: false,
4127     srButton : true,
4128     autohide : false,
4129     desktopCenter : false,
4130    
4131     
4132     getAutoCreate : function(){
4133         
4134         var   cfg = {
4135             tag: this.nav || 'nav',
4136             cls: 'navbar navbar-expand-md',
4137             role: 'navigation',
4138             cn: []
4139         };
4140         
4141         var cn = cfg.cn;
4142         if (this.desktopCenter) {
4143             cn.push({cls : 'container', cn : []});
4144             cn = cn[0].cn;
4145         }
4146         
4147         if(this.srButton){
4148             var btn = {
4149                 tag: 'button',
4150                 type: 'button',
4151                 cls: 'navbar-toggle navbar-toggler',
4152                 'data-toggle': 'collapse',
4153                 cn: [
4154                     {
4155                         tag: 'span',
4156                         cls: 'sr-only',
4157                         html: 'Toggle navigation'
4158                     },
4159                     {
4160                         tag: 'span',
4161                         cls: 'icon-bar navbar-toggler-icon'
4162                     },
4163                     {
4164                         tag: 'span',
4165                         cls: 'icon-bar'
4166                     },
4167                     {
4168                         tag: 'span',
4169                         cls: 'icon-bar'
4170                     }
4171                 ]
4172             };
4173             
4174             cn.push( Roo.bootstrap.version == 4 ? btn : {
4175                 tag: 'div',
4176                 cls: 'navbar-header',
4177                 cn: [
4178                     btn
4179                 ]
4180             });
4181         }
4182         
4183         cn.push({
4184             tag: 'div',
4185             cls: 'collapse navbar-collapse',
4186             cn : []
4187         });
4188         
4189         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4190         
4191         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4192             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4193             
4194             // tag can override this..
4195             
4196             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4197         }
4198         
4199         if (this.brand !== '') {
4200             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4201             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4202                 tag: 'a',
4203                 href: this.brand_href ? this.brand_href : '#',
4204                 cls: 'navbar-brand',
4205                 cn: [
4206                 this.brand
4207                 ]
4208             });
4209         }
4210         
4211         if(this.main){
4212             cfg.cls += ' main-nav';
4213         }
4214         
4215         
4216         return cfg;
4217
4218         
4219     },
4220     getHeaderChildContainer : function()
4221     {
4222         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4223             return this.el.select('.navbar-header',true).first();
4224         }
4225         
4226         return this.getChildContainer();
4227     },
4228     
4229     
4230     initEvents : function()
4231     {
4232         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4233         
4234         if (this.autohide) {
4235             
4236             var prevScroll = 0;
4237             var ft = this.el;
4238             
4239             Roo.get(document).on('scroll',function(e) {
4240                 var ns = Roo.get(document).getScroll().top;
4241                 var os = prevScroll;
4242                 prevScroll = ns;
4243                 
4244                 if(ns > os){
4245                     ft.removeClass('slideDown');
4246                     ft.addClass('slideUp');
4247                     return;
4248                 }
4249                 ft.removeClass('slideUp');
4250                 ft.addClass('slideDown');
4251                  
4252               
4253           },this);
4254         }
4255     }    
4256     
4257 });
4258
4259
4260
4261  
4262
4263  /*
4264  * - LGPL
4265  *
4266  * navbar
4267  * 
4268  */
4269
4270 /**
4271  * @class Roo.bootstrap.NavSidebar
4272  * @extends Roo.bootstrap.Navbar
4273  * Bootstrap Sidebar class
4274  * 
4275  * @constructor
4276  * Create a new Sidebar
4277  * @param {Object} config The config object
4278  */
4279
4280
4281 Roo.bootstrap.NavSidebar = function(config){
4282     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4283 };
4284
4285 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4286     
4287     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4288     
4289     getAutoCreate : function(){
4290         
4291         
4292         return  {
4293             tag: 'div',
4294             cls: 'sidebar sidebar-nav'
4295         };
4296     
4297         
4298     }
4299     
4300     
4301     
4302 });
4303
4304
4305
4306  
4307
4308  /*
4309  * - LGPL
4310  *
4311  * nav group
4312  * 
4313  */
4314
4315 /**
4316  * @class Roo.bootstrap.NavGroup
4317  * @extends Roo.bootstrap.Component
4318  * Bootstrap NavGroup class
4319  * @cfg {String} align (left|right)
4320  * @cfg {Boolean} inverse
4321  * @cfg {String} type (nav|pills|tab) default nav
4322  * @cfg {String} navId - reference Id for navbar.
4323
4324  * 
4325  * @constructor
4326  * Create a new nav group
4327  * @param {Object} config The config object
4328  */
4329
4330 Roo.bootstrap.NavGroup = function(config){
4331     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4332     this.navItems = [];
4333    
4334     Roo.bootstrap.NavGroup.register(this);
4335      this.addEvents({
4336         /**
4337              * @event changed
4338              * Fires when the active item changes
4339              * @param {Roo.bootstrap.NavGroup} this
4340              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4341              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4342          */
4343         'changed': true
4344      });
4345     
4346 };
4347
4348 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4349     
4350     align: '',
4351     inverse: false,
4352     form: false,
4353     type: 'nav',
4354     navId : '',
4355     // private
4356     
4357     navItems : false, 
4358     
4359     getAutoCreate : function()
4360     {
4361         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4362         
4363         cfg = {
4364             tag : 'ul',
4365             cls: 'nav' 
4366         };
4367         if (Roo.bootstrap.version == 4) {
4368             if (this.type == 'pills') {
4369                 cfg.cls = ' nav-pills';
4370             }
4371         } else {
4372             if (['tabs','pills'].indexOf(this.type)!==-1) {
4373                 cfg.cls += ' nav-' + this.type
4374             } else {
4375                 if (this.type !== 'nav') {
4376                     Roo.log('nav type must be nav/tabs/pills')
4377                 }
4378                 cfg.cls += ' navbar-nav'
4379             }
4380         }
4381         
4382         if (this.parent() && this.parent().sidebar) {
4383             cfg = {
4384                 tag: 'ul',
4385                 cls: 'dashboard-menu sidebar-menu'
4386             };
4387             
4388             return cfg;
4389         }
4390         
4391         if (this.form === true) {
4392             cfg = {
4393                 tag: 'form',
4394                 cls: 'navbar-form form-inline'
4395             };
4396             
4397             if (this.align === 'right') {
4398                 cfg.cls += ' navbar-right ml-md-auto';
4399             } else {
4400                 cfg.cls += ' navbar-left';
4401             }
4402         }
4403         
4404         if (this.align === 'right') {
4405             cfg.cls += ' navbar-right ml-md-auto';
4406         } else {
4407             cfg.cls += ' mr-auto';
4408         }
4409         
4410         if (this.inverse) {
4411             cfg.cls += ' navbar-inverse';
4412             
4413         }
4414         
4415         
4416         return cfg;
4417     },
4418     /**
4419     * sets the active Navigation item
4420     * @param {Roo.bootstrap.NavItem} the new current navitem
4421     */
4422     setActiveItem : function(item)
4423     {
4424         var prev = false;
4425         Roo.each(this.navItems, function(v){
4426             if (v == item) {
4427                 return ;
4428             }
4429             if (v.isActive()) {
4430                 v.setActive(false, true);
4431                 prev = v;
4432                 
4433             }
4434             
4435         });
4436
4437         item.setActive(true, true);
4438         this.fireEvent('changed', this, item, prev);
4439         
4440         
4441     },
4442     /**
4443     * gets the active Navigation item
4444     * @return {Roo.bootstrap.NavItem} the current navitem
4445     */
4446     getActive : function()
4447     {
4448         
4449         var prev = false;
4450         Roo.each(this.navItems, function(v){
4451             
4452             if (v.isActive()) {
4453                 prev = v;
4454                 
4455             }
4456             
4457         });
4458         return prev;
4459     },
4460     
4461     indexOfNav : function()
4462     {
4463         
4464         var prev = false;
4465         Roo.each(this.navItems, function(v,i){
4466             
4467             if (v.isActive()) {
4468                 prev = i;
4469                 
4470             }
4471             
4472         });
4473         return prev;
4474     },
4475     /**
4476     * adds a Navigation item
4477     * @param {Roo.bootstrap.NavItem} the navitem to add
4478     */
4479     addItem : function(cfg)
4480     {
4481         if (this.form && Roo.bootstrap.version == 4) {
4482             cfg.tag = 'div';
4483         }
4484         var cn = new Roo.bootstrap.NavItem(cfg);
4485         this.register(cn);
4486         cn.parentId = this.id;
4487         cn.onRender(this.el, null);
4488         return cn;
4489     },
4490     /**
4491     * register a Navigation item
4492     * @param {Roo.bootstrap.NavItem} the navitem to add
4493     */
4494     register : function(item)
4495     {
4496         this.navItems.push( item);
4497         item.navId = this.navId;
4498     
4499     },
4500     
4501     /**
4502     * clear all the Navigation item
4503     */
4504    
4505     clearAll : function()
4506     {
4507         this.navItems = [];
4508         this.el.dom.innerHTML = '';
4509     },
4510     
4511     getNavItem: function(tabId)
4512     {
4513         var ret = false;
4514         Roo.each(this.navItems, function(e) {
4515             if (e.tabId == tabId) {
4516                ret =  e;
4517                return false;
4518             }
4519             return true;
4520             
4521         });
4522         return ret;
4523     },
4524     
4525     setActiveNext : function()
4526     {
4527         var i = this.indexOfNav(this.getActive());
4528         if (i > this.navItems.length) {
4529             return;
4530         }
4531         this.setActiveItem(this.navItems[i+1]);
4532     },
4533     setActivePrev : function()
4534     {
4535         var i = this.indexOfNav(this.getActive());
4536         if (i  < 1) {
4537             return;
4538         }
4539         this.setActiveItem(this.navItems[i-1]);
4540     },
4541     clearWasActive : function(except) {
4542         Roo.each(this.navItems, function(e) {
4543             if (e.tabId != except.tabId && e.was_active) {
4544                e.was_active = false;
4545                return false;
4546             }
4547             return true;
4548             
4549         });
4550     },
4551     getWasActive : function ()
4552     {
4553         var r = false;
4554         Roo.each(this.navItems, function(e) {
4555             if (e.was_active) {
4556                r = e;
4557                return false;
4558             }
4559             return true;
4560             
4561         });
4562         return r;
4563     }
4564     
4565     
4566 });
4567
4568  
4569 Roo.apply(Roo.bootstrap.NavGroup, {
4570     
4571     groups: {},
4572      /**
4573     * register a Navigation Group
4574     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4575     */
4576     register : function(navgrp)
4577     {
4578         this.groups[navgrp.navId] = navgrp;
4579         
4580     },
4581     /**
4582     * fetch a Navigation Group based on the navigation ID
4583     * @param {string} the navgroup to add
4584     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4585     */
4586     get: function(navId) {
4587         if (typeof(this.groups[navId]) == 'undefined') {
4588             return false;
4589             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4590         }
4591         return this.groups[navId] ;
4592     }
4593     
4594     
4595     
4596 });
4597
4598  /*
4599  * - LGPL
4600  *
4601  * row
4602  * 
4603  */
4604
4605 /**
4606  * @class Roo.bootstrap.NavItem
4607  * @extends Roo.bootstrap.Component
4608  * Bootstrap Navbar.NavItem class
4609  * @cfg {String} href  link to
4610  * @cfg {String} html content of button
4611  * @cfg {String} badge text inside badge
4612  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4613  * @cfg {String} glyphicon DEPRICATED - use fa
4614  * @cfg {String} icon DEPRICATED - use fa
4615  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4616  * @cfg {Boolean} active Is item active
4617  * @cfg {Boolean} disabled Is item disabled
4618  
4619  * @cfg {Boolean} preventDefault (true | false) default false
4620  * @cfg {String} tabId the tab that this item activates.
4621  * @cfg {String} tagtype (a|span) render as a href or span?
4622  * @cfg {Boolean} animateRef (true|false) link to element default false  
4623   
4624  * @constructor
4625  * Create a new Navbar Item
4626  * @param {Object} config The config object
4627  */
4628 Roo.bootstrap.NavItem = function(config){
4629     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4630     this.addEvents({
4631         // raw events
4632         /**
4633          * @event click
4634          * The raw click event for the entire grid.
4635          * @param {Roo.EventObject} e
4636          */
4637         "click" : true,
4638          /**
4639             * @event changed
4640             * Fires when the active item active state changes
4641             * @param {Roo.bootstrap.NavItem} this
4642             * @param {boolean} state the new state
4643              
4644          */
4645         'changed': true,
4646         /**
4647             * @event scrollto
4648             * Fires when scroll to element
4649             * @param {Roo.bootstrap.NavItem} this
4650             * @param {Object} options
4651             * @param {Roo.EventObject} e
4652              
4653          */
4654         'scrollto': true
4655     });
4656    
4657 };
4658
4659 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4660     
4661     href: false,
4662     html: '',
4663     badge: '',
4664     icon: false,
4665     fa : false,
4666     glyphicon: false,
4667     active: false,
4668     preventDefault : false,
4669     tabId : false,
4670     tagtype : 'a',
4671     tag: 'li',
4672     disabled : false,
4673     animateRef : false,
4674     was_active : false,
4675     
4676     getAutoCreate : function(){
4677          
4678         var cfg = {
4679             tag: this.tag,
4680             cls: 'nav-item'
4681             
4682         };
4683         
4684         if (this.active) {
4685             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4686         }
4687         if (this.disabled) {
4688             cfg.cls += ' disabled';
4689         }
4690         
4691         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4692             cfg.cn = [
4693                 {
4694                     tag: this.tagtype,
4695                     href : this.href || "#",
4696                     html: this.html || ''
4697                 }
4698             ];
4699             if (this.tagtype == 'a') {
4700                 cfg.cn[0].cls = 'nav-link';
4701             }
4702             if (this.icon) {
4703                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4704             }
4705             if (this.fa) {
4706                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4707             }
4708             if(this.glyphicon) {
4709                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4710             }
4711             
4712             if (this.menu) {
4713                 
4714                 cfg.cn[0].html += " <span class='caret'></span>";
4715              
4716             }
4717             
4718             if (this.badge !== '') {
4719                  
4720                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4721             }
4722         }
4723         
4724         
4725         
4726         return cfg;
4727     },
4728     onRender : function(ct, position)
4729     {
4730        // Roo.log("Call onRender: " + this.xtype);
4731         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
4732             this.tag = 'div';
4733         }
4734         
4735         return Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
4736     },
4737       
4738     
4739     initEvents: function() 
4740     {
4741         if (typeof (this.menu) != 'undefined') {
4742             this.menu.parentType = this.xtype;
4743             this.menu.triggerEl = this.el;
4744             this.menu = this.addxtype(Roo.apply({}, this.menu));
4745         }
4746         
4747         this.el.select('a',true).on('click', this.onClick, this);
4748         
4749         if(this.tagtype == 'span'){
4750             this.el.select('span',true).on('click', this.onClick, this);
4751         }
4752        
4753         // at this point parent should be available..
4754         this.parent().register(this);
4755     },
4756     
4757     onClick : function(e)
4758     {
4759         if (e.getTarget('.dropdown-menu-item')) {
4760             // did you click on a menu itemm.... - then don't trigger onclick..
4761             return;
4762         }
4763         
4764         if(
4765                 this.preventDefault || 
4766                 this.href == '#' 
4767         ){
4768             Roo.log("NavItem - prevent Default?");
4769             e.preventDefault();
4770         }
4771         
4772         if (this.disabled) {
4773             return;
4774         }
4775         
4776         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4777         if (tg && tg.transition) {
4778             Roo.log("waiting for the transitionend");
4779             return;
4780         }
4781         
4782         
4783         
4784         //Roo.log("fire event clicked");
4785         if(this.fireEvent('click', this, e) === false){
4786             return;
4787         };
4788         
4789         if(this.tagtype == 'span'){
4790             return;
4791         }
4792         
4793         //Roo.log(this.href);
4794         var ael = this.el.select('a',true).first();
4795         //Roo.log(ael);
4796         
4797         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4798             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4799             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4800                 return; // ignore... - it's a 'hash' to another page.
4801             }
4802             Roo.log("NavItem - prevent Default?");
4803             e.preventDefault();
4804             this.scrollToElement(e);
4805         }
4806         
4807         
4808         var p =  this.parent();
4809    
4810         if (['tabs','pills'].indexOf(p.type)!==-1) {
4811             if (typeof(p.setActiveItem) !== 'undefined') {
4812                 p.setActiveItem(this);
4813             }
4814         }
4815         
4816         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4817         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4818             // remove the collapsed menu expand...
4819             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4820         }
4821     },
4822     
4823     isActive: function () {
4824         return this.active
4825     },
4826     setActive : function(state, fire, is_was_active)
4827     {
4828         if (this.active && !state && this.navId) {
4829             this.was_active = true;
4830             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4831             if (nv) {
4832                 nv.clearWasActive(this);
4833             }
4834             
4835         }
4836         this.active = state;
4837         
4838         if (!state ) {
4839             this.el.removeClass('active');
4840         } else if (!this.el.hasClass('active')) {
4841             this.el.addClass('active');
4842         }
4843         if (fire) {
4844             this.fireEvent('changed', this, state);
4845         }
4846         
4847         // show a panel if it's registered and related..
4848         
4849         if (!this.navId || !this.tabId || !state || is_was_active) {
4850             return;
4851         }
4852         
4853         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4854         if (!tg) {
4855             return;
4856         }
4857         var pan = tg.getPanelByName(this.tabId);
4858         if (!pan) {
4859             return;
4860         }
4861         // if we can not flip to new panel - go back to old nav highlight..
4862         if (false == tg.showPanel(pan)) {
4863             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4864             if (nv) {
4865                 var onav = nv.getWasActive();
4866                 if (onav) {
4867                     onav.setActive(true, false, true);
4868                 }
4869             }
4870             
4871         }
4872         
4873         
4874         
4875     },
4876      // this should not be here...
4877     setDisabled : function(state)
4878     {
4879         this.disabled = state;
4880         if (!state ) {
4881             this.el.removeClass('disabled');
4882         } else if (!this.el.hasClass('disabled')) {
4883             this.el.addClass('disabled');
4884         }
4885         
4886     },
4887     
4888     /**
4889      * Fetch the element to display the tooltip on.
4890      * @return {Roo.Element} defaults to this.el
4891      */
4892     tooltipEl : function()
4893     {
4894         return this.el.select('' + this.tagtype + '', true).first();
4895     },
4896     
4897     scrollToElement : function(e)
4898     {
4899         var c = document.body;
4900         
4901         /*
4902          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4903          */
4904         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4905             c = document.documentElement;
4906         }
4907         
4908         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4909         
4910         if(!target){
4911             return;
4912         }
4913
4914         var o = target.calcOffsetsTo(c);
4915         
4916         var options = {
4917             target : target,
4918             value : o[1]
4919         };
4920         
4921         this.fireEvent('scrollto', this, options, e);
4922         
4923         Roo.get(c).scrollTo('top', options.value, true);
4924         
4925         return;
4926     }
4927 });
4928  
4929
4930  /*
4931  * - LGPL
4932  *
4933  * sidebar item
4934  *
4935  *  li
4936  *    <span> icon </span>
4937  *    <span> text </span>
4938  *    <span>badge </span>
4939  */
4940
4941 /**
4942  * @class Roo.bootstrap.NavSidebarItem
4943  * @extends Roo.bootstrap.NavItem
4944  * Bootstrap Navbar.NavSidebarItem class
4945  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4946  * {Boolean} open is the menu open
4947  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4948  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4949  * {String} buttonSize (sm|md|lg)the extra classes for the button
4950  * {Boolean} showArrow show arrow next to the text (default true)
4951  * @constructor
4952  * Create a new Navbar Button
4953  * @param {Object} config The config object
4954  */
4955 Roo.bootstrap.NavSidebarItem = function(config){
4956     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4957     this.addEvents({
4958         // raw events
4959         /**
4960          * @event click
4961          * The raw click event for the entire grid.
4962          * @param {Roo.EventObject} e
4963          */
4964         "click" : true,
4965          /**
4966             * @event changed
4967             * Fires when the active item active state changes
4968             * @param {Roo.bootstrap.NavSidebarItem} this
4969             * @param {boolean} state the new state
4970              
4971          */
4972         'changed': true
4973     });
4974    
4975 };
4976
4977 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4978     
4979     badgeWeight : 'default',
4980     
4981     open: false,
4982     
4983     buttonView : false,
4984     
4985     buttonWeight : 'default',
4986     
4987     buttonSize : 'md',
4988     
4989     showArrow : true,
4990     
4991     getAutoCreate : function(){
4992         
4993         
4994         var a = {
4995                 tag: 'a',
4996                 href : this.href || '#',
4997                 cls: '',
4998                 html : '',
4999                 cn : []
5000         };
5001         
5002         if(this.buttonView){
5003             a = {
5004                 tag: 'button',
5005                 href : this.href || '#',
5006                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5007                 html : this.html,
5008                 cn : []
5009             };
5010         }
5011         
5012         var cfg = {
5013             tag: 'li',
5014             cls: '',
5015             cn: [ a ]
5016         };
5017         
5018         if (this.active) {
5019             cfg.cls += ' active';
5020         }
5021         
5022         if (this.disabled) {
5023             cfg.cls += ' disabled';
5024         }
5025         if (this.open) {
5026             cfg.cls += ' open x-open';
5027         }
5028         // left icon..
5029         if (this.glyphicon || this.icon) {
5030             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5031             a.cn.push({ tag : 'i', cls : c }) ;
5032         }
5033         
5034         if(!this.buttonView){
5035             var span = {
5036                 tag: 'span',
5037                 html : this.html || ''
5038             };
5039
5040             a.cn.push(span);
5041             
5042         }
5043         
5044         if (this.badge !== '') {
5045             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5046         }
5047         
5048         if (this.menu) {
5049             
5050             if(this.showArrow){
5051                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5052             }
5053             
5054             a.cls += ' dropdown-toggle treeview' ;
5055         }
5056         
5057         return cfg;
5058     },
5059     
5060     initEvents : function()
5061     { 
5062         if (typeof (this.menu) != 'undefined') {
5063             this.menu.parentType = this.xtype;
5064             this.menu.triggerEl = this.el;
5065             this.menu = this.addxtype(Roo.apply({}, this.menu));
5066         }
5067         
5068         this.el.on('click', this.onClick, this);
5069         
5070         if(this.badge !== ''){
5071             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5072         }
5073         
5074     },
5075     
5076     onClick : function(e)
5077     {
5078         if(this.disabled){
5079             e.preventDefault();
5080             return;
5081         }
5082         
5083         if(this.preventDefault){
5084             e.preventDefault();
5085         }
5086         
5087         this.fireEvent('click', this);
5088     },
5089     
5090     disable : function()
5091     {
5092         this.setDisabled(true);
5093     },
5094     
5095     enable : function()
5096     {
5097         this.setDisabled(false);
5098     },
5099     
5100     setDisabled : function(state)
5101     {
5102         if(this.disabled == state){
5103             return;
5104         }
5105         
5106         this.disabled = state;
5107         
5108         if (state) {
5109             this.el.addClass('disabled');
5110             return;
5111         }
5112         
5113         this.el.removeClass('disabled');
5114         
5115         return;
5116     },
5117     
5118     setActive : function(state)
5119     {
5120         if(this.active == state){
5121             return;
5122         }
5123         
5124         this.active = state;
5125         
5126         if (state) {
5127             this.el.addClass('active');
5128             return;
5129         }
5130         
5131         this.el.removeClass('active');
5132         
5133         return;
5134     },
5135     
5136     isActive: function () 
5137     {
5138         return this.active;
5139     },
5140     
5141     setBadge : function(str)
5142     {
5143         if(!this.badgeEl){
5144             return;
5145         }
5146         
5147         this.badgeEl.dom.innerHTML = str;
5148     }
5149     
5150    
5151      
5152  
5153 });
5154  
5155
5156  /*
5157  * - LGPL
5158  *
5159  * row
5160  * 
5161  */
5162
5163 /**
5164  * @class Roo.bootstrap.Row
5165  * @extends Roo.bootstrap.Component
5166  * Bootstrap Row class (contains columns...)
5167  * 
5168  * @constructor
5169  * Create a new Row
5170  * @param {Object} config The config object
5171  */
5172
5173 Roo.bootstrap.Row = function(config){
5174     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5175 };
5176
5177 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5178     
5179     getAutoCreate : function(){
5180        return {
5181             cls: 'row clearfix'
5182        };
5183     }
5184     
5185     
5186 });
5187
5188  
5189
5190  /*
5191  * - LGPL
5192  *
5193  * element
5194  * 
5195  */
5196
5197 /**
5198  * @class Roo.bootstrap.Element
5199  * @extends Roo.bootstrap.Component
5200  * Bootstrap Element class
5201  * @cfg {String} html contents of the element
5202  * @cfg {String} tag tag of the element
5203  * @cfg {String} cls class of the element
5204  * @cfg {Boolean} preventDefault (true|false) default false
5205  * @cfg {Boolean} clickable (true|false) default false
5206  * 
5207  * @constructor
5208  * Create a new Element
5209  * @param {Object} config The config object
5210  */
5211
5212 Roo.bootstrap.Element = function(config){
5213     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5214     
5215     this.addEvents({
5216         // raw events
5217         /**
5218          * @event click
5219          * When a element is chick
5220          * @param {Roo.bootstrap.Element} this
5221          * @param {Roo.EventObject} e
5222          */
5223         "click" : true
5224     });
5225 };
5226
5227 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5228     
5229     tag: 'div',
5230     cls: '',
5231     html: '',
5232     preventDefault: false, 
5233     clickable: false,
5234     
5235     getAutoCreate : function(){
5236         
5237         var cfg = {
5238             tag: this.tag,
5239             // cls: this.cls, double assign in parent class Component.js :: onRender
5240             html: this.html
5241         };
5242         
5243         return cfg;
5244     },
5245     
5246     initEvents: function() 
5247     {
5248         Roo.bootstrap.Element.superclass.initEvents.call(this);
5249         
5250         if(this.clickable){
5251             this.el.on('click', this.onClick, this);
5252         }
5253         
5254     },
5255     
5256     onClick : function(e)
5257     {
5258         if(this.preventDefault){
5259             e.preventDefault();
5260         }
5261         
5262         this.fireEvent('click', this, e);
5263     },
5264     
5265     getValue : function()
5266     {
5267         return this.el.dom.innerHTML;
5268     },
5269     
5270     setValue : function(value)
5271     {
5272         this.el.dom.innerHTML = value;
5273     }
5274    
5275 });
5276
5277  
5278
5279  /*
5280  * - LGPL
5281  *
5282  * pagination
5283  * 
5284  */
5285
5286 /**
5287  * @class Roo.bootstrap.Pagination
5288  * @extends Roo.bootstrap.Component
5289  * Bootstrap Pagination class
5290  * @cfg {String} size xs | sm | md | lg
5291  * @cfg {Boolean} inverse false | true
5292  * 
5293  * @constructor
5294  * Create a new Pagination
5295  * @param {Object} config The config object
5296  */
5297
5298 Roo.bootstrap.Pagination = function(config){
5299     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5300 };
5301
5302 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5303     
5304     cls: false,
5305     size: false,
5306     inverse: false,
5307     
5308     getAutoCreate : function(){
5309         var cfg = {
5310             tag: 'ul',
5311                 cls: 'pagination'
5312         };
5313         if (this.inverse) {
5314             cfg.cls += ' inverse';
5315         }
5316         if (this.html) {
5317             cfg.html=this.html;
5318         }
5319         if (this.cls) {
5320             cfg.cls += " " + this.cls;
5321         }
5322         return cfg;
5323     }
5324    
5325 });
5326
5327  
5328
5329  /*
5330  * - LGPL
5331  *
5332  * Pagination item
5333  * 
5334  */
5335
5336
5337 /**
5338  * @class Roo.bootstrap.PaginationItem
5339  * @extends Roo.bootstrap.Component
5340  * Bootstrap PaginationItem class
5341  * @cfg {String} html text
5342  * @cfg {String} href the link
5343  * @cfg {Boolean} preventDefault (true | false) default true
5344  * @cfg {Boolean} active (true | false) default false
5345  * @cfg {Boolean} disabled default false
5346  * 
5347  * 
5348  * @constructor
5349  * Create a new PaginationItem
5350  * @param {Object} config The config object
5351  */
5352
5353
5354 Roo.bootstrap.PaginationItem = function(config){
5355     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5356     this.addEvents({
5357         // raw events
5358         /**
5359          * @event click
5360          * The raw click event for the entire grid.
5361          * @param {Roo.EventObject} e
5362          */
5363         "click" : true
5364     });
5365 };
5366
5367 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5368     
5369     href : false,
5370     html : false,
5371     preventDefault: true,
5372     active : false,
5373     cls : false,
5374     disabled: false,
5375     
5376     getAutoCreate : function(){
5377         var cfg= {
5378             tag: 'li',
5379             cn: [
5380                 {
5381                     tag : 'a',
5382                     href : this.href ? this.href : '#',
5383                     html : this.html ? this.html : ''
5384                 }
5385             ]
5386         };
5387         
5388         if(this.cls){
5389             cfg.cls = this.cls;
5390         }
5391         
5392         if(this.disabled){
5393             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5394         }
5395         
5396         if(this.active){
5397             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5398         }
5399         
5400         return cfg;
5401     },
5402     
5403     initEvents: function() {
5404         
5405         this.el.on('click', this.onClick, this);
5406         
5407     },
5408     onClick : function(e)
5409     {
5410         Roo.log('PaginationItem on click ');
5411         if(this.preventDefault){
5412             e.preventDefault();
5413         }
5414         
5415         if(this.disabled){
5416             return;
5417         }
5418         
5419         this.fireEvent('click', this, e);
5420     }
5421    
5422 });
5423
5424  
5425
5426  /*
5427  * - LGPL
5428  *
5429  * slider
5430  * 
5431  */
5432
5433
5434 /**
5435  * @class Roo.bootstrap.Slider
5436  * @extends Roo.bootstrap.Component
5437  * Bootstrap Slider class
5438  *    
5439  * @constructor
5440  * Create a new Slider
5441  * @param {Object} config The config object
5442  */
5443
5444 Roo.bootstrap.Slider = function(config){
5445     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5446 };
5447
5448 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5449     
5450     getAutoCreate : function(){
5451         
5452         var cfg = {
5453             tag: 'div',
5454             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5455             cn: [
5456                 {
5457                     tag: 'a',
5458                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5459                 }
5460             ]
5461         };
5462         
5463         return cfg;
5464     }
5465    
5466 });
5467
5468  /*
5469  * Based on:
5470  * Ext JS Library 1.1.1
5471  * Copyright(c) 2006-2007, Ext JS, LLC.
5472  *
5473  * Originally Released Under LGPL - original licence link has changed is not relivant.
5474  *
5475  * Fork - LGPL
5476  * <script type="text/javascript">
5477  */
5478  
5479
5480 /**
5481  * @class Roo.grid.ColumnModel
5482  * @extends Roo.util.Observable
5483  * This is the default implementation of a ColumnModel used by the Grid. It defines
5484  * the columns in the grid.
5485  * <br>Usage:<br>
5486  <pre><code>
5487  var colModel = new Roo.grid.ColumnModel([
5488         {header: "Ticker", width: 60, sortable: true, locked: true},
5489         {header: "Company Name", width: 150, sortable: true},
5490         {header: "Market Cap.", width: 100, sortable: true},
5491         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5492         {header: "Employees", width: 100, sortable: true, resizable: false}
5493  ]);
5494  </code></pre>
5495  * <p>
5496  
5497  * The config options listed for this class are options which may appear in each
5498  * individual column definition.
5499  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5500  * @constructor
5501  * @param {Object} config An Array of column config objects. See this class's
5502  * config objects for details.
5503 */
5504 Roo.grid.ColumnModel = function(config){
5505         /**
5506      * The config passed into the constructor
5507      */
5508     this.config = config;
5509     this.lookup = {};
5510
5511     // if no id, create one
5512     // if the column does not have a dataIndex mapping,
5513     // map it to the order it is in the config
5514     for(var i = 0, len = config.length; i < len; i++){
5515         var c = config[i];
5516         if(typeof c.dataIndex == "undefined"){
5517             c.dataIndex = i;
5518         }
5519         if(typeof c.renderer == "string"){
5520             c.renderer = Roo.util.Format[c.renderer];
5521         }
5522         if(typeof c.id == "undefined"){
5523             c.id = Roo.id();
5524         }
5525         if(c.editor && c.editor.xtype){
5526             c.editor  = Roo.factory(c.editor, Roo.grid);
5527         }
5528         if(c.editor && c.editor.isFormField){
5529             c.editor = new Roo.grid.GridEditor(c.editor);
5530         }
5531         this.lookup[c.id] = c;
5532     }
5533
5534     /**
5535      * The width of columns which have no width specified (defaults to 100)
5536      * @type Number
5537      */
5538     this.defaultWidth = 100;
5539
5540     /**
5541      * Default sortable of columns which have no sortable specified (defaults to false)
5542      * @type Boolean
5543      */
5544     this.defaultSortable = false;
5545
5546     this.addEvents({
5547         /**
5548              * @event widthchange
5549              * Fires when the width of a column changes.
5550              * @param {ColumnModel} this
5551              * @param {Number} columnIndex The column index
5552              * @param {Number} newWidth The new width
5553              */
5554             "widthchange": true,
5555         /**
5556              * @event headerchange
5557              * Fires when the text of a header changes.
5558              * @param {ColumnModel} this
5559              * @param {Number} columnIndex The column index
5560              * @param {Number} newText The new header text
5561              */
5562             "headerchange": true,
5563         /**
5564              * @event hiddenchange
5565              * Fires when a column is hidden or "unhidden".
5566              * @param {ColumnModel} this
5567              * @param {Number} columnIndex The column index
5568              * @param {Boolean} hidden true if hidden, false otherwise
5569              */
5570             "hiddenchange": true,
5571             /**
5572          * @event columnmoved
5573          * Fires when a column is moved.
5574          * @param {ColumnModel} this
5575          * @param {Number} oldIndex
5576          * @param {Number} newIndex
5577          */
5578         "columnmoved" : true,
5579         /**
5580          * @event columlockchange
5581          * Fires when a column's locked state is changed
5582          * @param {ColumnModel} this
5583          * @param {Number} colIndex
5584          * @param {Boolean} locked true if locked
5585          */
5586         "columnlockchange" : true
5587     });
5588     Roo.grid.ColumnModel.superclass.constructor.call(this);
5589 };
5590 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5591     /**
5592      * @cfg {String} header The header text to display in the Grid view.
5593      */
5594     /**
5595      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5596      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5597      * specified, the column's index is used as an index into the Record's data Array.
5598      */
5599     /**
5600      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5601      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5602      */
5603     /**
5604      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5605      * Defaults to the value of the {@link #defaultSortable} property.
5606      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5607      */
5608     /**
5609      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5610      */
5611     /**
5612      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5613      */
5614     /**
5615      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5616      */
5617     /**
5618      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5619      */
5620     /**
5621      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5622      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5623      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5624      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5625      */
5626        /**
5627      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5628      */
5629     /**
5630      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5631      */
5632     /**
5633      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5634      */
5635     /**
5636      * @cfg {String} cursor (Optional)
5637      */
5638     /**
5639      * @cfg {String} tooltip (Optional)
5640      */
5641     /**
5642      * @cfg {Number} xs (Optional)
5643      */
5644     /**
5645      * @cfg {Number} sm (Optional)
5646      */
5647     /**
5648      * @cfg {Number} md (Optional)
5649      */
5650     /**
5651      * @cfg {Number} lg (Optional)
5652      */
5653     /**
5654      * Returns the id of the column at the specified index.
5655      * @param {Number} index The column index
5656      * @return {String} the id
5657      */
5658     getColumnId : function(index){
5659         return this.config[index].id;
5660     },
5661
5662     /**
5663      * Returns the column for a specified id.
5664      * @param {String} id The column id
5665      * @return {Object} the column
5666      */
5667     getColumnById : function(id){
5668         return this.lookup[id];
5669     },
5670
5671     
5672     /**
5673      * Returns the column for a specified dataIndex.
5674      * @param {String} dataIndex The column dataIndex
5675      * @return {Object|Boolean} the column or false if not found
5676      */
5677     getColumnByDataIndex: function(dataIndex){
5678         var index = this.findColumnIndex(dataIndex);
5679         return index > -1 ? this.config[index] : false;
5680     },
5681     
5682     /**
5683      * Returns the index for a specified column id.
5684      * @param {String} id The column id
5685      * @return {Number} the index, or -1 if not found
5686      */
5687     getIndexById : function(id){
5688         for(var i = 0, len = this.config.length; i < len; i++){
5689             if(this.config[i].id == id){
5690                 return i;
5691             }
5692         }
5693         return -1;
5694     },
5695     
5696     /**
5697      * Returns the index for a specified column dataIndex.
5698      * @param {String} dataIndex The column dataIndex
5699      * @return {Number} the index, or -1 if not found
5700      */
5701     
5702     findColumnIndex : function(dataIndex){
5703         for(var i = 0, len = this.config.length; i < len; i++){
5704             if(this.config[i].dataIndex == dataIndex){
5705                 return i;
5706             }
5707         }
5708         return -1;
5709     },
5710     
5711     
5712     moveColumn : function(oldIndex, newIndex){
5713         var c = this.config[oldIndex];
5714         this.config.splice(oldIndex, 1);
5715         this.config.splice(newIndex, 0, c);
5716         this.dataMap = null;
5717         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5718     },
5719
5720     isLocked : function(colIndex){
5721         return this.config[colIndex].locked === true;
5722     },
5723
5724     setLocked : function(colIndex, value, suppressEvent){
5725         if(this.isLocked(colIndex) == value){
5726             return;
5727         }
5728         this.config[colIndex].locked = value;
5729         if(!suppressEvent){
5730             this.fireEvent("columnlockchange", this, colIndex, value);
5731         }
5732     },
5733
5734     getTotalLockedWidth : function(){
5735         var totalWidth = 0;
5736         for(var i = 0; i < this.config.length; i++){
5737             if(this.isLocked(i) && !this.isHidden(i)){
5738                 this.totalWidth += this.getColumnWidth(i);
5739             }
5740         }
5741         return totalWidth;
5742     },
5743
5744     getLockedCount : function(){
5745         for(var i = 0, len = this.config.length; i < len; i++){
5746             if(!this.isLocked(i)){
5747                 return i;
5748             }
5749         }
5750         
5751         return this.config.length;
5752     },
5753
5754     /**
5755      * Returns the number of columns.
5756      * @return {Number}
5757      */
5758     getColumnCount : function(visibleOnly){
5759         if(visibleOnly === true){
5760             var c = 0;
5761             for(var i = 0, len = this.config.length; i < len; i++){
5762                 if(!this.isHidden(i)){
5763                     c++;
5764                 }
5765             }
5766             return c;
5767         }
5768         return this.config.length;
5769     },
5770
5771     /**
5772      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5773      * @param {Function} fn
5774      * @param {Object} scope (optional)
5775      * @return {Array} result
5776      */
5777     getColumnsBy : function(fn, scope){
5778         var r = [];
5779         for(var i = 0, len = this.config.length; i < len; i++){
5780             var c = this.config[i];
5781             if(fn.call(scope||this, c, i) === true){
5782                 r[r.length] = c;
5783             }
5784         }
5785         return r;
5786     },
5787
5788     /**
5789      * Returns true if the specified column is sortable.
5790      * @param {Number} col The column index
5791      * @return {Boolean}
5792      */
5793     isSortable : function(col){
5794         if(typeof this.config[col].sortable == "undefined"){
5795             return this.defaultSortable;
5796         }
5797         return this.config[col].sortable;
5798     },
5799
5800     /**
5801      * Returns the rendering (formatting) function defined for the column.
5802      * @param {Number} col The column index.
5803      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5804      */
5805     getRenderer : function(col){
5806         if(!this.config[col].renderer){
5807             return Roo.grid.ColumnModel.defaultRenderer;
5808         }
5809         return this.config[col].renderer;
5810     },
5811
5812     /**
5813      * Sets the rendering (formatting) function for a column.
5814      * @param {Number} col The column index
5815      * @param {Function} fn The function to use to process the cell's raw data
5816      * to return HTML markup for the grid view. The render function is called with
5817      * the following parameters:<ul>
5818      * <li>Data value.</li>
5819      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5820      * <li>css A CSS style string to apply to the table cell.</li>
5821      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5822      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5823      * <li>Row index</li>
5824      * <li>Column index</li>
5825      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5826      */
5827     setRenderer : function(col, fn){
5828         this.config[col].renderer = fn;
5829     },
5830
5831     /**
5832      * Returns the width for the specified column.
5833      * @param {Number} col The column index
5834      * @return {Number}
5835      */
5836     getColumnWidth : function(col){
5837         return this.config[col].width * 1 || this.defaultWidth;
5838     },
5839
5840     /**
5841      * Sets the width for a column.
5842      * @param {Number} col The column index
5843      * @param {Number} width The new width
5844      */
5845     setColumnWidth : function(col, width, suppressEvent){
5846         this.config[col].width = width;
5847         this.totalWidth = null;
5848         if(!suppressEvent){
5849              this.fireEvent("widthchange", this, col, width);
5850         }
5851     },
5852
5853     /**
5854      * Returns the total width of all columns.
5855      * @param {Boolean} includeHidden True to include hidden column widths
5856      * @return {Number}
5857      */
5858     getTotalWidth : function(includeHidden){
5859         if(!this.totalWidth){
5860             this.totalWidth = 0;
5861             for(var i = 0, len = this.config.length; i < len; i++){
5862                 if(includeHidden || !this.isHidden(i)){
5863                     this.totalWidth += this.getColumnWidth(i);
5864                 }
5865             }
5866         }
5867         return this.totalWidth;
5868     },
5869
5870     /**
5871      * Returns the header for the specified column.
5872      * @param {Number} col The column index
5873      * @return {String}
5874      */
5875     getColumnHeader : function(col){
5876         return this.config[col].header;
5877     },
5878
5879     /**
5880      * Sets the header for a column.
5881      * @param {Number} col The column index
5882      * @param {String} header The new header
5883      */
5884     setColumnHeader : function(col, header){
5885         this.config[col].header = header;
5886         this.fireEvent("headerchange", this, col, header);
5887     },
5888
5889     /**
5890      * Returns the tooltip for the specified column.
5891      * @param {Number} col The column index
5892      * @return {String}
5893      */
5894     getColumnTooltip : function(col){
5895             return this.config[col].tooltip;
5896     },
5897     /**
5898      * Sets the tooltip for a column.
5899      * @param {Number} col The column index
5900      * @param {String} tooltip The new tooltip
5901      */
5902     setColumnTooltip : function(col, tooltip){
5903             this.config[col].tooltip = tooltip;
5904     },
5905
5906     /**
5907      * Returns the dataIndex for the specified column.
5908      * @param {Number} col The column index
5909      * @return {Number}
5910      */
5911     getDataIndex : function(col){
5912         return this.config[col].dataIndex;
5913     },
5914
5915     /**
5916      * Sets the dataIndex for a column.
5917      * @param {Number} col The column index
5918      * @param {Number} dataIndex The new dataIndex
5919      */
5920     setDataIndex : function(col, dataIndex){
5921         this.config[col].dataIndex = dataIndex;
5922     },
5923
5924     
5925     
5926     /**
5927      * Returns true if the cell is editable.
5928      * @param {Number} colIndex The column index
5929      * @param {Number} rowIndex The row index - this is nto actually used..?
5930      * @return {Boolean}
5931      */
5932     isCellEditable : function(colIndex, rowIndex){
5933         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5934     },
5935
5936     /**
5937      * Returns the editor defined for the cell/column.
5938      * return false or null to disable editing.
5939      * @param {Number} colIndex The column index
5940      * @param {Number} rowIndex The row index
5941      * @return {Object}
5942      */
5943     getCellEditor : function(colIndex, rowIndex){
5944         return this.config[colIndex].editor;
5945     },
5946
5947     /**
5948      * Sets if a column is editable.
5949      * @param {Number} col The column index
5950      * @param {Boolean} editable True if the column is editable
5951      */
5952     setEditable : function(col, editable){
5953         this.config[col].editable = editable;
5954     },
5955
5956
5957     /**
5958      * Returns true if the column is hidden.
5959      * @param {Number} colIndex The column index
5960      * @return {Boolean}
5961      */
5962     isHidden : function(colIndex){
5963         return this.config[colIndex].hidden;
5964     },
5965
5966
5967     /**
5968      * Returns true if the column width cannot be changed
5969      */
5970     isFixed : function(colIndex){
5971         return this.config[colIndex].fixed;
5972     },
5973
5974     /**
5975      * Returns true if the column can be resized
5976      * @return {Boolean}
5977      */
5978     isResizable : function(colIndex){
5979         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5980     },
5981     /**
5982      * Sets if a column is hidden.
5983      * @param {Number} colIndex The column index
5984      * @param {Boolean} hidden True if the column is hidden
5985      */
5986     setHidden : function(colIndex, hidden){
5987         this.config[colIndex].hidden = hidden;
5988         this.totalWidth = null;
5989         this.fireEvent("hiddenchange", this, colIndex, hidden);
5990     },
5991
5992     /**
5993      * Sets the editor for a column.
5994      * @param {Number} col The column index
5995      * @param {Object} editor The editor object
5996      */
5997     setEditor : function(col, editor){
5998         this.config[col].editor = editor;
5999     }
6000 });
6001
6002 Roo.grid.ColumnModel.defaultRenderer = function(value)
6003 {
6004     if(typeof value == "object") {
6005         return value;
6006     }
6007         if(typeof value == "string" && value.length < 1){
6008             return "&#160;";
6009         }
6010     
6011         return String.format("{0}", value);
6012 };
6013
6014 // Alias for backwards compatibility
6015 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6016 /*
6017  * Based on:
6018  * Ext JS Library 1.1.1
6019  * Copyright(c) 2006-2007, Ext JS, LLC.
6020  *
6021  * Originally Released Under LGPL - original licence link has changed is not relivant.
6022  *
6023  * Fork - LGPL
6024  * <script type="text/javascript">
6025  */
6026  
6027 /**
6028  * @class Roo.LoadMask
6029  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6030  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6031  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6032  * element's UpdateManager load indicator and will be destroyed after the initial load.
6033  * @constructor
6034  * Create a new LoadMask
6035  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6036  * @param {Object} config The config object
6037  */
6038 Roo.LoadMask = function(el, config){
6039     this.el = Roo.get(el);
6040     Roo.apply(this, config);
6041     if(this.store){
6042         this.store.on('beforeload', this.onBeforeLoad, this);
6043         this.store.on('load', this.onLoad, this);
6044         this.store.on('loadexception', this.onLoadException, this);
6045         this.removeMask = false;
6046     }else{
6047         var um = this.el.getUpdateManager();
6048         um.showLoadIndicator = false; // disable the default indicator
6049         um.on('beforeupdate', this.onBeforeLoad, this);
6050         um.on('update', this.onLoad, this);
6051         um.on('failure', this.onLoad, this);
6052         this.removeMask = true;
6053     }
6054 };
6055
6056 Roo.LoadMask.prototype = {
6057     /**
6058      * @cfg {Boolean} removeMask
6059      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6060      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6061      */
6062     /**
6063      * @cfg {String} msg
6064      * The text to display in a centered loading message box (defaults to 'Loading...')
6065      */
6066     msg : 'Loading...',
6067     /**
6068      * @cfg {String} msgCls
6069      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6070      */
6071     msgCls : 'x-mask-loading',
6072
6073     /**
6074      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6075      * @type Boolean
6076      */
6077     disabled: false,
6078
6079     /**
6080      * Disables the mask to prevent it from being displayed
6081      */
6082     disable : function(){
6083        this.disabled = true;
6084     },
6085
6086     /**
6087      * Enables the mask so that it can be displayed
6088      */
6089     enable : function(){
6090         this.disabled = false;
6091     },
6092     
6093     onLoadException : function()
6094     {
6095         Roo.log(arguments);
6096         
6097         if (typeof(arguments[3]) != 'undefined') {
6098             Roo.MessageBox.alert("Error loading",arguments[3]);
6099         } 
6100         /*
6101         try {
6102             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6103                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6104             }   
6105         } catch(e) {
6106             
6107         }
6108         */
6109     
6110         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6111     },
6112     // private
6113     onLoad : function()
6114     {
6115         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6116     },
6117
6118     // private
6119     onBeforeLoad : function(){
6120         if(!this.disabled){
6121             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6122         }
6123     },
6124
6125     // private
6126     destroy : function(){
6127         if(this.store){
6128             this.store.un('beforeload', this.onBeforeLoad, this);
6129             this.store.un('load', this.onLoad, this);
6130             this.store.un('loadexception', this.onLoadException, this);
6131         }else{
6132             var um = this.el.getUpdateManager();
6133             um.un('beforeupdate', this.onBeforeLoad, this);
6134             um.un('update', this.onLoad, this);
6135             um.un('failure', this.onLoad, this);
6136         }
6137     }
6138 };/*
6139  * - LGPL
6140  *
6141  * table
6142  * 
6143  */
6144
6145 /**
6146  * @class Roo.bootstrap.Table
6147  * @extends Roo.bootstrap.Component
6148  * Bootstrap Table class
6149  * @cfg {String} cls table class
6150  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6151  * @cfg {String} bgcolor Specifies the background color for a table
6152  * @cfg {Number} border Specifies whether the table cells should have borders or not
6153  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6154  * @cfg {Number} cellspacing Specifies the space between cells
6155  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6156  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6157  * @cfg {String} sortable Specifies that the table should be sortable
6158  * @cfg {String} summary Specifies a summary of the content of a table
6159  * @cfg {Number} width Specifies the width of a table
6160  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6161  * 
6162  * @cfg {boolean} striped Should the rows be alternative striped
6163  * @cfg {boolean} bordered Add borders to the table
6164  * @cfg {boolean} hover Add hover highlighting
6165  * @cfg {boolean} condensed Format condensed
6166  * @cfg {boolean} responsive Format condensed
6167  * @cfg {Boolean} loadMask (true|false) default false
6168  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6169  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6170  * @cfg {Boolean} rowSelection (true|false) default false
6171  * @cfg {Boolean} cellSelection (true|false) default false
6172  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6173  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6174  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6175  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6176  
6177  * 
6178  * @constructor
6179  * Create a new Table
6180  * @param {Object} config The config object
6181  */
6182
6183 Roo.bootstrap.Table = function(config){
6184     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6185     
6186   
6187     
6188     // BC...
6189     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6190     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6191     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6192     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6193     
6194     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6195     if (this.sm) {
6196         this.sm.grid = this;
6197         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6198         this.sm = this.selModel;
6199         this.sm.xmodule = this.xmodule || false;
6200     }
6201     
6202     if (this.cm && typeof(this.cm.config) == 'undefined') {
6203         this.colModel = new Roo.grid.ColumnModel(this.cm);
6204         this.cm = this.colModel;
6205         this.cm.xmodule = this.xmodule || false;
6206     }
6207     if (this.store) {
6208         this.store= Roo.factory(this.store, Roo.data);
6209         this.ds = this.store;
6210         this.ds.xmodule = this.xmodule || false;
6211          
6212     }
6213     if (this.footer && this.store) {
6214         this.footer.dataSource = this.ds;
6215         this.footer = Roo.factory(this.footer);
6216     }
6217     
6218     /** @private */
6219     this.addEvents({
6220         /**
6221          * @event cellclick
6222          * Fires when a cell is clicked
6223          * @param {Roo.bootstrap.Table} this
6224          * @param {Roo.Element} el
6225          * @param {Number} rowIndex
6226          * @param {Number} columnIndex
6227          * @param {Roo.EventObject} e
6228          */
6229         "cellclick" : true,
6230         /**
6231          * @event celldblclick
6232          * Fires when a cell is double clicked
6233          * @param {Roo.bootstrap.Table} this
6234          * @param {Roo.Element} el
6235          * @param {Number} rowIndex
6236          * @param {Number} columnIndex
6237          * @param {Roo.EventObject} e
6238          */
6239         "celldblclick" : true,
6240         /**
6241          * @event rowclick
6242          * Fires when a row is clicked
6243          * @param {Roo.bootstrap.Table} this
6244          * @param {Roo.Element} el
6245          * @param {Number} rowIndex
6246          * @param {Roo.EventObject} e
6247          */
6248         "rowclick" : true,
6249         /**
6250          * @event rowdblclick
6251          * Fires when a row is double clicked
6252          * @param {Roo.bootstrap.Table} this
6253          * @param {Roo.Element} el
6254          * @param {Number} rowIndex
6255          * @param {Roo.EventObject} e
6256          */
6257         "rowdblclick" : true,
6258         /**
6259          * @event mouseover
6260          * Fires when a mouseover occur
6261          * @param {Roo.bootstrap.Table} this
6262          * @param {Roo.Element} el
6263          * @param {Number} rowIndex
6264          * @param {Number} columnIndex
6265          * @param {Roo.EventObject} e
6266          */
6267         "mouseover" : true,
6268         /**
6269          * @event mouseout
6270          * Fires when a mouseout occur
6271          * @param {Roo.bootstrap.Table} this
6272          * @param {Roo.Element} el
6273          * @param {Number} rowIndex
6274          * @param {Number} columnIndex
6275          * @param {Roo.EventObject} e
6276          */
6277         "mouseout" : true,
6278         /**
6279          * @event rowclass
6280          * Fires when a row is rendered, so you can change add a style to it.
6281          * @param {Roo.bootstrap.Table} this
6282          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6283          */
6284         'rowclass' : true,
6285           /**
6286          * @event rowsrendered
6287          * Fires when all the  rows have been rendered
6288          * @param {Roo.bootstrap.Table} this
6289          */
6290         'rowsrendered' : true,
6291         /**
6292          * @event contextmenu
6293          * The raw contextmenu event for the entire grid.
6294          * @param {Roo.EventObject} e
6295          */
6296         "contextmenu" : true,
6297         /**
6298          * @event rowcontextmenu
6299          * Fires when a row is right clicked
6300          * @param {Roo.bootstrap.Table} this
6301          * @param {Number} rowIndex
6302          * @param {Roo.EventObject} e
6303          */
6304         "rowcontextmenu" : true,
6305         /**
6306          * @event cellcontextmenu
6307          * Fires when a cell is right clicked
6308          * @param {Roo.bootstrap.Table} this
6309          * @param {Number} rowIndex
6310          * @param {Number} cellIndex
6311          * @param {Roo.EventObject} e
6312          */
6313          "cellcontextmenu" : true,
6314          /**
6315          * @event headercontextmenu
6316          * Fires when a header is right clicked
6317          * @param {Roo.bootstrap.Table} this
6318          * @param {Number} columnIndex
6319          * @param {Roo.EventObject} e
6320          */
6321         "headercontextmenu" : true
6322     });
6323 };
6324
6325 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6326     
6327     cls: false,
6328     align: false,
6329     bgcolor: false,
6330     border: false,
6331     cellpadding: false,
6332     cellspacing: false,
6333     frame: false,
6334     rules: false,
6335     sortable: false,
6336     summary: false,
6337     width: false,
6338     striped : false,
6339     scrollBody : false,
6340     bordered: false,
6341     hover:  false,
6342     condensed : false,
6343     responsive : false,
6344     sm : false,
6345     cm : false,
6346     store : false,
6347     loadMask : false,
6348     footerShow : true,
6349     headerShow : true,
6350   
6351     rowSelection : false,
6352     cellSelection : false,
6353     layout : false,
6354     
6355     // Roo.Element - the tbody
6356     mainBody: false,
6357     // Roo.Element - thead element
6358     mainHead: false,
6359     
6360     container: false, // used by gridpanel...
6361     
6362     lazyLoad : false,
6363     
6364     CSS : Roo.util.CSS,
6365     
6366     auto_hide_footer : false,
6367     
6368     getAutoCreate : function()
6369     {
6370         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6371         
6372         cfg = {
6373             tag: 'table',
6374             cls : 'table',
6375             cn : []
6376         };
6377         if (this.scrollBody) {
6378             cfg.cls += ' table-body-fixed';
6379         }    
6380         if (this.striped) {
6381             cfg.cls += ' table-striped';
6382         }
6383         
6384         if (this.hover) {
6385             cfg.cls += ' table-hover';
6386         }
6387         if (this.bordered) {
6388             cfg.cls += ' table-bordered';
6389         }
6390         if (this.condensed) {
6391             cfg.cls += ' table-condensed';
6392         }
6393         if (this.responsive) {
6394             cfg.cls += ' table-responsive';
6395         }
6396         
6397         if (this.cls) {
6398             cfg.cls+=  ' ' +this.cls;
6399         }
6400         
6401         // this lot should be simplifed...
6402         var _t = this;
6403         var cp = [
6404             'align',
6405             'bgcolor',
6406             'border',
6407             'cellpadding',
6408             'cellspacing',
6409             'frame',
6410             'rules',
6411             'sortable',
6412             'summary',
6413             'width'
6414         ].forEach(function(k) {
6415             if (_t[k]) {
6416                 cfg[k] = _t[k];
6417             }
6418         });
6419         
6420         
6421         if (this.layout) {
6422             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6423         }
6424         
6425         if(this.store || this.cm){
6426             if(this.headerShow){
6427                 cfg.cn.push(this.renderHeader());
6428             }
6429             
6430             cfg.cn.push(this.renderBody());
6431             
6432             if(this.footerShow){
6433                 cfg.cn.push(this.renderFooter());
6434             }
6435             // where does this come from?
6436             //cfg.cls+=  ' TableGrid';
6437         }
6438         
6439         return { cn : [ cfg ] };
6440     },
6441     
6442     initEvents : function()
6443     {   
6444         if(!this.store || !this.cm){
6445             return;
6446         }
6447         if (this.selModel) {
6448             this.selModel.initEvents();
6449         }
6450         
6451         
6452         //Roo.log('initEvents with ds!!!!');
6453         
6454         this.mainBody = this.el.select('tbody', true).first();
6455         this.mainHead = this.el.select('thead', true).first();
6456         this.mainFoot = this.el.select('tfoot', true).first();
6457         
6458         
6459         
6460         var _this = this;
6461         
6462         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6463             e.on('click', _this.sort, _this);
6464         });
6465         
6466         this.mainBody.on("click", this.onClick, this);
6467         this.mainBody.on("dblclick", this.onDblClick, this);
6468         
6469         // why is this done????? = it breaks dialogs??
6470         //this.parent().el.setStyle('position', 'relative');
6471         
6472         
6473         if (this.footer) {
6474             this.footer.parentId = this.id;
6475             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6476             
6477             if(this.lazyLoad){
6478                 this.el.select('tfoot tr td').first().addClass('hide');
6479             }
6480         } 
6481         
6482         if(this.loadMask) {
6483             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6484         }
6485         
6486         this.store.on('load', this.onLoad, this);
6487         this.store.on('beforeload', this.onBeforeLoad, this);
6488         this.store.on('update', this.onUpdate, this);
6489         this.store.on('add', this.onAdd, this);
6490         this.store.on("clear", this.clear, this);
6491         
6492         this.el.on("contextmenu", this.onContextMenu, this);
6493         
6494         this.mainBody.on('scroll', this.onBodyScroll, this);
6495         
6496         this.cm.on("headerchange", this.onHeaderChange, this);
6497         
6498         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6499         
6500     },
6501     
6502     onContextMenu : function(e, t)
6503     {
6504         this.processEvent("contextmenu", e);
6505     },
6506     
6507     processEvent : function(name, e)
6508     {
6509         if (name != 'touchstart' ) {
6510             this.fireEvent(name, e);    
6511         }
6512         
6513         var t = e.getTarget();
6514         
6515         var cell = Roo.get(t);
6516         
6517         if(!cell){
6518             return;
6519         }
6520         
6521         if(cell.findParent('tfoot', false, true)){
6522             return;
6523         }
6524         
6525         if(cell.findParent('thead', false, true)){
6526             
6527             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6528                 cell = Roo.get(t).findParent('th', false, true);
6529                 if (!cell) {
6530                     Roo.log("failed to find th in thead?");
6531                     Roo.log(e.getTarget());
6532                     return;
6533                 }
6534             }
6535             
6536             var cellIndex = cell.dom.cellIndex;
6537             
6538             var ename = name == 'touchstart' ? 'click' : name;
6539             this.fireEvent("header" + ename, this, cellIndex, e);
6540             
6541             return;
6542         }
6543         
6544         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6545             cell = Roo.get(t).findParent('td', false, true);
6546             if (!cell) {
6547                 Roo.log("failed to find th in tbody?");
6548                 Roo.log(e.getTarget());
6549                 return;
6550             }
6551         }
6552         
6553         var row = cell.findParent('tr', false, true);
6554         var cellIndex = cell.dom.cellIndex;
6555         var rowIndex = row.dom.rowIndex - 1;
6556         
6557         if(row !== false){
6558             
6559             this.fireEvent("row" + name, this, rowIndex, e);
6560             
6561             if(cell !== false){
6562             
6563                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6564             }
6565         }
6566         
6567     },
6568     
6569     onMouseover : function(e, el)
6570     {
6571         var cell = Roo.get(el);
6572         
6573         if(!cell){
6574             return;
6575         }
6576         
6577         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6578             cell = cell.findParent('td', false, true);
6579         }
6580         
6581         var row = cell.findParent('tr', false, true);
6582         var cellIndex = cell.dom.cellIndex;
6583         var rowIndex = row.dom.rowIndex - 1; // start from 0
6584         
6585         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6586         
6587     },
6588     
6589     onMouseout : function(e, el)
6590     {
6591         var cell = Roo.get(el);
6592         
6593         if(!cell){
6594             return;
6595         }
6596         
6597         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6598             cell = cell.findParent('td', false, true);
6599         }
6600         
6601         var row = cell.findParent('tr', false, true);
6602         var cellIndex = cell.dom.cellIndex;
6603         var rowIndex = row.dom.rowIndex - 1; // start from 0
6604         
6605         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6606         
6607     },
6608     
6609     onClick : function(e, el)
6610     {
6611         var cell = Roo.get(el);
6612         
6613         if(!cell || (!this.cellSelection && !this.rowSelection)){
6614             return;
6615         }
6616         
6617         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6618             cell = cell.findParent('td', false, true);
6619         }
6620         
6621         if(!cell || typeof(cell) == 'undefined'){
6622             return;
6623         }
6624         
6625         var row = cell.findParent('tr', false, true);
6626         
6627         if(!row || typeof(row) == 'undefined'){
6628             return;
6629         }
6630         
6631         var cellIndex = cell.dom.cellIndex;
6632         var rowIndex = this.getRowIndex(row);
6633         
6634         // why??? - should these not be based on SelectionModel?
6635         if(this.cellSelection){
6636             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6637         }
6638         
6639         if(this.rowSelection){
6640             this.fireEvent('rowclick', this, row, rowIndex, e);
6641         }
6642         
6643         
6644     },
6645         
6646     onDblClick : function(e,el)
6647     {
6648         var cell = Roo.get(el);
6649         
6650         if(!cell || (!this.cellSelection && !this.rowSelection)){
6651             return;
6652         }
6653         
6654         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6655             cell = cell.findParent('td', false, true);
6656         }
6657         
6658         if(!cell || typeof(cell) == 'undefined'){
6659             return;
6660         }
6661         
6662         var row = cell.findParent('tr', false, true);
6663         
6664         if(!row || typeof(row) == 'undefined'){
6665             return;
6666         }
6667         
6668         var cellIndex = cell.dom.cellIndex;
6669         var rowIndex = this.getRowIndex(row);
6670         
6671         if(this.cellSelection){
6672             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6673         }
6674         
6675         if(this.rowSelection){
6676             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6677         }
6678     },
6679     
6680     sort : function(e,el)
6681     {
6682         var col = Roo.get(el);
6683         
6684         if(!col.hasClass('sortable')){
6685             return;
6686         }
6687         
6688         var sort = col.attr('sort');
6689         var dir = 'ASC';
6690         
6691         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6692             dir = 'DESC';
6693         }
6694         
6695         this.store.sortInfo = {field : sort, direction : dir};
6696         
6697         if (this.footer) {
6698             Roo.log("calling footer first");
6699             this.footer.onClick('first');
6700         } else {
6701         
6702             this.store.load({ params : { start : 0 } });
6703         }
6704     },
6705     
6706     renderHeader : function()
6707     {
6708         var header = {
6709             tag: 'thead',
6710             cn : []
6711         };
6712         
6713         var cm = this.cm;
6714         this.totalWidth = 0;
6715         
6716         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6717             
6718             var config = cm.config[i];
6719             
6720             var c = {
6721                 tag: 'th',
6722                 cls : 'x-hcol-' + i,
6723                 style : '',
6724                 html: cm.getColumnHeader(i)
6725             };
6726             
6727             var hh = '';
6728             
6729             if(typeof(config.sortable) != 'undefined' && config.sortable){
6730                 c.cls = 'sortable';
6731                 c.html = '<i class="glyphicon"></i>' + c.html;
6732             }
6733             
6734             if(typeof(config.lgHeader) != 'undefined'){
6735                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6736             }
6737             
6738             if(typeof(config.mdHeader) != 'undefined'){
6739                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6740             }
6741             
6742             if(typeof(config.smHeader) != 'undefined'){
6743                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6744             }
6745             
6746             if(typeof(config.xsHeader) != 'undefined'){
6747                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6748             }
6749             
6750             if(hh.length){
6751                 c.html = hh;
6752             }
6753             
6754             if(typeof(config.tooltip) != 'undefined'){
6755                 c.tooltip = config.tooltip;
6756             }
6757             
6758             if(typeof(config.colspan) != 'undefined'){
6759                 c.colspan = config.colspan;
6760             }
6761             
6762             if(typeof(config.hidden) != 'undefined' && config.hidden){
6763                 c.style += ' display:none;';
6764             }
6765             
6766             if(typeof(config.dataIndex) != 'undefined'){
6767                 c.sort = config.dataIndex;
6768             }
6769             
6770            
6771             
6772             if(typeof(config.align) != 'undefined' && config.align.length){
6773                 c.style += ' text-align:' + config.align + ';';
6774             }
6775             
6776             if(typeof(config.width) != 'undefined'){
6777                 c.style += ' width:' + config.width + 'px;';
6778                 this.totalWidth += config.width;
6779             } else {
6780                 this.totalWidth += 100; // assume minimum of 100 per column?
6781             }
6782             
6783             if(typeof(config.cls) != 'undefined'){
6784                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6785             }
6786             
6787             ['xs','sm','md','lg'].map(function(size){
6788                 
6789                 if(typeof(config[size]) == 'undefined'){
6790                     return;
6791                 }
6792                 
6793                 if (!config[size]) { // 0 = hidden
6794                     c.cls += ' hidden-' + size;
6795                     return;
6796                 }
6797                 
6798                 c.cls += ' col-' + size + '-' + config[size];
6799
6800             });
6801             
6802             header.cn.push(c)
6803         }
6804         
6805         return header;
6806     },
6807     
6808     renderBody : function()
6809     {
6810         var body = {
6811             tag: 'tbody',
6812             cn : [
6813                 {
6814                     tag: 'tr',
6815                     cn : [
6816                         {
6817                             tag : 'td',
6818                             colspan :  this.cm.getColumnCount()
6819                         }
6820                     ]
6821                 }
6822             ]
6823         };
6824         
6825         return body;
6826     },
6827     
6828     renderFooter : function()
6829     {
6830         var footer = {
6831             tag: 'tfoot',
6832             cn : [
6833                 {
6834                     tag: 'tr',
6835                     cn : [
6836                         {
6837                             tag : 'td',
6838                             colspan :  this.cm.getColumnCount()
6839                         }
6840                     ]
6841                 }
6842             ]
6843         };
6844         
6845         return footer;
6846     },
6847     
6848     
6849     
6850     onLoad : function()
6851     {
6852 //        Roo.log('ds onload');
6853         this.clear();
6854         
6855         var _this = this;
6856         var cm = this.cm;
6857         var ds = this.store;
6858         
6859         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6860             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6861             if (_this.store.sortInfo) {
6862                     
6863                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6864                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6865                 }
6866                 
6867                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6868                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6869                 }
6870             }
6871         });
6872         
6873         var tbody =  this.mainBody;
6874               
6875         if(ds.getCount() > 0){
6876             ds.data.each(function(d,rowIndex){
6877                 var row =  this.renderRow(cm, ds, rowIndex);
6878                 
6879                 tbody.createChild(row);
6880                 
6881                 var _this = this;
6882                 
6883                 if(row.cellObjects.length){
6884                     Roo.each(row.cellObjects, function(r){
6885                         _this.renderCellObject(r);
6886                     })
6887                 }
6888                 
6889             }, this);
6890         }
6891         
6892         var tfoot = this.el.select('tfoot', true).first();
6893         
6894         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6895             
6896             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6897             
6898             var total = this.ds.getTotalCount();
6899             
6900             if(this.footer.pageSize < total){
6901                 this.mainFoot.show();
6902             }
6903         }
6904         
6905         Roo.each(this.el.select('tbody td', true).elements, function(e){
6906             e.on('mouseover', _this.onMouseover, _this);
6907         });
6908         
6909         Roo.each(this.el.select('tbody td', true).elements, function(e){
6910             e.on('mouseout', _this.onMouseout, _this);
6911         });
6912         this.fireEvent('rowsrendered', this);
6913         
6914         this.autoSize();
6915     },
6916     
6917     
6918     onUpdate : function(ds,record)
6919     {
6920         this.refreshRow(record);
6921         this.autoSize();
6922     },
6923     
6924     onRemove : function(ds, record, index, isUpdate){
6925         if(isUpdate !== true){
6926             this.fireEvent("beforerowremoved", this, index, record);
6927         }
6928         var bt = this.mainBody.dom;
6929         
6930         var rows = this.el.select('tbody > tr', true).elements;
6931         
6932         if(typeof(rows[index]) != 'undefined'){
6933             bt.removeChild(rows[index].dom);
6934         }
6935         
6936 //        if(bt.rows[index]){
6937 //            bt.removeChild(bt.rows[index]);
6938 //        }
6939         
6940         if(isUpdate !== true){
6941             //this.stripeRows(index);
6942             //this.syncRowHeights(index, index);
6943             //this.layout();
6944             this.fireEvent("rowremoved", this, index, record);
6945         }
6946     },
6947     
6948     onAdd : function(ds, records, rowIndex)
6949     {
6950         //Roo.log('on Add called');
6951         // - note this does not handle multiple adding very well..
6952         var bt = this.mainBody.dom;
6953         for (var i =0 ; i < records.length;i++) {
6954             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6955             //Roo.log(records[i]);
6956             //Roo.log(this.store.getAt(rowIndex+i));
6957             this.insertRow(this.store, rowIndex + i, false);
6958             return;
6959         }
6960         
6961     },
6962     
6963     
6964     refreshRow : function(record){
6965         var ds = this.store, index;
6966         if(typeof record == 'number'){
6967             index = record;
6968             record = ds.getAt(index);
6969         }else{
6970             index = ds.indexOf(record);
6971         }
6972         this.insertRow(ds, index, true);
6973         this.autoSize();
6974         this.onRemove(ds, record, index+1, true);
6975         this.autoSize();
6976         //this.syncRowHeights(index, index);
6977         //this.layout();
6978         this.fireEvent("rowupdated", this, index, record);
6979     },
6980     
6981     insertRow : function(dm, rowIndex, isUpdate){
6982         
6983         if(!isUpdate){
6984             this.fireEvent("beforerowsinserted", this, rowIndex);
6985         }
6986             //var s = this.getScrollState();
6987         var row = this.renderRow(this.cm, this.store, rowIndex);
6988         // insert before rowIndex..
6989         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6990         
6991         var _this = this;
6992                 
6993         if(row.cellObjects.length){
6994             Roo.each(row.cellObjects, function(r){
6995                 _this.renderCellObject(r);
6996             })
6997         }
6998             
6999         if(!isUpdate){
7000             this.fireEvent("rowsinserted", this, rowIndex);
7001             //this.syncRowHeights(firstRow, lastRow);
7002             //this.stripeRows(firstRow);
7003             //this.layout();
7004         }
7005         
7006     },
7007     
7008     
7009     getRowDom : function(rowIndex)
7010     {
7011         var rows = this.el.select('tbody > tr', true).elements;
7012         
7013         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7014         
7015     },
7016     // returns the object tree for a tr..
7017   
7018     
7019     renderRow : function(cm, ds, rowIndex) 
7020     {
7021         var d = ds.getAt(rowIndex);
7022         
7023         var row = {
7024             tag : 'tr',
7025             cls : 'x-row-' + rowIndex,
7026             cn : []
7027         };
7028             
7029         var cellObjects = [];
7030         
7031         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7032             var config = cm.config[i];
7033             
7034             var renderer = cm.getRenderer(i);
7035             var value = '';
7036             var id = false;
7037             
7038             if(typeof(renderer) !== 'undefined'){
7039                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7040             }
7041             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7042             // and are rendered into the cells after the row is rendered - using the id for the element.
7043             
7044             if(typeof(value) === 'object'){
7045                 id = Roo.id();
7046                 cellObjects.push({
7047                     container : id,
7048                     cfg : value 
7049                 })
7050             }
7051             
7052             var rowcfg = {
7053                 record: d,
7054                 rowIndex : rowIndex,
7055                 colIndex : i,
7056                 rowClass : ''
7057             };
7058
7059             this.fireEvent('rowclass', this, rowcfg);
7060             
7061             var td = {
7062                 tag: 'td',
7063                 cls : rowcfg.rowClass + ' x-col-' + i,
7064                 style: '',
7065                 html: (typeof(value) === 'object') ? '' : value
7066             };
7067             
7068             if (id) {
7069                 td.id = id;
7070             }
7071             
7072             if(typeof(config.colspan) != 'undefined'){
7073                 td.colspan = config.colspan;
7074             }
7075             
7076             if(typeof(config.hidden) != 'undefined' && config.hidden){
7077                 td.style += ' display:none;';
7078             }
7079             
7080             if(typeof(config.align) != 'undefined' && config.align.length){
7081                 td.style += ' text-align:' + config.align + ';';
7082             }
7083             if(typeof(config.valign) != 'undefined' && config.valign.length){
7084                 td.style += ' vertical-align:' + config.valign + ';';
7085             }
7086             
7087             if(typeof(config.width) != 'undefined'){
7088                 td.style += ' width:' +  config.width + 'px;';
7089             }
7090             
7091             if(typeof(config.cursor) != 'undefined'){
7092                 td.style += ' cursor:' +  config.cursor + ';';
7093             }
7094             
7095             if(typeof(config.cls) != 'undefined'){
7096                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7097             }
7098             
7099             ['xs','sm','md','lg'].map(function(size){
7100                 
7101                 if(typeof(config[size]) == 'undefined'){
7102                     return;
7103                 }
7104                 
7105                 if (!config[size]) { // 0 = hidden
7106                     td.cls += ' hidden-' + size;
7107                     return;
7108                 }
7109                 
7110                 td.cls += ' col-' + size + '-' + config[size];
7111
7112             });
7113             
7114             row.cn.push(td);
7115            
7116         }
7117         
7118         row.cellObjects = cellObjects;
7119         
7120         return row;
7121           
7122     },
7123     
7124     
7125     
7126     onBeforeLoad : function()
7127     {
7128         
7129     },
7130      /**
7131      * Remove all rows
7132      */
7133     clear : function()
7134     {
7135         this.el.select('tbody', true).first().dom.innerHTML = '';
7136     },
7137     /**
7138      * Show or hide a row.
7139      * @param {Number} rowIndex to show or hide
7140      * @param {Boolean} state hide
7141      */
7142     setRowVisibility : function(rowIndex, state)
7143     {
7144         var bt = this.mainBody.dom;
7145         
7146         var rows = this.el.select('tbody > tr', true).elements;
7147         
7148         if(typeof(rows[rowIndex]) == 'undefined'){
7149             return;
7150         }
7151         rows[rowIndex].dom.style.display = state ? '' : 'none';
7152     },
7153     
7154     
7155     getSelectionModel : function(){
7156         if(!this.selModel){
7157             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7158         }
7159         return this.selModel;
7160     },
7161     /*
7162      * Render the Roo.bootstrap object from renderder
7163      */
7164     renderCellObject : function(r)
7165     {
7166         var _this = this;
7167         
7168         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7169         
7170         var t = r.cfg.render(r.container);
7171         
7172         if(r.cfg.cn){
7173             Roo.each(r.cfg.cn, function(c){
7174                 var child = {
7175                     container: t.getChildContainer(),
7176                     cfg: c
7177                 };
7178                 _this.renderCellObject(child);
7179             })
7180         }
7181     },
7182     
7183     getRowIndex : function(row)
7184     {
7185         var rowIndex = -1;
7186         
7187         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7188             if(el != row){
7189                 return;
7190             }
7191             
7192             rowIndex = index;
7193         });
7194         
7195         return rowIndex;
7196     },
7197      /**
7198      * Returns the grid's underlying element = used by panel.Grid
7199      * @return {Element} The element
7200      */
7201     getGridEl : function(){
7202         return this.el;
7203     },
7204      /**
7205      * Forces a resize - used by panel.Grid
7206      * @return {Element} The element
7207      */
7208     autoSize : function()
7209     {
7210         //var ctr = Roo.get(this.container.dom.parentElement);
7211         var ctr = Roo.get(this.el.dom);
7212         
7213         var thd = this.getGridEl().select('thead',true).first();
7214         var tbd = this.getGridEl().select('tbody', true).first();
7215         var tfd = this.getGridEl().select('tfoot', true).first();
7216         
7217         var cw = ctr.getWidth();
7218         
7219         if (tbd) {
7220             
7221             tbd.setSize(ctr.getWidth(),
7222                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7223             );
7224             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7225             cw -= barsize;
7226         }
7227         cw = Math.max(cw, this.totalWidth);
7228         this.getGridEl().select('tr',true).setWidth(cw);
7229         // resize 'expandable coloumn?
7230         
7231         return; // we doe not have a view in this design..
7232         
7233     },
7234     onBodyScroll: function()
7235     {
7236         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7237         if(this.mainHead){
7238             this.mainHead.setStyle({
7239                 'position' : 'relative',
7240                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7241             });
7242         }
7243         
7244         if(this.lazyLoad){
7245             
7246             var scrollHeight = this.mainBody.dom.scrollHeight;
7247             
7248             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7249             
7250             var height = this.mainBody.getHeight();
7251             
7252             if(scrollHeight - height == scrollTop) {
7253                 
7254                 var total = this.ds.getTotalCount();
7255                 
7256                 if(this.footer.cursor + this.footer.pageSize < total){
7257                     
7258                     this.footer.ds.load({
7259                         params : {
7260                             start : this.footer.cursor + this.footer.pageSize,
7261                             limit : this.footer.pageSize
7262                         },
7263                         add : true
7264                     });
7265                 }
7266             }
7267             
7268         }
7269     },
7270     
7271     onHeaderChange : function()
7272     {
7273         var header = this.renderHeader();
7274         var table = this.el.select('table', true).first();
7275         
7276         this.mainHead.remove();
7277         this.mainHead = table.createChild(header, this.mainBody, false);
7278     },
7279     
7280     onHiddenChange : function(colModel, colIndex, hidden)
7281     {
7282         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7283         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7284         
7285         this.CSS.updateRule(thSelector, "display", "");
7286         this.CSS.updateRule(tdSelector, "display", "");
7287         
7288         if(hidden){
7289             this.CSS.updateRule(thSelector, "display", "none");
7290             this.CSS.updateRule(tdSelector, "display", "none");
7291         }
7292         
7293         this.onHeaderChange();
7294         this.onLoad();
7295     },
7296     
7297     setColumnWidth: function(col_index, width)
7298     {
7299         // width = "md-2 xs-2..."
7300         if(!this.colModel.config[col_index]) {
7301             return;
7302         }
7303         
7304         var w = width.split(" ");
7305         
7306         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7307         
7308         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7309         
7310         
7311         for(var j = 0; j < w.length; j++) {
7312             
7313             if(!w[j]) {
7314                 continue;
7315             }
7316             
7317             var size_cls = w[j].split("-");
7318             
7319             if(!Number.isInteger(size_cls[1] * 1)) {
7320                 continue;
7321             }
7322             
7323             if(!this.colModel.config[col_index][size_cls[0]]) {
7324                 continue;
7325             }
7326             
7327             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7328                 continue;
7329             }
7330             
7331             h_row[0].classList.replace(
7332                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7333                 "col-"+size_cls[0]+"-"+size_cls[1]
7334             );
7335             
7336             for(var i = 0; i < rows.length; i++) {
7337                 
7338                 var size_cls = w[j].split("-");
7339                 
7340                 if(!Number.isInteger(size_cls[1] * 1)) {
7341                     continue;
7342                 }
7343                 
7344                 if(!this.colModel.config[col_index][size_cls[0]]) {
7345                     continue;
7346                 }
7347                 
7348                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7349                     continue;
7350                 }
7351                 
7352                 rows[i].classList.replace(
7353                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7354                     "col-"+size_cls[0]+"-"+size_cls[1]
7355                 );
7356             }
7357             
7358             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7359         }
7360     }
7361 });
7362
7363  
7364
7365  /*
7366  * - LGPL
7367  *
7368  * table cell
7369  * 
7370  */
7371
7372 /**
7373  * @class Roo.bootstrap.TableCell
7374  * @extends Roo.bootstrap.Component
7375  * Bootstrap TableCell class
7376  * @cfg {String} html cell contain text
7377  * @cfg {String} cls cell class
7378  * @cfg {String} tag cell tag (td|th) default td
7379  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7380  * @cfg {String} align Aligns the content in a cell
7381  * @cfg {String} axis Categorizes cells
7382  * @cfg {String} bgcolor Specifies the background color of a cell
7383  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7384  * @cfg {Number} colspan Specifies the number of columns a cell should span
7385  * @cfg {String} headers Specifies one or more header cells a cell is related to
7386  * @cfg {Number} height Sets the height of a cell
7387  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7388  * @cfg {Number} rowspan Sets the number of rows a cell should span
7389  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7390  * @cfg {String} valign Vertical aligns the content in a cell
7391  * @cfg {Number} width Specifies the width of a cell
7392  * 
7393  * @constructor
7394  * Create a new TableCell
7395  * @param {Object} config The config object
7396  */
7397
7398 Roo.bootstrap.TableCell = function(config){
7399     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7400 };
7401
7402 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7403     
7404     html: false,
7405     cls: false,
7406     tag: false,
7407     abbr: false,
7408     align: false,
7409     axis: false,
7410     bgcolor: false,
7411     charoff: false,
7412     colspan: false,
7413     headers: false,
7414     height: false,
7415     nowrap: false,
7416     rowspan: false,
7417     scope: false,
7418     valign: false,
7419     width: false,
7420     
7421     
7422     getAutoCreate : function(){
7423         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7424         
7425         cfg = {
7426             tag: 'td'
7427         };
7428         
7429         if(this.tag){
7430             cfg.tag = this.tag;
7431         }
7432         
7433         if (this.html) {
7434             cfg.html=this.html
7435         }
7436         if (this.cls) {
7437             cfg.cls=this.cls
7438         }
7439         if (this.abbr) {
7440             cfg.abbr=this.abbr
7441         }
7442         if (this.align) {
7443             cfg.align=this.align
7444         }
7445         if (this.axis) {
7446             cfg.axis=this.axis
7447         }
7448         if (this.bgcolor) {
7449             cfg.bgcolor=this.bgcolor
7450         }
7451         if (this.charoff) {
7452             cfg.charoff=this.charoff
7453         }
7454         if (this.colspan) {
7455             cfg.colspan=this.colspan
7456         }
7457         if (this.headers) {
7458             cfg.headers=this.headers
7459         }
7460         if (this.height) {
7461             cfg.height=this.height
7462         }
7463         if (this.nowrap) {
7464             cfg.nowrap=this.nowrap
7465         }
7466         if (this.rowspan) {
7467             cfg.rowspan=this.rowspan
7468         }
7469         if (this.scope) {
7470             cfg.scope=this.scope
7471         }
7472         if (this.valign) {
7473             cfg.valign=this.valign
7474         }
7475         if (this.width) {
7476             cfg.width=this.width
7477         }
7478         
7479         
7480         return cfg;
7481     }
7482    
7483 });
7484
7485  
7486
7487  /*
7488  * - LGPL
7489  *
7490  * table row
7491  * 
7492  */
7493
7494 /**
7495  * @class Roo.bootstrap.TableRow
7496  * @extends Roo.bootstrap.Component
7497  * Bootstrap TableRow class
7498  * @cfg {String} cls row class
7499  * @cfg {String} align Aligns the content in a table row
7500  * @cfg {String} bgcolor Specifies a background color for a table row
7501  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7502  * @cfg {String} valign Vertical aligns the content in a table row
7503  * 
7504  * @constructor
7505  * Create a new TableRow
7506  * @param {Object} config The config object
7507  */
7508
7509 Roo.bootstrap.TableRow = function(config){
7510     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7511 };
7512
7513 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7514     
7515     cls: false,
7516     align: false,
7517     bgcolor: false,
7518     charoff: false,
7519     valign: false,
7520     
7521     getAutoCreate : function(){
7522         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7523         
7524         cfg = {
7525             tag: 'tr'
7526         };
7527             
7528         if(this.cls){
7529             cfg.cls = this.cls;
7530         }
7531         if(this.align){
7532             cfg.align = this.align;
7533         }
7534         if(this.bgcolor){
7535             cfg.bgcolor = this.bgcolor;
7536         }
7537         if(this.charoff){
7538             cfg.charoff = this.charoff;
7539         }
7540         if(this.valign){
7541             cfg.valign = this.valign;
7542         }
7543         
7544         return cfg;
7545     }
7546    
7547 });
7548
7549  
7550
7551  /*
7552  * - LGPL
7553  *
7554  * table body
7555  * 
7556  */
7557
7558 /**
7559  * @class Roo.bootstrap.TableBody
7560  * @extends Roo.bootstrap.Component
7561  * Bootstrap TableBody class
7562  * @cfg {String} cls element class
7563  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7564  * @cfg {String} align Aligns the content inside the element
7565  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7566  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7567  * 
7568  * @constructor
7569  * Create a new TableBody
7570  * @param {Object} config The config object
7571  */
7572
7573 Roo.bootstrap.TableBody = function(config){
7574     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7575 };
7576
7577 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7578     
7579     cls: false,
7580     tag: false,
7581     align: false,
7582     charoff: false,
7583     valign: false,
7584     
7585     getAutoCreate : function(){
7586         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7587         
7588         cfg = {
7589             tag: 'tbody'
7590         };
7591             
7592         if (this.cls) {
7593             cfg.cls=this.cls
7594         }
7595         if(this.tag){
7596             cfg.tag = this.tag;
7597         }
7598         
7599         if(this.align){
7600             cfg.align = this.align;
7601         }
7602         if(this.charoff){
7603             cfg.charoff = this.charoff;
7604         }
7605         if(this.valign){
7606             cfg.valign = this.valign;
7607         }
7608         
7609         return cfg;
7610     }
7611     
7612     
7613 //    initEvents : function()
7614 //    {
7615 //        
7616 //        if(!this.store){
7617 //            return;
7618 //        }
7619 //        
7620 //        this.store = Roo.factory(this.store, Roo.data);
7621 //        this.store.on('load', this.onLoad, this);
7622 //        
7623 //        this.store.load();
7624 //        
7625 //    },
7626 //    
7627 //    onLoad: function () 
7628 //    {   
7629 //        this.fireEvent('load', this);
7630 //    }
7631 //    
7632 //   
7633 });
7634
7635  
7636
7637  /*
7638  * Based on:
7639  * Ext JS Library 1.1.1
7640  * Copyright(c) 2006-2007, Ext JS, LLC.
7641  *
7642  * Originally Released Under LGPL - original licence link has changed is not relivant.
7643  *
7644  * Fork - LGPL
7645  * <script type="text/javascript">
7646  */
7647
7648 // as we use this in bootstrap.
7649 Roo.namespace('Roo.form');
7650  /**
7651  * @class Roo.form.Action
7652  * Internal Class used to handle form actions
7653  * @constructor
7654  * @param {Roo.form.BasicForm} el The form element or its id
7655  * @param {Object} config Configuration options
7656  */
7657
7658  
7659  
7660 // define the action interface
7661 Roo.form.Action = function(form, options){
7662     this.form = form;
7663     this.options = options || {};
7664 };
7665 /**
7666  * Client Validation Failed
7667  * @const 
7668  */
7669 Roo.form.Action.CLIENT_INVALID = 'client';
7670 /**
7671  * Server Validation Failed
7672  * @const 
7673  */
7674 Roo.form.Action.SERVER_INVALID = 'server';
7675  /**
7676  * Connect to Server Failed
7677  * @const 
7678  */
7679 Roo.form.Action.CONNECT_FAILURE = 'connect';
7680 /**
7681  * Reading Data from Server Failed
7682  * @const 
7683  */
7684 Roo.form.Action.LOAD_FAILURE = 'load';
7685
7686 Roo.form.Action.prototype = {
7687     type : 'default',
7688     failureType : undefined,
7689     response : undefined,
7690     result : undefined,
7691
7692     // interface method
7693     run : function(options){
7694
7695     },
7696
7697     // interface method
7698     success : function(response){
7699
7700     },
7701
7702     // interface method
7703     handleResponse : function(response){
7704
7705     },
7706
7707     // default connection failure
7708     failure : function(response){
7709         
7710         this.response = response;
7711         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7712         this.form.afterAction(this, false);
7713     },
7714
7715     processResponse : function(response){
7716         this.response = response;
7717         if(!response.responseText){
7718             return true;
7719         }
7720         this.result = this.handleResponse(response);
7721         return this.result;
7722     },
7723
7724     // utility functions used internally
7725     getUrl : function(appendParams){
7726         var url = this.options.url || this.form.url || this.form.el.dom.action;
7727         if(appendParams){
7728             var p = this.getParams();
7729             if(p){
7730                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7731             }
7732         }
7733         return url;
7734     },
7735
7736     getMethod : function(){
7737         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7738     },
7739
7740     getParams : function(){
7741         var bp = this.form.baseParams;
7742         var p = this.options.params;
7743         if(p){
7744             if(typeof p == "object"){
7745                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7746             }else if(typeof p == 'string' && bp){
7747                 p += '&' + Roo.urlEncode(bp);
7748             }
7749         }else if(bp){
7750             p = Roo.urlEncode(bp);
7751         }
7752         return p;
7753     },
7754
7755     createCallback : function(){
7756         return {
7757             success: this.success,
7758             failure: this.failure,
7759             scope: this,
7760             timeout: (this.form.timeout*1000),
7761             upload: this.form.fileUpload ? this.success : undefined
7762         };
7763     }
7764 };
7765
7766 Roo.form.Action.Submit = function(form, options){
7767     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7768 };
7769
7770 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7771     type : 'submit',
7772
7773     haveProgress : false,
7774     uploadComplete : false,
7775     
7776     // uploadProgress indicator.
7777     uploadProgress : function()
7778     {
7779         if (!this.form.progressUrl) {
7780             return;
7781         }
7782         
7783         if (!this.haveProgress) {
7784             Roo.MessageBox.progress("Uploading", "Uploading");
7785         }
7786         if (this.uploadComplete) {
7787            Roo.MessageBox.hide();
7788            return;
7789         }
7790         
7791         this.haveProgress = true;
7792    
7793         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7794         
7795         var c = new Roo.data.Connection();
7796         c.request({
7797             url : this.form.progressUrl,
7798             params: {
7799                 id : uid
7800             },
7801             method: 'GET',
7802             success : function(req){
7803                //console.log(data);
7804                 var rdata = false;
7805                 var edata;
7806                 try  {
7807                    rdata = Roo.decode(req.responseText)
7808                 } catch (e) {
7809                     Roo.log("Invalid data from server..");
7810                     Roo.log(edata);
7811                     return;
7812                 }
7813                 if (!rdata || !rdata.success) {
7814                     Roo.log(rdata);
7815                     Roo.MessageBox.alert(Roo.encode(rdata));
7816                     return;
7817                 }
7818                 var data = rdata.data;
7819                 
7820                 if (this.uploadComplete) {
7821                    Roo.MessageBox.hide();
7822                    return;
7823                 }
7824                    
7825                 if (data){
7826                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7827                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7828                     );
7829                 }
7830                 this.uploadProgress.defer(2000,this);
7831             },
7832        
7833             failure: function(data) {
7834                 Roo.log('progress url failed ');
7835                 Roo.log(data);
7836             },
7837             scope : this
7838         });
7839            
7840     },
7841     
7842     
7843     run : function()
7844     {
7845         // run get Values on the form, so it syncs any secondary forms.
7846         this.form.getValues();
7847         
7848         var o = this.options;
7849         var method = this.getMethod();
7850         var isPost = method == 'POST';
7851         if(o.clientValidation === false || this.form.isValid()){
7852             
7853             if (this.form.progressUrl) {
7854                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7855                     (new Date() * 1) + '' + Math.random());
7856                     
7857             } 
7858             
7859             
7860             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7861                 form:this.form.el.dom,
7862                 url:this.getUrl(!isPost),
7863                 method: method,
7864                 params:isPost ? this.getParams() : null,
7865                 isUpload: this.form.fileUpload
7866             }));
7867             
7868             this.uploadProgress();
7869
7870         }else if (o.clientValidation !== false){ // client validation failed
7871             this.failureType = Roo.form.Action.CLIENT_INVALID;
7872             this.form.afterAction(this, false);
7873         }
7874     },
7875
7876     success : function(response)
7877     {
7878         this.uploadComplete= true;
7879         if (this.haveProgress) {
7880             Roo.MessageBox.hide();
7881         }
7882         
7883         
7884         var result = this.processResponse(response);
7885         if(result === true || result.success){
7886             this.form.afterAction(this, true);
7887             return;
7888         }
7889         if(result.errors){
7890             this.form.markInvalid(result.errors);
7891             this.failureType = Roo.form.Action.SERVER_INVALID;
7892         }
7893         this.form.afterAction(this, false);
7894     },
7895     failure : function(response)
7896     {
7897         this.uploadComplete= true;
7898         if (this.haveProgress) {
7899             Roo.MessageBox.hide();
7900         }
7901         
7902         this.response = response;
7903         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7904         this.form.afterAction(this, false);
7905     },
7906     
7907     handleResponse : function(response){
7908         if(this.form.errorReader){
7909             var rs = this.form.errorReader.read(response);
7910             var errors = [];
7911             if(rs.records){
7912                 for(var i = 0, len = rs.records.length; i < len; i++) {
7913                     var r = rs.records[i];
7914                     errors[i] = r.data;
7915                 }
7916             }
7917             if(errors.length < 1){
7918                 errors = null;
7919             }
7920             return {
7921                 success : rs.success,
7922                 errors : errors
7923             };
7924         }
7925         var ret = false;
7926         try {
7927             ret = Roo.decode(response.responseText);
7928         } catch (e) {
7929             ret = {
7930                 success: false,
7931                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7932                 errors : []
7933             };
7934         }
7935         return ret;
7936         
7937     }
7938 });
7939
7940
7941 Roo.form.Action.Load = function(form, options){
7942     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7943     this.reader = this.form.reader;
7944 };
7945
7946 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7947     type : 'load',
7948
7949     run : function(){
7950         
7951         Roo.Ajax.request(Roo.apply(
7952                 this.createCallback(), {
7953                     method:this.getMethod(),
7954                     url:this.getUrl(false),
7955                     params:this.getParams()
7956         }));
7957     },
7958
7959     success : function(response){
7960         
7961         var result = this.processResponse(response);
7962         if(result === true || !result.success || !result.data){
7963             this.failureType = Roo.form.Action.LOAD_FAILURE;
7964             this.form.afterAction(this, false);
7965             return;
7966         }
7967         this.form.clearInvalid();
7968         this.form.setValues(result.data);
7969         this.form.afterAction(this, true);
7970     },
7971
7972     handleResponse : function(response){
7973         if(this.form.reader){
7974             var rs = this.form.reader.read(response);
7975             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7976             return {
7977                 success : rs.success,
7978                 data : data
7979             };
7980         }
7981         return Roo.decode(response.responseText);
7982     }
7983 });
7984
7985 Roo.form.Action.ACTION_TYPES = {
7986     'load' : Roo.form.Action.Load,
7987     'submit' : Roo.form.Action.Submit
7988 };/*
7989  * - LGPL
7990  *
7991  * form
7992  *
7993  */
7994
7995 /**
7996  * @class Roo.bootstrap.Form
7997  * @extends Roo.bootstrap.Component
7998  * Bootstrap Form class
7999  * @cfg {String} method  GET | POST (default POST)
8000  * @cfg {String} labelAlign top | left (default top)
8001  * @cfg {String} align left  | right - for navbars
8002  * @cfg {Boolean} loadMask load mask when submit (default true)
8003
8004  *
8005  * @constructor
8006  * Create a new Form
8007  * @param {Object} config The config object
8008  */
8009
8010
8011 Roo.bootstrap.Form = function(config){
8012     
8013     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8014     
8015     Roo.bootstrap.Form.popover.apply();
8016     
8017     this.addEvents({
8018         /**
8019          * @event clientvalidation
8020          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8021          * @param {Form} this
8022          * @param {Boolean} valid true if the form has passed client-side validation
8023          */
8024         clientvalidation: true,
8025         /**
8026          * @event beforeaction
8027          * Fires before any action is performed. Return false to cancel the action.
8028          * @param {Form} this
8029          * @param {Action} action The action to be performed
8030          */
8031         beforeaction: true,
8032         /**
8033          * @event actionfailed
8034          * Fires when an action fails.
8035          * @param {Form} this
8036          * @param {Action} action The action that failed
8037          */
8038         actionfailed : true,
8039         /**
8040          * @event actioncomplete
8041          * Fires when an action is completed.
8042          * @param {Form} this
8043          * @param {Action} action The action that completed
8044          */
8045         actioncomplete : true
8046     });
8047 };
8048
8049 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8050
8051      /**
8052      * @cfg {String} method
8053      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8054      */
8055     method : 'POST',
8056     /**
8057      * @cfg {String} url
8058      * The URL to use for form actions if one isn't supplied in the action options.
8059      */
8060     /**
8061      * @cfg {Boolean} fileUpload
8062      * Set to true if this form is a file upload.
8063      */
8064
8065     /**
8066      * @cfg {Object} baseParams
8067      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8068      */
8069
8070     /**
8071      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8072      */
8073     timeout: 30,
8074     /**
8075      * @cfg {Sting} align (left|right) for navbar forms
8076      */
8077     align : 'left',
8078
8079     // private
8080     activeAction : null,
8081
8082     /**
8083      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8084      * element by passing it or its id or mask the form itself by passing in true.
8085      * @type Mixed
8086      */
8087     waitMsgTarget : false,
8088
8089     loadMask : true,
8090     
8091     /**
8092      * @cfg {Boolean} errorMask (true|false) default false
8093      */
8094     errorMask : false,
8095     
8096     /**
8097      * @cfg {Number} maskOffset Default 100
8098      */
8099     maskOffset : 100,
8100     
8101     /**
8102      * @cfg {Boolean} maskBody
8103      */
8104     maskBody : false,
8105
8106     getAutoCreate : function(){
8107
8108         var cfg = {
8109             tag: 'form',
8110             method : this.method || 'POST',
8111             id : this.id || Roo.id(),
8112             cls : ''
8113         };
8114         if (this.parent().xtype.match(/^Nav/)) {
8115             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8116
8117         }
8118
8119         if (this.labelAlign == 'left' ) {
8120             cfg.cls += ' form-horizontal';
8121         }
8122
8123
8124         return cfg;
8125     },
8126     initEvents : function()
8127     {
8128         this.el.on('submit', this.onSubmit, this);
8129         // this was added as random key presses on the form where triggering form submit.
8130         this.el.on('keypress', function(e) {
8131             if (e.getCharCode() != 13) {
8132                 return true;
8133             }
8134             // we might need to allow it for textareas.. and some other items.
8135             // check e.getTarget().
8136
8137             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8138                 return true;
8139             }
8140
8141             Roo.log("keypress blocked");
8142
8143             e.preventDefault();
8144             return false;
8145         });
8146         
8147     },
8148     // private
8149     onSubmit : function(e){
8150         e.stopEvent();
8151     },
8152
8153      /**
8154      * Returns true if client-side validation on the form is successful.
8155      * @return Boolean
8156      */
8157     isValid : function(){
8158         var items = this.getItems();
8159         var valid = true;
8160         var target = false;
8161         
8162         items.each(function(f){
8163             
8164             if(f.validate()){
8165                 return;
8166             }
8167             
8168             Roo.log('invalid field: ' + f.name);
8169             
8170             valid = false;
8171
8172             if(!target && f.el.isVisible(true)){
8173                 target = f;
8174             }
8175            
8176         });
8177         
8178         if(this.errorMask && !valid){
8179             Roo.bootstrap.Form.popover.mask(this, target);
8180         }
8181         
8182         return valid;
8183     },
8184     
8185     /**
8186      * Returns true if any fields in this form have changed since their original load.
8187      * @return Boolean
8188      */
8189     isDirty : function(){
8190         var dirty = false;
8191         var items = this.getItems();
8192         items.each(function(f){
8193            if(f.isDirty()){
8194                dirty = true;
8195                return false;
8196            }
8197            return true;
8198         });
8199         return dirty;
8200     },
8201      /**
8202      * Performs a predefined action (submit or load) or custom actions you define on this form.
8203      * @param {String} actionName The name of the action type
8204      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8205      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8206      * accept other config options):
8207      * <pre>
8208 Property          Type             Description
8209 ----------------  ---------------  ----------------------------------------------------------------------------------
8210 url               String           The url for the action (defaults to the form's url)
8211 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8212 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8213 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8214                                    validate the form on the client (defaults to false)
8215      * </pre>
8216      * @return {BasicForm} this
8217      */
8218     doAction : function(action, options){
8219         if(typeof action == 'string'){
8220             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8221         }
8222         if(this.fireEvent('beforeaction', this, action) !== false){
8223             this.beforeAction(action);
8224             action.run.defer(100, action);
8225         }
8226         return this;
8227     },
8228
8229     // private
8230     beforeAction : function(action){
8231         var o = action.options;
8232         
8233         if(this.loadMask){
8234             
8235             if(this.maskBody){
8236                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8237             } else {
8238                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8239             }
8240         }
8241         // not really supported yet.. ??
8242
8243         //if(this.waitMsgTarget === true){
8244         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8245         //}else if(this.waitMsgTarget){
8246         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8247         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8248         //}else {
8249         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8250        // }
8251
8252     },
8253
8254     // private
8255     afterAction : function(action, success){
8256         this.activeAction = null;
8257         var o = action.options;
8258
8259         if(this.loadMask){
8260             
8261             if(this.maskBody){
8262                 Roo.get(document.body).unmask();
8263             } else {
8264                 this.el.unmask();
8265             }
8266         }
8267         
8268         //if(this.waitMsgTarget === true){
8269 //            this.el.unmask();
8270         //}else if(this.waitMsgTarget){
8271         //    this.waitMsgTarget.unmask();
8272         //}else{
8273         //    Roo.MessageBox.updateProgress(1);
8274         //    Roo.MessageBox.hide();
8275        // }
8276         //
8277         if(success){
8278             if(o.reset){
8279                 this.reset();
8280             }
8281             Roo.callback(o.success, o.scope, [this, action]);
8282             this.fireEvent('actioncomplete', this, action);
8283
8284         }else{
8285
8286             // failure condition..
8287             // we have a scenario where updates need confirming.
8288             // eg. if a locking scenario exists..
8289             // we look for { errors : { needs_confirm : true }} in the response.
8290             if (
8291                 (typeof(action.result) != 'undefined')  &&
8292                 (typeof(action.result.errors) != 'undefined')  &&
8293                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8294            ){
8295                 var _t = this;
8296                 Roo.log("not supported yet");
8297                  /*
8298
8299                 Roo.MessageBox.confirm(
8300                     "Change requires confirmation",
8301                     action.result.errorMsg,
8302                     function(r) {
8303                         if (r != 'yes') {
8304                             return;
8305                         }
8306                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8307                     }
8308
8309                 );
8310                 */
8311
8312
8313                 return;
8314             }
8315
8316             Roo.callback(o.failure, o.scope, [this, action]);
8317             // show an error message if no failed handler is set..
8318             if (!this.hasListener('actionfailed')) {
8319                 Roo.log("need to add dialog support");
8320                 /*
8321                 Roo.MessageBox.alert("Error",
8322                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8323                         action.result.errorMsg :
8324                         "Saving Failed, please check your entries or try again"
8325                 );
8326                 */
8327             }
8328
8329             this.fireEvent('actionfailed', this, action);
8330         }
8331
8332     },
8333     /**
8334      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8335      * @param {String} id The value to search for
8336      * @return Field
8337      */
8338     findField : function(id){
8339         var items = this.getItems();
8340         var field = items.get(id);
8341         if(!field){
8342              items.each(function(f){
8343                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8344                     field = f;
8345                     return false;
8346                 }
8347                 return true;
8348             });
8349         }
8350         return field || null;
8351     },
8352      /**
8353      * Mark fields in this form invalid in bulk.
8354      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8355      * @return {BasicForm} this
8356      */
8357     markInvalid : function(errors){
8358         if(errors instanceof Array){
8359             for(var i = 0, len = errors.length; i < len; i++){
8360                 var fieldError = errors[i];
8361                 var f = this.findField(fieldError.id);
8362                 if(f){
8363                     f.markInvalid(fieldError.msg);
8364                 }
8365             }
8366         }else{
8367             var field, id;
8368             for(id in errors){
8369                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8370                     field.markInvalid(errors[id]);
8371                 }
8372             }
8373         }
8374         //Roo.each(this.childForms || [], function (f) {
8375         //    f.markInvalid(errors);
8376         //});
8377
8378         return this;
8379     },
8380
8381     /**
8382      * Set values for fields in this form in bulk.
8383      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8384      * @return {BasicForm} this
8385      */
8386     setValues : function(values){
8387         if(values instanceof Array){ // array of objects
8388             for(var i = 0, len = values.length; i < len; i++){
8389                 var v = values[i];
8390                 var f = this.findField(v.id);
8391                 if(f){
8392                     f.setValue(v.value);
8393                     if(this.trackResetOnLoad){
8394                         f.originalValue = f.getValue();
8395                     }
8396                 }
8397             }
8398         }else{ // object hash
8399             var field, id;
8400             for(id in values){
8401                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8402
8403                     if (field.setFromData &&
8404                         field.valueField &&
8405                         field.displayField &&
8406                         // combos' with local stores can
8407                         // be queried via setValue()
8408                         // to set their value..
8409                         (field.store && !field.store.isLocal)
8410                         ) {
8411                         // it's a combo
8412                         var sd = { };
8413                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8414                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8415                         field.setFromData(sd);
8416
8417                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8418                         
8419                         field.setFromData(values);
8420                         
8421                     } else {
8422                         field.setValue(values[id]);
8423                     }
8424
8425
8426                     if(this.trackResetOnLoad){
8427                         field.originalValue = field.getValue();
8428                     }
8429                 }
8430             }
8431         }
8432
8433         //Roo.each(this.childForms || [], function (f) {
8434         //    f.setValues(values);
8435         //});
8436
8437         return this;
8438     },
8439
8440     /**
8441      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8442      * they are returned as an array.
8443      * @param {Boolean} asString
8444      * @return {Object}
8445      */
8446     getValues : function(asString){
8447         //if (this.childForms) {
8448             // copy values from the child forms
8449         //    Roo.each(this.childForms, function (f) {
8450         //        this.setValues(f.getValues());
8451         //    }, this);
8452         //}
8453
8454
8455
8456         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8457         if(asString === true){
8458             return fs;
8459         }
8460         return Roo.urlDecode(fs);
8461     },
8462
8463     /**
8464      * Returns the fields in this form as an object with key/value pairs.
8465      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8466      * @return {Object}
8467      */
8468     getFieldValues : function(with_hidden)
8469     {
8470         var items = this.getItems();
8471         var ret = {};
8472         items.each(function(f){
8473             
8474             if (!f.getName()) {
8475                 return;
8476             }
8477             
8478             var v = f.getValue();
8479             
8480             if (f.inputType =='radio') {
8481                 if (typeof(ret[f.getName()]) == 'undefined') {
8482                     ret[f.getName()] = ''; // empty..
8483                 }
8484
8485                 if (!f.el.dom.checked) {
8486                     return;
8487
8488                 }
8489                 v = f.el.dom.value;
8490
8491             }
8492             
8493             if(f.xtype == 'MoneyField'){
8494                 ret[f.currencyName] = f.getCurrency();
8495             }
8496
8497             // not sure if this supported any more..
8498             if ((typeof(v) == 'object') && f.getRawValue) {
8499                 v = f.getRawValue() ; // dates..
8500             }
8501             // combo boxes where name != hiddenName...
8502             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8503                 ret[f.name] = f.getRawValue();
8504             }
8505             ret[f.getName()] = v;
8506         });
8507
8508         return ret;
8509     },
8510
8511     /**
8512      * Clears all invalid messages in this form.
8513      * @return {BasicForm} this
8514      */
8515     clearInvalid : function(){
8516         var items = this.getItems();
8517
8518         items.each(function(f){
8519            f.clearInvalid();
8520         });
8521
8522         return this;
8523     },
8524
8525     /**
8526      * Resets this form.
8527      * @return {BasicForm} this
8528      */
8529     reset : function(){
8530         var items = this.getItems();
8531         items.each(function(f){
8532             f.reset();
8533         });
8534
8535         Roo.each(this.childForms || [], function (f) {
8536             f.reset();
8537         });
8538
8539
8540         return this;
8541     },
8542     
8543     getItems : function()
8544     {
8545         var r=new Roo.util.MixedCollection(false, function(o){
8546             return o.id || (o.id = Roo.id());
8547         });
8548         var iter = function(el) {
8549             if (el.inputEl) {
8550                 r.add(el);
8551             }
8552             if (!el.items) {
8553                 return;
8554             }
8555             Roo.each(el.items,function(e) {
8556                 iter(e);
8557             });
8558         };
8559
8560         iter(this);
8561         return r;
8562     },
8563     
8564     hideFields : function(items)
8565     {
8566         Roo.each(items, function(i){
8567             
8568             var f = this.findField(i);
8569             
8570             if(!f){
8571                 return;
8572             }
8573             
8574             f.hide();
8575             
8576         }, this);
8577     },
8578     
8579     showFields : function(items)
8580     {
8581         Roo.each(items, function(i){
8582             
8583             var f = this.findField(i);
8584             
8585             if(!f){
8586                 return;
8587             }
8588             
8589             f.show();
8590             
8591         }, this);
8592     }
8593
8594 });
8595
8596 Roo.apply(Roo.bootstrap.Form, {
8597     
8598     popover : {
8599         
8600         padding : 5,
8601         
8602         isApplied : false,
8603         
8604         isMasked : false,
8605         
8606         form : false,
8607         
8608         target : false,
8609         
8610         toolTip : false,
8611         
8612         intervalID : false,
8613         
8614         maskEl : false,
8615         
8616         apply : function()
8617         {
8618             if(this.isApplied){
8619                 return;
8620             }
8621             
8622             this.maskEl = {
8623                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8624                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8625                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8626                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8627             };
8628             
8629             this.maskEl.top.enableDisplayMode("block");
8630             this.maskEl.left.enableDisplayMode("block");
8631             this.maskEl.bottom.enableDisplayMode("block");
8632             this.maskEl.right.enableDisplayMode("block");
8633             
8634             this.toolTip = new Roo.bootstrap.Tooltip({
8635                 cls : 'roo-form-error-popover',
8636                 alignment : {
8637                     'left' : ['r-l', [-2,0], 'right'],
8638                     'right' : ['l-r', [2,0], 'left'],
8639                     'bottom' : ['tl-bl', [0,2], 'top'],
8640                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8641                 }
8642             });
8643             
8644             this.toolTip.render(Roo.get(document.body));
8645
8646             this.toolTip.el.enableDisplayMode("block");
8647             
8648             Roo.get(document.body).on('click', function(){
8649                 this.unmask();
8650             }, this);
8651             
8652             Roo.get(document.body).on('touchstart', function(){
8653                 this.unmask();
8654             }, this);
8655             
8656             this.isApplied = true
8657         },
8658         
8659         mask : function(form, target)
8660         {
8661             this.form = form;
8662             
8663             this.target = target;
8664             
8665             if(!this.form.errorMask || !target.el){
8666                 return;
8667             }
8668             
8669             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8670             
8671             Roo.log(scrollable);
8672             
8673             var ot = this.target.el.calcOffsetsTo(scrollable);
8674             
8675             var scrollTo = ot[1] - this.form.maskOffset;
8676             
8677             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8678             
8679             scrollable.scrollTo('top', scrollTo);
8680             
8681             var box = this.target.el.getBox();
8682             Roo.log(box);
8683             var zIndex = Roo.bootstrap.Modal.zIndex++;
8684
8685             
8686             this.maskEl.top.setStyle('position', 'absolute');
8687             this.maskEl.top.setStyle('z-index', zIndex);
8688             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8689             this.maskEl.top.setLeft(0);
8690             this.maskEl.top.setTop(0);
8691             this.maskEl.top.show();
8692             
8693             this.maskEl.left.setStyle('position', 'absolute');
8694             this.maskEl.left.setStyle('z-index', zIndex);
8695             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8696             this.maskEl.left.setLeft(0);
8697             this.maskEl.left.setTop(box.y - this.padding);
8698             this.maskEl.left.show();
8699
8700             this.maskEl.bottom.setStyle('position', 'absolute');
8701             this.maskEl.bottom.setStyle('z-index', zIndex);
8702             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8703             this.maskEl.bottom.setLeft(0);
8704             this.maskEl.bottom.setTop(box.bottom + this.padding);
8705             this.maskEl.bottom.show();
8706
8707             this.maskEl.right.setStyle('position', 'absolute');
8708             this.maskEl.right.setStyle('z-index', zIndex);
8709             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8710             this.maskEl.right.setLeft(box.right + this.padding);
8711             this.maskEl.right.setTop(box.y - this.padding);
8712             this.maskEl.right.show();
8713
8714             this.toolTip.bindEl = this.target.el;
8715
8716             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8717
8718             var tip = this.target.blankText;
8719
8720             if(this.target.getValue() !== '' ) {
8721                 
8722                 if (this.target.invalidText.length) {
8723                     tip = this.target.invalidText;
8724                 } else if (this.target.regexText.length){
8725                     tip = this.target.regexText;
8726                 }
8727             }
8728
8729             this.toolTip.show(tip);
8730
8731             this.intervalID = window.setInterval(function() {
8732                 Roo.bootstrap.Form.popover.unmask();
8733             }, 10000);
8734
8735             window.onwheel = function(){ return false;};
8736             
8737             (function(){ this.isMasked = true; }).defer(500, this);
8738             
8739         },
8740         
8741         unmask : function()
8742         {
8743             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8744                 return;
8745             }
8746             
8747             this.maskEl.top.setStyle('position', 'absolute');
8748             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8749             this.maskEl.top.hide();
8750
8751             this.maskEl.left.setStyle('position', 'absolute');
8752             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8753             this.maskEl.left.hide();
8754
8755             this.maskEl.bottom.setStyle('position', 'absolute');
8756             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8757             this.maskEl.bottom.hide();
8758
8759             this.maskEl.right.setStyle('position', 'absolute');
8760             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8761             this.maskEl.right.hide();
8762             
8763             this.toolTip.hide();
8764             
8765             this.toolTip.el.hide();
8766             
8767             window.onwheel = function(){ return true;};
8768             
8769             if(this.intervalID){
8770                 window.clearInterval(this.intervalID);
8771                 this.intervalID = false;
8772             }
8773             
8774             this.isMasked = false;
8775             
8776         }
8777         
8778     }
8779     
8780 });
8781
8782 /*
8783  * Based on:
8784  * Ext JS Library 1.1.1
8785  * Copyright(c) 2006-2007, Ext JS, LLC.
8786  *
8787  * Originally Released Under LGPL - original licence link has changed is not relivant.
8788  *
8789  * Fork - LGPL
8790  * <script type="text/javascript">
8791  */
8792 /**
8793  * @class Roo.form.VTypes
8794  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8795  * @singleton
8796  */
8797 Roo.form.VTypes = function(){
8798     // closure these in so they are only created once.
8799     var alpha = /^[a-zA-Z_]+$/;
8800     var alphanum = /^[a-zA-Z0-9_]+$/;
8801     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8802     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8803
8804     // All these messages and functions are configurable
8805     return {
8806         /**
8807          * The function used to validate email addresses
8808          * @param {String} value The email address
8809          */
8810         'email' : function(v){
8811             return email.test(v);
8812         },
8813         /**
8814          * The error text to display when the email validation function returns false
8815          * @type String
8816          */
8817         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8818         /**
8819          * The keystroke filter mask to be applied on email input
8820          * @type RegExp
8821          */
8822         'emailMask' : /[a-z0-9_\.\-@]/i,
8823
8824         /**
8825          * The function used to validate URLs
8826          * @param {String} value The URL
8827          */
8828         'url' : function(v){
8829             return url.test(v);
8830         },
8831         /**
8832          * The error text to display when the url validation function returns false
8833          * @type String
8834          */
8835         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8836         
8837         /**
8838          * The function used to validate alpha values
8839          * @param {String} value The value
8840          */
8841         'alpha' : function(v){
8842             return alpha.test(v);
8843         },
8844         /**
8845          * The error text to display when the alpha validation function returns false
8846          * @type String
8847          */
8848         'alphaText' : 'This field should only contain letters and _',
8849         /**
8850          * The keystroke filter mask to be applied on alpha input
8851          * @type RegExp
8852          */
8853         'alphaMask' : /[a-z_]/i,
8854
8855         /**
8856          * The function used to validate alphanumeric values
8857          * @param {String} value The value
8858          */
8859         'alphanum' : function(v){
8860             return alphanum.test(v);
8861         },
8862         /**
8863          * The error text to display when the alphanumeric validation function returns false
8864          * @type String
8865          */
8866         'alphanumText' : 'This field should only contain letters, numbers and _',
8867         /**
8868          * The keystroke filter mask to be applied on alphanumeric input
8869          * @type RegExp
8870          */
8871         'alphanumMask' : /[a-z0-9_]/i
8872     };
8873 }();/*
8874  * - LGPL
8875  *
8876  * Input
8877  * 
8878  */
8879
8880 /**
8881  * @class Roo.bootstrap.Input
8882  * @extends Roo.bootstrap.Component
8883  * Bootstrap Input class
8884  * @cfg {Boolean} disabled is it disabled
8885  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8886  * @cfg {String} name name of the input
8887  * @cfg {string} fieldLabel - the label associated
8888  * @cfg {string} placeholder - placeholder to put in text.
8889  * @cfg {string}  before - input group add on before
8890  * @cfg {string} after - input group add on after
8891  * @cfg {string} size - (lg|sm) or leave empty..
8892  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8893  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8894  * @cfg {Number} md colspan out of 12 for computer-sized screens
8895  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8896  * @cfg {string} value default value of the input
8897  * @cfg {Number} labelWidth set the width of label 
8898  * @cfg {Number} labellg set the width of label (1-12)
8899  * @cfg {Number} labelmd set the width of label (1-12)
8900  * @cfg {Number} labelsm set the width of label (1-12)
8901  * @cfg {Number} labelxs set the width of label (1-12)
8902  * @cfg {String} labelAlign (top|left)
8903  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8904  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8905  * @cfg {String} indicatorpos (left|right) default left
8906  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8907  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8908
8909  * @cfg {String} align (left|center|right) Default left
8910  * @cfg {Boolean} forceFeedback (true|false) Default false
8911  * 
8912  * @constructor
8913  * Create a new Input
8914  * @param {Object} config The config object
8915  */
8916
8917 Roo.bootstrap.Input = function(config){
8918     
8919     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8920     
8921     this.addEvents({
8922         /**
8923          * @event focus
8924          * Fires when this field receives input focus.
8925          * @param {Roo.form.Field} this
8926          */
8927         focus : true,
8928         /**
8929          * @event blur
8930          * Fires when this field loses input focus.
8931          * @param {Roo.form.Field} this
8932          */
8933         blur : true,
8934         /**
8935          * @event specialkey
8936          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8937          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8938          * @param {Roo.form.Field} this
8939          * @param {Roo.EventObject} e The event object
8940          */
8941         specialkey : true,
8942         /**
8943          * @event change
8944          * Fires just before the field blurs if the field value has changed.
8945          * @param {Roo.form.Field} this
8946          * @param {Mixed} newValue The new value
8947          * @param {Mixed} oldValue The original value
8948          */
8949         change : true,
8950         /**
8951          * @event invalid
8952          * Fires after the field has been marked as invalid.
8953          * @param {Roo.form.Field} this
8954          * @param {String} msg The validation message
8955          */
8956         invalid : true,
8957         /**
8958          * @event valid
8959          * Fires after the field has been validated with no errors.
8960          * @param {Roo.form.Field} this
8961          */
8962         valid : true,
8963          /**
8964          * @event keyup
8965          * Fires after the key up
8966          * @param {Roo.form.Field} this
8967          * @param {Roo.EventObject}  e The event Object
8968          */
8969         keyup : true
8970     });
8971 };
8972
8973 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8974      /**
8975      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8976       automatic validation (defaults to "keyup").
8977      */
8978     validationEvent : "keyup",
8979      /**
8980      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8981      */
8982     validateOnBlur : true,
8983     /**
8984      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8985      */
8986     validationDelay : 250,
8987      /**
8988      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8989      */
8990     focusClass : "x-form-focus",  // not needed???
8991     
8992        
8993     /**
8994      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8995      */
8996     invalidClass : "has-warning",
8997     
8998     /**
8999      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
9000      */
9001     validClass : "has-success",
9002     
9003     /**
9004      * @cfg {Boolean} hasFeedback (true|false) default true
9005      */
9006     hasFeedback : true,
9007     
9008     /**
9009      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9010      */
9011     invalidFeedbackClass : "glyphicon-warning-sign",
9012     
9013     /**
9014      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9015      */
9016     validFeedbackClass : "glyphicon-ok",
9017     
9018     /**
9019      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9020      */
9021     selectOnFocus : false,
9022     
9023      /**
9024      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9025      */
9026     maskRe : null,
9027        /**
9028      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9029      */
9030     vtype : null,
9031     
9032       /**
9033      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9034      */
9035     disableKeyFilter : false,
9036     
9037        /**
9038      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9039      */
9040     disabled : false,
9041      /**
9042      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9043      */
9044     allowBlank : true,
9045     /**
9046      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9047      */
9048     blankText : "Please complete this mandatory field",
9049     
9050      /**
9051      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9052      */
9053     minLength : 0,
9054     /**
9055      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9056      */
9057     maxLength : Number.MAX_VALUE,
9058     /**
9059      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9060      */
9061     minLengthText : "The minimum length for this field is {0}",
9062     /**
9063      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9064      */
9065     maxLengthText : "The maximum length for this field is {0}",
9066   
9067     
9068     /**
9069      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9070      * If available, this function will be called only after the basic validators all return true, and will be passed the
9071      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9072      */
9073     validator : null,
9074     /**
9075      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9076      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9077      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9078      */
9079     regex : null,
9080     /**
9081      * @cfg {String} regexText -- Depricated - use Invalid Text
9082      */
9083     regexText : "",
9084     
9085     /**
9086      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9087      */
9088     invalidText : "",
9089     
9090     
9091     
9092     autocomplete: false,
9093     
9094     
9095     fieldLabel : '',
9096     inputType : 'text',
9097     
9098     name : false,
9099     placeholder: false,
9100     before : false,
9101     after : false,
9102     size : false,
9103     hasFocus : false,
9104     preventMark: false,
9105     isFormField : true,
9106     value : '',
9107     labelWidth : 2,
9108     labelAlign : false,
9109     readOnly : false,
9110     align : false,
9111     formatedValue : false,
9112     forceFeedback : false,
9113     
9114     indicatorpos : 'left',
9115     
9116     labellg : 0,
9117     labelmd : 0,
9118     labelsm : 0,
9119     labelxs : 0,
9120     
9121     capture : '',
9122     accept : '',
9123     
9124     parentLabelAlign : function()
9125     {
9126         var parent = this;
9127         while (parent.parent()) {
9128             parent = parent.parent();
9129             if (typeof(parent.labelAlign) !='undefined') {
9130                 return parent.labelAlign;
9131             }
9132         }
9133         return 'left';
9134         
9135     },
9136     
9137     getAutoCreate : function()
9138     {
9139         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9140         
9141         var id = Roo.id();
9142         
9143         var cfg = {};
9144         
9145         if(this.inputType != 'hidden'){
9146             cfg.cls = 'form-group' //input-group
9147         }
9148         
9149         var input =  {
9150             tag: 'input',
9151             id : id,
9152             type : this.inputType,
9153             value : this.value,
9154             cls : 'form-control',
9155             placeholder : this.placeholder || '',
9156             autocomplete : this.autocomplete || 'new-password'
9157         };
9158         
9159         if(this.capture.length){
9160             input.capture = this.capture;
9161         }
9162         
9163         if(this.accept.length){
9164             input.accept = this.accept + "/*";
9165         }
9166         
9167         if(this.align){
9168             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9169         }
9170         
9171         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9172             input.maxLength = this.maxLength;
9173         }
9174         
9175         if (this.disabled) {
9176             input.disabled=true;
9177         }
9178         
9179         if (this.readOnly) {
9180             input.readonly=true;
9181         }
9182         
9183         if (this.name) {
9184             input.name = this.name;
9185         }
9186         
9187         if (this.size) {
9188             input.cls += ' input-' + this.size;
9189         }
9190         
9191         var settings=this;
9192         ['xs','sm','md','lg'].map(function(size){
9193             if (settings[size]) {
9194                 cfg.cls += ' col-' + size + '-' + settings[size];
9195             }
9196         });
9197         
9198         var inputblock = input;
9199         
9200         var feedback = {
9201             tag: 'span',
9202             cls: 'glyphicon form-control-feedback'
9203         };
9204             
9205         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9206             
9207             inputblock = {
9208                 cls : 'has-feedback',
9209                 cn :  [
9210                     input,
9211                     feedback
9212                 ] 
9213             };  
9214         }
9215         
9216         if (this.before || this.after) {
9217             
9218             inputblock = {
9219                 cls : 'input-group',
9220                 cn :  [] 
9221             };
9222             
9223             if (this.before && typeof(this.before) == 'string') {
9224                 
9225                 inputblock.cn.push({
9226                     tag :'span',
9227                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9228                     html : this.before
9229                 });
9230             }
9231             if (this.before && typeof(this.before) == 'object') {
9232                 this.before = Roo.factory(this.before);
9233                 
9234                 inputblock.cn.push({
9235                     tag :'span',
9236                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9237                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9238                 });
9239             }
9240             
9241             inputblock.cn.push(input);
9242             
9243             if (this.after && typeof(this.after) == 'string') {
9244                 inputblock.cn.push({
9245                     tag :'span',
9246                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9247                     html : this.after
9248                 });
9249             }
9250             if (this.after && typeof(this.after) == 'object') {
9251                 this.after = Roo.factory(this.after);
9252                 
9253                 inputblock.cn.push({
9254                     tag :'span',
9255                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9256                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9257                 });
9258             }
9259             
9260             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9261                 inputblock.cls += ' has-feedback';
9262                 inputblock.cn.push(feedback);
9263             }
9264         };
9265         var indicator = {
9266             tag : 'i',
9267             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9268             tooltip : 'This field is required'
9269         };
9270         if (Roo.bootstrap.version == 4) {
9271             indicator = {
9272                 tag : 'i',
9273                 style : 'display-none'
9274             };
9275         }
9276         if (align ==='left' && this.fieldLabel.length) {
9277             
9278             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9279             
9280             cfg.cn = [
9281                 indicator,
9282                 {
9283                     tag: 'label',
9284                     'for' :  id,
9285                     cls : 'control-label col-form-label',
9286                     html : this.fieldLabel
9287
9288                 },
9289                 {
9290                     cls : "", 
9291                     cn: [
9292                         inputblock
9293                     ]
9294                 }
9295             ];
9296             
9297             var labelCfg = cfg.cn[1];
9298             var contentCfg = cfg.cn[2];
9299             
9300             if(this.indicatorpos == 'right'){
9301                 cfg.cn = [
9302                     {
9303                         tag: 'label',
9304                         'for' :  id,
9305                         cls : 'control-label col-form-label',
9306                         cn : [
9307                             {
9308                                 tag : 'span',
9309                                 html : this.fieldLabel
9310                             },
9311                             indicator
9312                         ]
9313                     },
9314                     {
9315                         cls : "",
9316                         cn: [
9317                             inputblock
9318                         ]
9319                     }
9320
9321                 ];
9322                 
9323                 labelCfg = cfg.cn[0];
9324                 contentCfg = cfg.cn[1];
9325             
9326             }
9327             
9328             if(this.labelWidth > 12){
9329                 labelCfg.style = "width: " + this.labelWidth + 'px';
9330             }
9331             
9332             if(this.labelWidth < 13 && this.labelmd == 0){
9333                 this.labelmd = this.labelWidth;
9334             }
9335             
9336             if(this.labellg > 0){
9337                 labelCfg.cls += ' col-lg-' + this.labellg;
9338                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9339             }
9340             
9341             if(this.labelmd > 0){
9342                 labelCfg.cls += ' col-md-' + this.labelmd;
9343                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9344             }
9345             
9346             if(this.labelsm > 0){
9347                 labelCfg.cls += ' col-sm-' + this.labelsm;
9348                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9349             }
9350             
9351             if(this.labelxs > 0){
9352                 labelCfg.cls += ' col-xs-' + this.labelxs;
9353                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9354             }
9355             
9356             
9357         } else if ( this.fieldLabel.length) {
9358                 
9359             cfg.cn = [
9360                 {
9361                     tag : 'i',
9362                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9363                     tooltip : 'This field is required'
9364                 },
9365                 {
9366                     tag: 'label',
9367                    //cls : 'input-group-addon',
9368                     html : this.fieldLabel
9369
9370                 },
9371
9372                inputblock
9373
9374            ];
9375            
9376            if(this.indicatorpos == 'right'){
9377                 
9378                 cfg.cn = [
9379                     {
9380                         tag: 'label',
9381                        //cls : 'input-group-addon',
9382                         html : this.fieldLabel
9383
9384                     },
9385                     {
9386                         tag : 'i',
9387                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9388                         tooltip : 'This field is required'
9389                     },
9390
9391                    inputblock
9392
9393                ];
9394
9395             }
9396
9397         } else {
9398             
9399             cfg.cn = [
9400
9401                     inputblock
9402
9403             ];
9404                 
9405                 
9406         };
9407         
9408         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9409            cfg.cls += ' navbar-form';
9410         }
9411         
9412         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9413             // on BS4 we do this only if not form 
9414             cfg.cls += ' navbar-form';
9415             cfg.tag = 'li';
9416         }
9417         
9418         return cfg;
9419         
9420     },
9421     /**
9422      * return the real input element.
9423      */
9424     inputEl: function ()
9425     {
9426         return this.el.select('input.form-control',true).first();
9427     },
9428     
9429     tooltipEl : function()
9430     {
9431         return this.inputEl();
9432     },
9433     
9434     indicatorEl : function()
9435     {
9436         if (Roo.bootstrap.version == 4) {
9437             return false; // not enabled in v4 yet.
9438         }
9439         
9440         var indicator = this.el.select('i.roo-required-indicator',true).first();
9441         
9442         if(!indicator){
9443             return false;
9444         }
9445         
9446         return indicator;
9447         
9448     },
9449     
9450     setDisabled : function(v)
9451     {
9452         var i  = this.inputEl().dom;
9453         if (!v) {
9454             i.removeAttribute('disabled');
9455             return;
9456             
9457         }
9458         i.setAttribute('disabled','true');
9459     },
9460     initEvents : function()
9461     {
9462           
9463         this.inputEl().on("keydown" , this.fireKey,  this);
9464         this.inputEl().on("focus", this.onFocus,  this);
9465         this.inputEl().on("blur", this.onBlur,  this);
9466         
9467         this.inputEl().relayEvent('keyup', this);
9468         
9469         this.indicator = this.indicatorEl();
9470         
9471         if(this.indicator){
9472             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9473         }
9474  
9475         // reference to original value for reset
9476         this.originalValue = this.getValue();
9477         //Roo.form.TextField.superclass.initEvents.call(this);
9478         if(this.validationEvent == 'keyup'){
9479             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9480             this.inputEl().on('keyup', this.filterValidation, this);
9481         }
9482         else if(this.validationEvent !== false){
9483             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9484         }
9485         
9486         if(this.selectOnFocus){
9487             this.on("focus", this.preFocus, this);
9488             
9489         }
9490         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9491             this.inputEl().on("keypress", this.filterKeys, this);
9492         } else {
9493             this.inputEl().relayEvent('keypress', this);
9494         }
9495        /* if(this.grow){
9496             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9497             this.el.on("click", this.autoSize,  this);
9498         }
9499         */
9500         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9501             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9502         }
9503         
9504         if (typeof(this.before) == 'object') {
9505             this.before.render(this.el.select('.roo-input-before',true).first());
9506         }
9507         if (typeof(this.after) == 'object') {
9508             this.after.render(this.el.select('.roo-input-after',true).first());
9509         }
9510         
9511         this.inputEl().on('change', this.onChange, this);
9512         
9513     },
9514     filterValidation : function(e){
9515         if(!e.isNavKeyPress()){
9516             this.validationTask.delay(this.validationDelay);
9517         }
9518     },
9519      /**
9520      * Validates the field value
9521      * @return {Boolean} True if the value is valid, else false
9522      */
9523     validate : function(){
9524         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9525         if(this.disabled || this.validateValue(this.getRawValue())){
9526             this.markValid();
9527             return true;
9528         }
9529         
9530         this.markInvalid();
9531         return false;
9532     },
9533     
9534     
9535     /**
9536      * Validates a value according to the field's validation rules and marks the field as invalid
9537      * if the validation fails
9538      * @param {Mixed} value The value to validate
9539      * @return {Boolean} True if the value is valid, else false
9540      */
9541     validateValue : function(value)
9542     {
9543         if(this.getVisibilityEl().hasClass('hidden')){
9544             return true;
9545         }
9546         
9547         if(value.length < 1)  { // if it's blank
9548             if(this.allowBlank){
9549                 return true;
9550             }
9551             return false;
9552         }
9553         
9554         if(value.length < this.minLength){
9555             this.invalidText = String.format(this.minLengthText, this.minLength);
9556             return false;
9557         }
9558         if(value.length > this.maxLength){
9559             this.invalidText = String.format(this.maxLengthText, this.maxLength);
9560             return false;
9561         }
9562         if(this.vtype){
9563             var vt = Roo.form.VTypes;
9564             if(!vt[this.vtype](value, this)){
9565                 return false;
9566             }
9567         }
9568         if(typeof this.validator == "function"){
9569             var msg = this.validator(value);
9570             if(msg !== true){
9571                 return false;
9572             }
9573             if (typeof(msg) == 'string') {
9574                 this.invalidText = msg;
9575             }
9576         }
9577         
9578         if(this.regex && !this.regex.test(value)){
9579             return false;
9580         }
9581         
9582         return true;
9583     },
9584     
9585      // private
9586     fireKey : function(e){
9587         //Roo.log('field ' + e.getKey());
9588         if(e.isNavKeyPress()){
9589             this.fireEvent("specialkey", this, e);
9590         }
9591     },
9592     focus : function (selectText){
9593         if(this.rendered){
9594             this.inputEl().focus();
9595             if(selectText === true){
9596                 this.inputEl().dom.select();
9597             }
9598         }
9599         return this;
9600     } ,
9601     
9602     onFocus : function(){
9603         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9604            // this.el.addClass(this.focusClass);
9605         }
9606         if(!this.hasFocus){
9607             this.hasFocus = true;
9608             this.startValue = this.getValue();
9609             this.fireEvent("focus", this);
9610         }
9611     },
9612     
9613     beforeBlur : Roo.emptyFn,
9614
9615     
9616     // private
9617     onBlur : function(){
9618         this.beforeBlur();
9619         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9620             //this.el.removeClass(this.focusClass);
9621         }
9622         this.hasFocus = false;
9623         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9624             this.validate();
9625         }
9626         var v = this.getValue();
9627         if(String(v) !== String(this.startValue)){
9628             this.fireEvent('change', this, v, this.startValue);
9629         }
9630         this.fireEvent("blur", this);
9631     },
9632     
9633     onChange : function(e)
9634     {
9635         var v = this.getValue();
9636         if(String(v) !== String(this.startValue)){
9637             this.fireEvent('change', this, v, this.startValue);
9638         }
9639         
9640     },
9641     
9642     /**
9643      * Resets the current field value to the originally loaded value and clears any validation messages
9644      */
9645     reset : function(){
9646         this.setValue(this.originalValue);
9647         this.validate();
9648     },
9649      /**
9650      * Returns the name of the field
9651      * @return {Mixed} name The name field
9652      */
9653     getName: function(){
9654         return this.name;
9655     },
9656      /**
9657      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9658      * @return {Mixed} value The field value
9659      */
9660     getValue : function(){
9661         
9662         var v = this.inputEl().getValue();
9663         
9664         return v;
9665     },
9666     /**
9667      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9668      * @return {Mixed} value The field value
9669      */
9670     getRawValue : function(){
9671         var v = this.inputEl().getValue();
9672         
9673         return v;
9674     },
9675     
9676     /**
9677      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9678      * @param {Mixed} value The value to set
9679      */
9680     setRawValue : function(v){
9681         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9682     },
9683     
9684     selectText : function(start, end){
9685         var v = this.getRawValue();
9686         if(v.length > 0){
9687             start = start === undefined ? 0 : start;
9688             end = end === undefined ? v.length : end;
9689             var d = this.inputEl().dom;
9690             if(d.setSelectionRange){
9691                 d.setSelectionRange(start, end);
9692             }else if(d.createTextRange){
9693                 var range = d.createTextRange();
9694                 range.moveStart("character", start);
9695                 range.moveEnd("character", v.length-end);
9696                 range.select();
9697             }
9698         }
9699     },
9700     
9701     /**
9702      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9703      * @param {Mixed} value The value to set
9704      */
9705     setValue : function(v){
9706         this.value = v;
9707         if(this.rendered){
9708             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9709             this.validate();
9710         }
9711     },
9712     
9713     /*
9714     processValue : function(value){
9715         if(this.stripCharsRe){
9716             var newValue = value.replace(this.stripCharsRe, '');
9717             if(newValue !== value){
9718                 this.setRawValue(newValue);
9719                 return newValue;
9720             }
9721         }
9722         return value;
9723     },
9724   */
9725     preFocus : function(){
9726         
9727         if(this.selectOnFocus){
9728             this.inputEl().dom.select();
9729         }
9730     },
9731     filterKeys : function(e){
9732         var k = e.getKey();
9733         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9734             return;
9735         }
9736         var c = e.getCharCode(), cc = String.fromCharCode(c);
9737         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9738             return;
9739         }
9740         if(!this.maskRe.test(cc)){
9741             e.stopEvent();
9742         }
9743     },
9744      /**
9745      * Clear any invalid styles/messages for this field
9746      */
9747     clearInvalid : function(){
9748         
9749         if(!this.el || this.preventMark){ // not rendered
9750             return;
9751         }
9752         
9753      
9754         this.el.removeClass(this.invalidClass);
9755         
9756         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9757             
9758             var feedback = this.el.select('.form-control-feedback', true).first();
9759             
9760             if(feedback){
9761                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9762             }
9763             
9764         }
9765         
9766         if(this.indicator){
9767             this.indicator.removeClass('visible');
9768             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9769         }
9770         
9771         this.fireEvent('valid', this);
9772     },
9773     
9774      /**
9775      * Mark this field as valid
9776      */
9777     markValid : function()
9778     {
9779         if(!this.el  || this.preventMark){ // not rendered...
9780             return;
9781         }
9782         
9783         this.el.removeClass([this.invalidClass, this.validClass]);
9784         
9785         var feedback = this.el.select('.form-control-feedback', true).first();
9786             
9787         if(feedback){
9788             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9789         }
9790         
9791         if(this.indicator){
9792             this.indicator.removeClass('visible');
9793             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9794         }
9795         
9796         if(this.disabled){
9797             return;
9798         }
9799         
9800         if(this.allowBlank && !this.getRawValue().length){
9801             return;
9802         }
9803         
9804         this.el.addClass(this.validClass);
9805         
9806         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9807             
9808             var feedback = this.el.select('.form-control-feedback', true).first();
9809             
9810             if(feedback){
9811                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9812                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9813             }
9814             
9815         }
9816         
9817         this.fireEvent('valid', this);
9818     },
9819     
9820      /**
9821      * Mark this field as invalid
9822      * @param {String} msg The validation message
9823      */
9824     markInvalid : function(msg)
9825     {
9826         if(!this.el  || this.preventMark){ // not rendered
9827             return;
9828         }
9829         
9830         this.el.removeClass([this.invalidClass, this.validClass]);
9831         
9832         var feedback = this.el.select('.form-control-feedback', true).first();
9833             
9834         if(feedback){
9835             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9836         }
9837
9838         if(this.disabled){
9839             return;
9840         }
9841         
9842         if(this.allowBlank && !this.getRawValue().length){
9843             return;
9844         }
9845         
9846         if(this.indicator){
9847             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9848             this.indicator.addClass('visible');
9849         }
9850         
9851         this.el.addClass(this.invalidClass);
9852         
9853         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9854             
9855             var feedback = this.el.select('.form-control-feedback', true).first();
9856             
9857             if(feedback){
9858                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9859                 
9860                 if(this.getValue().length || this.forceFeedback){
9861                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9862                 }
9863                 
9864             }
9865             
9866         }
9867         
9868         this.fireEvent('invalid', this, msg);
9869     },
9870     // private
9871     SafariOnKeyDown : function(event)
9872     {
9873         // this is a workaround for a password hang bug on chrome/ webkit.
9874         if (this.inputEl().dom.type != 'password') {
9875             return;
9876         }
9877         
9878         var isSelectAll = false;
9879         
9880         if(this.inputEl().dom.selectionEnd > 0){
9881             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9882         }
9883         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9884             event.preventDefault();
9885             this.setValue('');
9886             return;
9887         }
9888         
9889         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9890             
9891             event.preventDefault();
9892             // this is very hacky as keydown always get's upper case.
9893             //
9894             var cc = String.fromCharCode(event.getCharCode());
9895             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9896             
9897         }
9898     },
9899     adjustWidth : function(tag, w){
9900         tag = tag.toLowerCase();
9901         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9902             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9903                 if(tag == 'input'){
9904                     return w + 2;
9905                 }
9906                 if(tag == 'textarea'){
9907                     return w-2;
9908                 }
9909             }else if(Roo.isOpera){
9910                 if(tag == 'input'){
9911                     return w + 2;
9912                 }
9913                 if(tag == 'textarea'){
9914                     return w-2;
9915                 }
9916             }
9917         }
9918         return w;
9919     },
9920     
9921     setFieldLabel : function(v)
9922     {
9923         if(!this.rendered){
9924             return;
9925         }
9926         
9927         if(this.indicatorEl()){
9928             var ar = this.el.select('label > span',true);
9929             
9930             if (ar.elements.length) {
9931                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9932                 this.fieldLabel = v;
9933                 return;
9934             }
9935             
9936             var br = this.el.select('label',true);
9937             
9938             if(br.elements.length) {
9939                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9940                 this.fieldLabel = v;
9941                 return;
9942             }
9943             
9944             Roo.log('Cannot Found any of label > span || label in input');
9945             return;
9946         }
9947         
9948         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9949         this.fieldLabel = v;
9950         
9951         
9952     }
9953 });
9954
9955  
9956 /*
9957  * - LGPL
9958  *
9959  * Input
9960  * 
9961  */
9962
9963 /**
9964  * @class Roo.bootstrap.TextArea
9965  * @extends Roo.bootstrap.Input
9966  * Bootstrap TextArea class
9967  * @cfg {Number} cols Specifies the visible width of a text area
9968  * @cfg {Number} rows Specifies the visible number of lines in a text area
9969  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9970  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9971  * @cfg {string} html text
9972  * 
9973  * @constructor
9974  * Create a new TextArea
9975  * @param {Object} config The config object
9976  */
9977
9978 Roo.bootstrap.TextArea = function(config){
9979     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9980    
9981 };
9982
9983 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9984      
9985     cols : false,
9986     rows : 5,
9987     readOnly : false,
9988     warp : 'soft',
9989     resize : false,
9990     value: false,
9991     html: false,
9992     
9993     getAutoCreate : function(){
9994         
9995         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9996         
9997         var id = Roo.id();
9998         
9999         var cfg = {};
10000         
10001         if(this.inputType != 'hidden'){
10002             cfg.cls = 'form-group' //input-group
10003         }
10004         
10005         var input =  {
10006             tag: 'textarea',
10007             id : id,
10008             warp : this.warp,
10009             rows : this.rows,
10010             value : this.value || '',
10011             html: this.html || '',
10012             cls : 'form-control',
10013             placeholder : this.placeholder || '' 
10014             
10015         };
10016         
10017         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10018             input.maxLength = this.maxLength;
10019         }
10020         
10021         if(this.resize){
10022             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10023         }
10024         
10025         if(this.cols){
10026             input.cols = this.cols;
10027         }
10028         
10029         if (this.readOnly) {
10030             input.readonly = true;
10031         }
10032         
10033         if (this.name) {
10034             input.name = this.name;
10035         }
10036         
10037         if (this.size) {
10038             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10039         }
10040         
10041         var settings=this;
10042         ['xs','sm','md','lg'].map(function(size){
10043             if (settings[size]) {
10044                 cfg.cls += ' col-' + size + '-' + settings[size];
10045             }
10046         });
10047         
10048         var inputblock = input;
10049         
10050         if(this.hasFeedback && !this.allowBlank){
10051             
10052             var feedback = {
10053                 tag: 'span',
10054                 cls: 'glyphicon form-control-feedback'
10055             };
10056
10057             inputblock = {
10058                 cls : 'has-feedback',
10059                 cn :  [
10060                     input,
10061                     feedback
10062                 ] 
10063             };  
10064         }
10065         
10066         
10067         if (this.before || this.after) {
10068             
10069             inputblock = {
10070                 cls : 'input-group',
10071                 cn :  [] 
10072             };
10073             if (this.before) {
10074                 inputblock.cn.push({
10075                     tag :'span',
10076                     cls : 'input-group-addon',
10077                     html : this.before
10078                 });
10079             }
10080             
10081             inputblock.cn.push(input);
10082             
10083             if(this.hasFeedback && !this.allowBlank){
10084                 inputblock.cls += ' has-feedback';
10085                 inputblock.cn.push(feedback);
10086             }
10087             
10088             if (this.after) {
10089                 inputblock.cn.push({
10090                     tag :'span',
10091                     cls : 'input-group-addon',
10092                     html : this.after
10093                 });
10094             }
10095             
10096         }
10097         
10098         if (align ==='left' && this.fieldLabel.length) {
10099             cfg.cn = [
10100                 {
10101                     tag: 'label',
10102                     'for' :  id,
10103                     cls : 'control-label',
10104                     html : this.fieldLabel
10105                 },
10106                 {
10107                     cls : "",
10108                     cn: [
10109                         inputblock
10110                     ]
10111                 }
10112
10113             ];
10114             
10115             if(this.labelWidth > 12){
10116                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10117             }
10118
10119             if(this.labelWidth < 13 && this.labelmd == 0){
10120                 this.labelmd = this.labelWidth;
10121             }
10122
10123             if(this.labellg > 0){
10124                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10125                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10126             }
10127
10128             if(this.labelmd > 0){
10129                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10130                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10131             }
10132
10133             if(this.labelsm > 0){
10134                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10135                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10136             }
10137
10138             if(this.labelxs > 0){
10139                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10140                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10141             }
10142             
10143         } else if ( this.fieldLabel.length) {
10144             cfg.cn = [
10145
10146                {
10147                    tag: 'label',
10148                    //cls : 'input-group-addon',
10149                    html : this.fieldLabel
10150
10151                },
10152
10153                inputblock
10154
10155            ];
10156
10157         } else {
10158
10159             cfg.cn = [
10160
10161                 inputblock
10162
10163             ];
10164                 
10165         }
10166         
10167         if (this.disabled) {
10168             input.disabled=true;
10169         }
10170         
10171         return cfg;
10172         
10173     },
10174     /**
10175      * return the real textarea element.
10176      */
10177     inputEl: function ()
10178     {
10179         return this.el.select('textarea.form-control',true).first();
10180     },
10181     
10182     /**
10183      * Clear any invalid styles/messages for this field
10184      */
10185     clearInvalid : function()
10186     {
10187         
10188         if(!this.el || this.preventMark){ // not rendered
10189             return;
10190         }
10191         
10192         var label = this.el.select('label', true).first();
10193         var icon = this.el.select('i.fa-star', true).first();
10194         
10195         if(label && icon){
10196             icon.remove();
10197         }
10198         
10199         this.el.removeClass(this.invalidClass);
10200         
10201         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10202             
10203             var feedback = this.el.select('.form-control-feedback', true).first();
10204             
10205             if(feedback){
10206                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10207             }
10208             
10209         }
10210         
10211         this.fireEvent('valid', this);
10212     },
10213     
10214      /**
10215      * Mark this field as valid
10216      */
10217     markValid : function()
10218     {
10219         if(!this.el  || this.preventMark){ // not rendered
10220             return;
10221         }
10222         
10223         this.el.removeClass([this.invalidClass, this.validClass]);
10224         
10225         var feedback = this.el.select('.form-control-feedback', true).first();
10226             
10227         if(feedback){
10228             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10229         }
10230
10231         if(this.disabled || this.allowBlank){
10232             return;
10233         }
10234         
10235         var label = this.el.select('label', true).first();
10236         var icon = this.el.select('i.fa-star', true).first();
10237         
10238         if(label && icon){
10239             icon.remove();
10240         }
10241         
10242         this.el.addClass(this.validClass);
10243         
10244         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10245             
10246             var feedback = this.el.select('.form-control-feedback', true).first();
10247             
10248             if(feedback){
10249                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10250                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10251             }
10252             
10253         }
10254         
10255         this.fireEvent('valid', this);
10256     },
10257     
10258      /**
10259      * Mark this field as invalid
10260      * @param {String} msg The validation message
10261      */
10262     markInvalid : function(msg)
10263     {
10264         if(!this.el  || this.preventMark){ // not rendered
10265             return;
10266         }
10267         
10268         this.el.removeClass([this.invalidClass, this.validClass]);
10269         
10270         var feedback = this.el.select('.form-control-feedback', true).first();
10271             
10272         if(feedback){
10273             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10274         }
10275
10276         if(this.disabled || this.allowBlank){
10277             return;
10278         }
10279         
10280         var label = this.el.select('label', true).first();
10281         var icon = this.el.select('i.fa-star', true).first();
10282         
10283         if(!this.getValue().length && label && !icon){
10284             this.el.createChild({
10285                 tag : 'i',
10286                 cls : 'text-danger fa fa-lg fa-star',
10287                 tooltip : 'This field is required',
10288                 style : 'margin-right:5px;'
10289             }, label, true);
10290         }
10291
10292         this.el.addClass(this.invalidClass);
10293         
10294         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10295             
10296             var feedback = this.el.select('.form-control-feedback', true).first();
10297             
10298             if(feedback){
10299                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10300                 
10301                 if(this.getValue().length || this.forceFeedback){
10302                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10303                 }
10304                 
10305             }
10306             
10307         }
10308         
10309         this.fireEvent('invalid', this, msg);
10310     }
10311 });
10312
10313  
10314 /*
10315  * - LGPL
10316  *
10317  * trigger field - base class for combo..
10318  * 
10319  */
10320  
10321 /**
10322  * @class Roo.bootstrap.TriggerField
10323  * @extends Roo.bootstrap.Input
10324  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10325  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10326  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10327  * for which you can provide a custom implementation.  For example:
10328  * <pre><code>
10329 var trigger = new Roo.bootstrap.TriggerField();
10330 trigger.onTriggerClick = myTriggerFn;
10331 trigger.applyTo('my-field');
10332 </code></pre>
10333  *
10334  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10335  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10336  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10337  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10338  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10339
10340  * @constructor
10341  * Create a new TriggerField.
10342  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10343  * to the base TextField)
10344  */
10345 Roo.bootstrap.TriggerField = function(config){
10346     this.mimicing = false;
10347     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10348 };
10349
10350 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10351     /**
10352      * @cfg {String} triggerClass A CSS class to apply to the trigger
10353      */
10354      /**
10355      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10356      */
10357     hideTrigger:false,
10358
10359     /**
10360      * @cfg {Boolean} removable (true|false) special filter default false
10361      */
10362     removable : false,
10363     
10364     /** @cfg {Boolean} grow @hide */
10365     /** @cfg {Number} growMin @hide */
10366     /** @cfg {Number} growMax @hide */
10367
10368     /**
10369      * @hide 
10370      * @method
10371      */
10372     autoSize: Roo.emptyFn,
10373     // private
10374     monitorTab : true,
10375     // private
10376     deferHeight : true,
10377
10378     
10379     actionMode : 'wrap',
10380     
10381     caret : false,
10382     
10383     
10384     getAutoCreate : function(){
10385        
10386         var align = this.labelAlign || this.parentLabelAlign();
10387         
10388         var id = Roo.id();
10389         
10390         var cfg = {
10391             cls: 'form-group' //input-group
10392         };
10393         
10394         
10395         var input =  {
10396             tag: 'input',
10397             id : id,
10398             type : this.inputType,
10399             cls : 'form-control',
10400             autocomplete: 'new-password',
10401             placeholder : this.placeholder || '' 
10402             
10403         };
10404         if (this.name) {
10405             input.name = this.name;
10406         }
10407         if (this.size) {
10408             input.cls += ' input-' + this.size;
10409         }
10410         
10411         if (this.disabled) {
10412             input.disabled=true;
10413         }
10414         
10415         var inputblock = input;
10416         
10417         if(this.hasFeedback && !this.allowBlank){
10418             
10419             var feedback = {
10420                 tag: 'span',
10421                 cls: 'glyphicon form-control-feedback'
10422             };
10423             
10424             if(this.removable && !this.editable && !this.tickable){
10425                 inputblock = {
10426                     cls : 'has-feedback',
10427                     cn :  [
10428                         inputblock,
10429                         {
10430                             tag: 'button',
10431                             html : 'x',
10432                             cls : 'roo-combo-removable-btn close'
10433                         },
10434                         feedback
10435                     ] 
10436                 };
10437             } else {
10438                 inputblock = {
10439                     cls : 'has-feedback',
10440                     cn :  [
10441                         inputblock,
10442                         feedback
10443                     ] 
10444                 };
10445             }
10446
10447         } else {
10448             if(this.removable && !this.editable && !this.tickable){
10449                 inputblock = {
10450                     cls : 'roo-removable',
10451                     cn :  [
10452                         inputblock,
10453                         {
10454                             tag: 'button',
10455                             html : 'x',
10456                             cls : 'roo-combo-removable-btn close'
10457                         }
10458                     ] 
10459                 };
10460             }
10461         }
10462         
10463         if (this.before || this.after) {
10464             
10465             inputblock = {
10466                 cls : 'input-group',
10467                 cn :  [] 
10468             };
10469             if (this.before) {
10470                 inputblock.cn.push({
10471                     tag :'span',
10472                     cls : 'input-group-addon input-group-prepend input-group-text',
10473                     html : this.before
10474                 });
10475             }
10476             
10477             inputblock.cn.push(input);
10478             
10479             if(this.hasFeedback && !this.allowBlank){
10480                 inputblock.cls += ' has-feedback';
10481                 inputblock.cn.push(feedback);
10482             }
10483             
10484             if (this.after) {
10485                 inputblock.cn.push({
10486                     tag :'span',
10487                     cls : 'input-group-addon input-group-append input-group-text',
10488                     html : this.after
10489                 });
10490             }
10491             
10492         };
10493         
10494       
10495         
10496         var ibwrap = inputblock;
10497         
10498         if(this.multiple){
10499             ibwrap = {
10500                 tag: 'ul',
10501                 cls: 'roo-select2-choices',
10502                 cn:[
10503                     {
10504                         tag: 'li',
10505                         cls: 'roo-select2-search-field',
10506                         cn: [
10507
10508                             inputblock
10509                         ]
10510                     }
10511                 ]
10512             };
10513                 
10514         }
10515         
10516         var combobox = {
10517             cls: 'roo-select2-container input-group',
10518             cn: [
10519                  {
10520                     tag: 'input',
10521                     type : 'hidden',
10522                     cls: 'form-hidden-field'
10523                 },
10524                 ibwrap
10525             ]
10526         };
10527         
10528         if(!this.multiple && this.showToggleBtn){
10529             
10530             var caret = {
10531                         tag: 'span',
10532                         cls: 'caret'
10533              };
10534             if (this.caret != false) {
10535                 caret = {
10536                      tag: 'i',
10537                      cls: 'fa fa-' + this.caret
10538                 };
10539                 
10540             }
10541             
10542             combobox.cn.push({
10543                 tag :'span',
10544                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10545                 cn : [
10546                     caret,
10547                     {
10548                         tag: 'span',
10549                         cls: 'combobox-clear',
10550                         cn  : [
10551                             {
10552                                 tag : 'i',
10553                                 cls: 'icon-remove'
10554                             }
10555                         ]
10556                     }
10557                 ]
10558
10559             })
10560         }
10561         
10562         if(this.multiple){
10563             combobox.cls += ' roo-select2-container-multi';
10564         }
10565          var indicator = {
10566             tag : 'i',
10567             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10568             tooltip : 'This field is required'
10569         };
10570         if (Roo.bootstrap.version == 4) {
10571             indicator = {
10572                 tag : 'i',
10573                 style : 'display:none'
10574             };
10575         }
10576         
10577         
10578         if (align ==='left' && this.fieldLabel.length) {
10579             
10580             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10581
10582             cfg.cn = [
10583                 indicator,
10584                 {
10585                     tag: 'label',
10586                     'for' :  id,
10587                     cls : 'control-label',
10588                     html : this.fieldLabel
10589
10590                 },
10591                 {
10592                     cls : "", 
10593                     cn: [
10594                         combobox
10595                     ]
10596                 }
10597
10598             ];
10599             
10600             var labelCfg = cfg.cn[1];
10601             var contentCfg = cfg.cn[2];
10602             
10603             if(this.indicatorpos == 'right'){
10604                 cfg.cn = [
10605                     {
10606                         tag: 'label',
10607                         'for' :  id,
10608                         cls : 'control-label',
10609                         cn : [
10610                             {
10611                                 tag : 'span',
10612                                 html : this.fieldLabel
10613                             },
10614                             indicator
10615                         ]
10616                     },
10617                     {
10618                         cls : "", 
10619                         cn: [
10620                             combobox
10621                         ]
10622                     }
10623
10624                 ];
10625                 
10626                 labelCfg = cfg.cn[0];
10627                 contentCfg = cfg.cn[1];
10628             }
10629             
10630             if(this.labelWidth > 12){
10631                 labelCfg.style = "width: " + this.labelWidth + 'px';
10632             }
10633             
10634             if(this.labelWidth < 13 && this.labelmd == 0){
10635                 this.labelmd = this.labelWidth;
10636             }
10637             
10638             if(this.labellg > 0){
10639                 labelCfg.cls += ' col-lg-' + this.labellg;
10640                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10641             }
10642             
10643             if(this.labelmd > 0){
10644                 labelCfg.cls += ' col-md-' + this.labelmd;
10645                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10646             }
10647             
10648             if(this.labelsm > 0){
10649                 labelCfg.cls += ' col-sm-' + this.labelsm;
10650                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10651             }
10652             
10653             if(this.labelxs > 0){
10654                 labelCfg.cls += ' col-xs-' + this.labelxs;
10655                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10656             }
10657             
10658         } else if ( this.fieldLabel.length) {
10659 //                Roo.log(" label");
10660             cfg.cn = [
10661                 indicator,
10662                {
10663                    tag: 'label',
10664                    //cls : 'input-group-addon',
10665                    html : this.fieldLabel
10666
10667                },
10668
10669                combobox
10670
10671             ];
10672             
10673             if(this.indicatorpos == 'right'){
10674                 
10675                 cfg.cn = [
10676                     {
10677                        tag: 'label',
10678                        cn : [
10679                            {
10680                                tag : 'span',
10681                                html : this.fieldLabel
10682                            },
10683                            indicator
10684                        ]
10685
10686                     },
10687                     combobox
10688
10689                 ];
10690
10691             }
10692
10693         } else {
10694             
10695 //                Roo.log(" no label && no align");
10696                 cfg = combobox
10697                      
10698                 
10699         }
10700         
10701         var settings=this;
10702         ['xs','sm','md','lg'].map(function(size){
10703             if (settings[size]) {
10704                 cfg.cls += ' col-' + size + '-' + settings[size];
10705             }
10706         });
10707         
10708         return cfg;
10709         
10710     },
10711     
10712     
10713     
10714     // private
10715     onResize : function(w, h){
10716 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10717 //        if(typeof w == 'number'){
10718 //            var x = w - this.trigger.getWidth();
10719 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10720 //            this.trigger.setStyle('left', x+'px');
10721 //        }
10722     },
10723
10724     // private
10725     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10726
10727     // private
10728     getResizeEl : function(){
10729         return this.inputEl();
10730     },
10731
10732     // private
10733     getPositionEl : function(){
10734         return this.inputEl();
10735     },
10736
10737     // private
10738     alignErrorIcon : function(){
10739         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10740     },
10741
10742     // private
10743     initEvents : function(){
10744         
10745         this.createList();
10746         
10747         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10748         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10749         if(!this.multiple && this.showToggleBtn){
10750             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10751             if(this.hideTrigger){
10752                 this.trigger.setDisplayed(false);
10753             }
10754             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10755         }
10756         
10757         if(this.multiple){
10758             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10759         }
10760         
10761         if(this.removable && !this.editable && !this.tickable){
10762             var close = this.closeTriggerEl();
10763             
10764             if(close){
10765                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10766                 close.on('click', this.removeBtnClick, this, close);
10767             }
10768         }
10769         
10770         //this.trigger.addClassOnOver('x-form-trigger-over');
10771         //this.trigger.addClassOnClick('x-form-trigger-click');
10772         
10773         //if(!this.width){
10774         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10775         //}
10776     },
10777     
10778     closeTriggerEl : function()
10779     {
10780         var close = this.el.select('.roo-combo-removable-btn', true).first();
10781         return close ? close : false;
10782     },
10783     
10784     removeBtnClick : function(e, h, el)
10785     {
10786         e.preventDefault();
10787         
10788         if(this.fireEvent("remove", this) !== false){
10789             this.reset();
10790             this.fireEvent("afterremove", this)
10791         }
10792     },
10793     
10794     createList : function()
10795     {
10796         this.list = Roo.get(document.body).createChild({
10797             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10798             cls: 'typeahead typeahead-long dropdown-menu',
10799             style: 'display:none'
10800         });
10801         
10802         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10803         
10804     },
10805
10806     // private
10807     initTrigger : function(){
10808        
10809     },
10810
10811     // private
10812     onDestroy : function(){
10813         if(this.trigger){
10814             this.trigger.removeAllListeners();
10815           //  this.trigger.remove();
10816         }
10817         //if(this.wrap){
10818         //    this.wrap.remove();
10819         //}
10820         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10821     },
10822
10823     // private
10824     onFocus : function(){
10825         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10826         /*
10827         if(!this.mimicing){
10828             this.wrap.addClass('x-trigger-wrap-focus');
10829             this.mimicing = true;
10830             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10831             if(this.monitorTab){
10832                 this.el.on("keydown", this.checkTab, this);
10833             }
10834         }
10835         */
10836     },
10837
10838     // private
10839     checkTab : function(e){
10840         if(e.getKey() == e.TAB){
10841             this.triggerBlur();
10842         }
10843     },
10844
10845     // private
10846     onBlur : function(){
10847         // do nothing
10848     },
10849
10850     // private
10851     mimicBlur : function(e, t){
10852         /*
10853         if(!this.wrap.contains(t) && this.validateBlur()){
10854             this.triggerBlur();
10855         }
10856         */
10857     },
10858
10859     // private
10860     triggerBlur : function(){
10861         this.mimicing = false;
10862         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10863         if(this.monitorTab){
10864             this.el.un("keydown", this.checkTab, this);
10865         }
10866         //this.wrap.removeClass('x-trigger-wrap-focus');
10867         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10868     },
10869
10870     // private
10871     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10872     validateBlur : function(e, t){
10873         return true;
10874     },
10875
10876     // private
10877     onDisable : function(){
10878         this.inputEl().dom.disabled = true;
10879         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10880         //if(this.wrap){
10881         //    this.wrap.addClass('x-item-disabled');
10882         //}
10883     },
10884
10885     // private
10886     onEnable : function(){
10887         this.inputEl().dom.disabled = false;
10888         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10889         //if(this.wrap){
10890         //    this.el.removeClass('x-item-disabled');
10891         //}
10892     },
10893
10894     // private
10895     onShow : function(){
10896         var ae = this.getActionEl();
10897         
10898         if(ae){
10899             ae.dom.style.display = '';
10900             ae.dom.style.visibility = 'visible';
10901         }
10902     },
10903
10904     // private
10905     
10906     onHide : function(){
10907         var ae = this.getActionEl();
10908         ae.dom.style.display = 'none';
10909     },
10910
10911     /**
10912      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10913      * by an implementing function.
10914      * @method
10915      * @param {EventObject} e
10916      */
10917     onTriggerClick : Roo.emptyFn
10918 });
10919  /*
10920  * Based on:
10921  * Ext JS Library 1.1.1
10922  * Copyright(c) 2006-2007, Ext JS, LLC.
10923  *
10924  * Originally Released Under LGPL - original licence link has changed is not relivant.
10925  *
10926  * Fork - LGPL
10927  * <script type="text/javascript">
10928  */
10929
10930
10931 /**
10932  * @class Roo.data.SortTypes
10933  * @singleton
10934  * Defines the default sorting (casting?) comparison functions used when sorting data.
10935  */
10936 Roo.data.SortTypes = {
10937     /**
10938      * Default sort that does nothing
10939      * @param {Mixed} s The value being converted
10940      * @return {Mixed} The comparison value
10941      */
10942     none : function(s){
10943         return s;
10944     },
10945     
10946     /**
10947      * The regular expression used to strip tags
10948      * @type {RegExp}
10949      * @property
10950      */
10951     stripTagsRE : /<\/?[^>]+>/gi,
10952     
10953     /**
10954      * Strips all HTML tags to sort on text only
10955      * @param {Mixed} s The value being converted
10956      * @return {String} The comparison value
10957      */
10958     asText : function(s){
10959         return String(s).replace(this.stripTagsRE, "");
10960     },
10961     
10962     /**
10963      * Strips all HTML tags to sort on text only - Case insensitive
10964      * @param {Mixed} s The value being converted
10965      * @return {String} The comparison value
10966      */
10967     asUCText : function(s){
10968         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10969     },
10970     
10971     /**
10972      * Case insensitive string
10973      * @param {Mixed} s The value being converted
10974      * @return {String} The comparison value
10975      */
10976     asUCString : function(s) {
10977         return String(s).toUpperCase();
10978     },
10979     
10980     /**
10981      * Date sorting
10982      * @param {Mixed} s The value being converted
10983      * @return {Number} The comparison value
10984      */
10985     asDate : function(s) {
10986         if(!s){
10987             return 0;
10988         }
10989         if(s instanceof Date){
10990             return s.getTime();
10991         }
10992         return Date.parse(String(s));
10993     },
10994     
10995     /**
10996      * Float sorting
10997      * @param {Mixed} s The value being converted
10998      * @return {Float} The comparison value
10999      */
11000     asFloat : function(s) {
11001         var val = parseFloat(String(s).replace(/,/g, ""));
11002         if(isNaN(val)) {
11003             val = 0;
11004         }
11005         return val;
11006     },
11007     
11008     /**
11009      * Integer sorting
11010      * @param {Mixed} s The value being converted
11011      * @return {Number} The comparison value
11012      */
11013     asInt : function(s) {
11014         var val = parseInt(String(s).replace(/,/g, ""));
11015         if(isNaN(val)) {
11016             val = 0;
11017         }
11018         return val;
11019     }
11020 };/*
11021  * Based on:
11022  * Ext JS Library 1.1.1
11023  * Copyright(c) 2006-2007, Ext JS, LLC.
11024  *
11025  * Originally Released Under LGPL - original licence link has changed is not relivant.
11026  *
11027  * Fork - LGPL
11028  * <script type="text/javascript">
11029  */
11030
11031 /**
11032 * @class Roo.data.Record
11033  * Instances of this class encapsulate both record <em>definition</em> information, and record
11034  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11035  * to access Records cached in an {@link Roo.data.Store} object.<br>
11036  * <p>
11037  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11038  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11039  * objects.<br>
11040  * <p>
11041  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11042  * @constructor
11043  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11044  * {@link #create}. The parameters are the same.
11045  * @param {Array} data An associative Array of data values keyed by the field name.
11046  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11047  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11048  * not specified an integer id is generated.
11049  */
11050 Roo.data.Record = function(data, id){
11051     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11052     this.data = data;
11053 };
11054
11055 /**
11056  * Generate a constructor for a specific record layout.
11057  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11058  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11059  * Each field definition object may contain the following properties: <ul>
11060  * <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,
11061  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11062  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11063  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11064  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11065  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11066  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11067  * this may be omitted.</p></li>
11068  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11069  * <ul><li>auto (Default, implies no conversion)</li>
11070  * <li>string</li>
11071  * <li>int</li>
11072  * <li>float</li>
11073  * <li>boolean</li>
11074  * <li>date</li></ul></p></li>
11075  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11076  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11077  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11078  * by the Reader into an object that will be stored in the Record. It is passed the
11079  * following parameters:<ul>
11080  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11081  * </ul></p></li>
11082  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11083  * </ul>
11084  * <br>usage:<br><pre><code>
11085 var TopicRecord = Roo.data.Record.create(
11086     {name: 'title', mapping: 'topic_title'},
11087     {name: 'author', mapping: 'username'},
11088     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11089     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11090     {name: 'lastPoster', mapping: 'user2'},
11091     {name: 'excerpt', mapping: 'post_text'}
11092 );
11093
11094 var myNewRecord = new TopicRecord({
11095     title: 'Do my job please',
11096     author: 'noobie',
11097     totalPosts: 1,
11098     lastPost: new Date(),
11099     lastPoster: 'Animal',
11100     excerpt: 'No way dude!'
11101 });
11102 myStore.add(myNewRecord);
11103 </code></pre>
11104  * @method create
11105  * @static
11106  */
11107 Roo.data.Record.create = function(o){
11108     var f = function(){
11109         f.superclass.constructor.apply(this, arguments);
11110     };
11111     Roo.extend(f, Roo.data.Record);
11112     var p = f.prototype;
11113     p.fields = new Roo.util.MixedCollection(false, function(field){
11114         return field.name;
11115     });
11116     for(var i = 0, len = o.length; i < len; i++){
11117         p.fields.add(new Roo.data.Field(o[i]));
11118     }
11119     f.getField = function(name){
11120         return p.fields.get(name);  
11121     };
11122     return f;
11123 };
11124
11125 Roo.data.Record.AUTO_ID = 1000;
11126 Roo.data.Record.EDIT = 'edit';
11127 Roo.data.Record.REJECT = 'reject';
11128 Roo.data.Record.COMMIT = 'commit';
11129
11130 Roo.data.Record.prototype = {
11131     /**
11132      * Readonly flag - true if this record has been modified.
11133      * @type Boolean
11134      */
11135     dirty : false,
11136     editing : false,
11137     error: null,
11138     modified: null,
11139
11140     // private
11141     join : function(store){
11142         this.store = store;
11143     },
11144
11145     /**
11146      * Set the named field to the specified value.
11147      * @param {String} name The name of the field to set.
11148      * @param {Object} value The value to set the field to.
11149      */
11150     set : function(name, value){
11151         if(this.data[name] == value){
11152             return;
11153         }
11154         this.dirty = true;
11155         if(!this.modified){
11156             this.modified = {};
11157         }
11158         if(typeof this.modified[name] == 'undefined'){
11159             this.modified[name] = this.data[name];
11160         }
11161         this.data[name] = value;
11162         if(!this.editing && this.store){
11163             this.store.afterEdit(this);
11164         }       
11165     },
11166
11167     /**
11168      * Get the value of the named field.
11169      * @param {String} name The name of the field to get the value of.
11170      * @return {Object} The value of the field.
11171      */
11172     get : function(name){
11173         return this.data[name]; 
11174     },
11175
11176     // private
11177     beginEdit : function(){
11178         this.editing = true;
11179         this.modified = {}; 
11180     },
11181
11182     // private
11183     cancelEdit : function(){
11184         this.editing = false;
11185         delete this.modified;
11186     },
11187
11188     // private
11189     endEdit : function(){
11190         this.editing = false;
11191         if(this.dirty && this.store){
11192             this.store.afterEdit(this);
11193         }
11194     },
11195
11196     /**
11197      * Usually called by the {@link Roo.data.Store} which owns the Record.
11198      * Rejects all changes made to the Record since either creation, or the last commit operation.
11199      * Modified fields are reverted to their original values.
11200      * <p>
11201      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11202      * of reject operations.
11203      */
11204     reject : function(){
11205         var m = this.modified;
11206         for(var n in m){
11207             if(typeof m[n] != "function"){
11208                 this.data[n] = m[n];
11209             }
11210         }
11211         this.dirty = false;
11212         delete this.modified;
11213         this.editing = false;
11214         if(this.store){
11215             this.store.afterReject(this);
11216         }
11217     },
11218
11219     /**
11220      * Usually called by the {@link Roo.data.Store} which owns the Record.
11221      * Commits all changes made to the Record since either creation, or the last commit operation.
11222      * <p>
11223      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11224      * of commit operations.
11225      */
11226     commit : function(){
11227         this.dirty = false;
11228         delete this.modified;
11229         this.editing = false;
11230         if(this.store){
11231             this.store.afterCommit(this);
11232         }
11233     },
11234
11235     // private
11236     hasError : function(){
11237         return this.error != null;
11238     },
11239
11240     // private
11241     clearError : function(){
11242         this.error = null;
11243     },
11244
11245     /**
11246      * Creates a copy of this record.
11247      * @param {String} id (optional) A new record id if you don't want to use this record's id
11248      * @return {Record}
11249      */
11250     copy : function(newId) {
11251         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11252     }
11253 };/*
11254  * Based on:
11255  * Ext JS Library 1.1.1
11256  * Copyright(c) 2006-2007, Ext JS, LLC.
11257  *
11258  * Originally Released Under LGPL - original licence link has changed is not relivant.
11259  *
11260  * Fork - LGPL
11261  * <script type="text/javascript">
11262  */
11263
11264
11265
11266 /**
11267  * @class Roo.data.Store
11268  * @extends Roo.util.Observable
11269  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11270  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11271  * <p>
11272  * 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
11273  * has no knowledge of the format of the data returned by the Proxy.<br>
11274  * <p>
11275  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11276  * instances from the data object. These records are cached and made available through accessor functions.
11277  * @constructor
11278  * Creates a new Store.
11279  * @param {Object} config A config object containing the objects needed for the Store to access data,
11280  * and read the data into Records.
11281  */
11282 Roo.data.Store = function(config){
11283     this.data = new Roo.util.MixedCollection(false);
11284     this.data.getKey = function(o){
11285         return o.id;
11286     };
11287     this.baseParams = {};
11288     // private
11289     this.paramNames = {
11290         "start" : "start",
11291         "limit" : "limit",
11292         "sort" : "sort",
11293         "dir" : "dir",
11294         "multisort" : "_multisort"
11295     };
11296
11297     if(config && config.data){
11298         this.inlineData = config.data;
11299         delete config.data;
11300     }
11301
11302     Roo.apply(this, config);
11303     
11304     if(this.reader){ // reader passed
11305         this.reader = Roo.factory(this.reader, Roo.data);
11306         this.reader.xmodule = this.xmodule || false;
11307         if(!this.recordType){
11308             this.recordType = this.reader.recordType;
11309         }
11310         if(this.reader.onMetaChange){
11311             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11312         }
11313     }
11314
11315     if(this.recordType){
11316         this.fields = this.recordType.prototype.fields;
11317     }
11318     this.modified = [];
11319
11320     this.addEvents({
11321         /**
11322          * @event datachanged
11323          * Fires when the data cache has changed, and a widget which is using this Store
11324          * as a Record cache should refresh its view.
11325          * @param {Store} this
11326          */
11327         datachanged : true,
11328         /**
11329          * @event metachange
11330          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11331          * @param {Store} this
11332          * @param {Object} meta The JSON metadata
11333          */
11334         metachange : true,
11335         /**
11336          * @event add
11337          * Fires when Records have been added to the Store
11338          * @param {Store} this
11339          * @param {Roo.data.Record[]} records The array of Records added
11340          * @param {Number} index The index at which the record(s) were added
11341          */
11342         add : true,
11343         /**
11344          * @event remove
11345          * Fires when a Record has been removed from the Store
11346          * @param {Store} this
11347          * @param {Roo.data.Record} record The Record that was removed
11348          * @param {Number} index The index at which the record was removed
11349          */
11350         remove : true,
11351         /**
11352          * @event update
11353          * Fires when a Record has been updated
11354          * @param {Store} this
11355          * @param {Roo.data.Record} record The Record that was updated
11356          * @param {String} operation The update operation being performed.  Value may be one of:
11357          * <pre><code>
11358  Roo.data.Record.EDIT
11359  Roo.data.Record.REJECT
11360  Roo.data.Record.COMMIT
11361          * </code></pre>
11362          */
11363         update : true,
11364         /**
11365          * @event clear
11366          * Fires when the data cache has been cleared.
11367          * @param {Store} this
11368          */
11369         clear : true,
11370         /**
11371          * @event beforeload
11372          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11373          * the load action will be canceled.
11374          * @param {Store} this
11375          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11376          */
11377         beforeload : true,
11378         /**
11379          * @event beforeloadadd
11380          * Fires after a new set of Records has been loaded.
11381          * @param {Store} this
11382          * @param {Roo.data.Record[]} records The Records that were loaded
11383          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11384          */
11385         beforeloadadd : true,
11386         /**
11387          * @event load
11388          * Fires after a new set of Records has been loaded, before they are added to the store.
11389          * @param {Store} this
11390          * @param {Roo.data.Record[]} records The Records that were loaded
11391          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11392          * @params {Object} return from reader
11393          */
11394         load : true,
11395         /**
11396          * @event loadexception
11397          * Fires if an exception occurs in the Proxy during loading.
11398          * Called with the signature of the Proxy's "loadexception" event.
11399          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11400          * 
11401          * @param {Proxy} 
11402          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11403          * @param {Object} load options 
11404          * @param {Object} jsonData from your request (normally this contains the Exception)
11405          */
11406         loadexception : true
11407     });
11408     
11409     if(this.proxy){
11410         this.proxy = Roo.factory(this.proxy, Roo.data);
11411         this.proxy.xmodule = this.xmodule || false;
11412         this.relayEvents(this.proxy,  ["loadexception"]);
11413     }
11414     this.sortToggle = {};
11415     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11416
11417     Roo.data.Store.superclass.constructor.call(this);
11418
11419     if(this.inlineData){
11420         this.loadData(this.inlineData);
11421         delete this.inlineData;
11422     }
11423 };
11424
11425 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11426      /**
11427     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11428     * without a remote query - used by combo/forms at present.
11429     */
11430     
11431     /**
11432     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11433     */
11434     /**
11435     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11436     */
11437     /**
11438     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11439     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11440     */
11441     /**
11442     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11443     * on any HTTP request
11444     */
11445     /**
11446     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11447     */
11448     /**
11449     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11450     */
11451     multiSort: false,
11452     /**
11453     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11454     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11455     */
11456     remoteSort : false,
11457
11458     /**
11459     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11460      * loaded or when a record is removed. (defaults to false).
11461     */
11462     pruneModifiedRecords : false,
11463
11464     // private
11465     lastOptions : null,
11466
11467     /**
11468      * Add Records to the Store and fires the add event.
11469      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11470      */
11471     add : function(records){
11472         records = [].concat(records);
11473         for(var i = 0, len = records.length; i < len; i++){
11474             records[i].join(this);
11475         }
11476         var index = this.data.length;
11477         this.data.addAll(records);
11478         this.fireEvent("add", this, records, index);
11479     },
11480
11481     /**
11482      * Remove a Record from the Store and fires the remove event.
11483      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11484      */
11485     remove : function(record){
11486         var index = this.data.indexOf(record);
11487         this.data.removeAt(index);
11488  
11489         if(this.pruneModifiedRecords){
11490             this.modified.remove(record);
11491         }
11492         this.fireEvent("remove", this, record, index);
11493     },
11494
11495     /**
11496      * Remove all Records from the Store and fires the clear event.
11497      */
11498     removeAll : function(){
11499         this.data.clear();
11500         if(this.pruneModifiedRecords){
11501             this.modified = [];
11502         }
11503         this.fireEvent("clear", this);
11504     },
11505
11506     /**
11507      * Inserts Records to the Store at the given index and fires the add event.
11508      * @param {Number} index The start index at which to insert the passed Records.
11509      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11510      */
11511     insert : function(index, records){
11512         records = [].concat(records);
11513         for(var i = 0, len = records.length; i < len; i++){
11514             this.data.insert(index, records[i]);
11515             records[i].join(this);
11516         }
11517         this.fireEvent("add", this, records, index);
11518     },
11519
11520     /**
11521      * Get the index within the cache of the passed Record.
11522      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11523      * @return {Number} The index of the passed Record. Returns -1 if not found.
11524      */
11525     indexOf : function(record){
11526         return this.data.indexOf(record);
11527     },
11528
11529     /**
11530      * Get the index within the cache of the Record with the passed id.
11531      * @param {String} id The id of the Record to find.
11532      * @return {Number} The index of the Record. Returns -1 if not found.
11533      */
11534     indexOfId : function(id){
11535         return this.data.indexOfKey(id);
11536     },
11537
11538     /**
11539      * Get the Record with the specified id.
11540      * @param {String} id The id of the Record to find.
11541      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11542      */
11543     getById : function(id){
11544         return this.data.key(id);
11545     },
11546
11547     /**
11548      * Get the Record at the specified index.
11549      * @param {Number} index The index of the Record to find.
11550      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11551      */
11552     getAt : function(index){
11553         return this.data.itemAt(index);
11554     },
11555
11556     /**
11557      * Returns a range of Records between specified indices.
11558      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11559      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11560      * @return {Roo.data.Record[]} An array of Records
11561      */
11562     getRange : function(start, end){
11563         return this.data.getRange(start, end);
11564     },
11565
11566     // private
11567     storeOptions : function(o){
11568         o = Roo.apply({}, o);
11569         delete o.callback;
11570         delete o.scope;
11571         this.lastOptions = o;
11572     },
11573
11574     /**
11575      * Loads the Record cache from the configured Proxy using the configured Reader.
11576      * <p>
11577      * If using remote paging, then the first load call must specify the <em>start</em>
11578      * and <em>limit</em> properties in the options.params property to establish the initial
11579      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11580      * <p>
11581      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11582      * and this call will return before the new data has been loaded. Perform any post-processing
11583      * in a callback function, or in a "load" event handler.</strong>
11584      * <p>
11585      * @param {Object} options An object containing properties which control loading options:<ul>
11586      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11587      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11588      * passed the following arguments:<ul>
11589      * <li>r : Roo.data.Record[]</li>
11590      * <li>options: Options object from the load call</li>
11591      * <li>success: Boolean success indicator</li></ul></li>
11592      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11593      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11594      * </ul>
11595      */
11596     load : function(options){
11597         options = options || {};
11598         if(this.fireEvent("beforeload", this, options) !== false){
11599             this.storeOptions(options);
11600             var p = Roo.apply(options.params || {}, this.baseParams);
11601             // if meta was not loaded from remote source.. try requesting it.
11602             if (!this.reader.metaFromRemote) {
11603                 p._requestMeta = 1;
11604             }
11605             if(this.sortInfo && this.remoteSort){
11606                 var pn = this.paramNames;
11607                 p[pn["sort"]] = this.sortInfo.field;
11608                 p[pn["dir"]] = this.sortInfo.direction;
11609             }
11610             if (this.multiSort) {
11611                 var pn = this.paramNames;
11612                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11613             }
11614             
11615             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11616         }
11617     },
11618
11619     /**
11620      * Reloads the Record cache from the configured Proxy using the configured Reader and
11621      * the options from the last load operation performed.
11622      * @param {Object} options (optional) An object containing properties which may override the options
11623      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11624      * the most recently used options are reused).
11625      */
11626     reload : function(options){
11627         this.load(Roo.applyIf(options||{}, this.lastOptions));
11628     },
11629
11630     // private
11631     // Called as a callback by the Reader during a load operation.
11632     loadRecords : function(o, options, success){
11633         if(!o || success === false){
11634             if(success !== false){
11635                 this.fireEvent("load", this, [], options, o);
11636             }
11637             if(options.callback){
11638                 options.callback.call(options.scope || this, [], options, false);
11639             }
11640             return;
11641         }
11642         // if data returned failure - throw an exception.
11643         if (o.success === false) {
11644             // show a message if no listener is registered.
11645             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11646                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11647             }
11648             // loadmask wil be hooked into this..
11649             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11650             return;
11651         }
11652         var r = o.records, t = o.totalRecords || r.length;
11653         
11654         this.fireEvent("beforeloadadd", this, r, options, o);
11655         
11656         if(!options || options.add !== true){
11657             if(this.pruneModifiedRecords){
11658                 this.modified = [];
11659             }
11660             for(var i = 0, len = r.length; i < len; i++){
11661                 r[i].join(this);
11662             }
11663             if(this.snapshot){
11664                 this.data = this.snapshot;
11665                 delete this.snapshot;
11666             }
11667             this.data.clear();
11668             this.data.addAll(r);
11669             this.totalLength = t;
11670             this.applySort();
11671             this.fireEvent("datachanged", this);
11672         }else{
11673             this.totalLength = Math.max(t, this.data.length+r.length);
11674             this.add(r);
11675         }
11676         
11677         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11678                 
11679             var e = new Roo.data.Record({});
11680
11681             e.set(this.parent.displayField, this.parent.emptyTitle);
11682             e.set(this.parent.valueField, '');
11683
11684             this.insert(0, e);
11685         }
11686             
11687         this.fireEvent("load", this, r, options, o);
11688         if(options.callback){
11689             options.callback.call(options.scope || this, r, options, true);
11690         }
11691     },
11692
11693
11694     /**
11695      * Loads data from a passed data block. A Reader which understands the format of the data
11696      * must have been configured in the constructor.
11697      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11698      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11699      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11700      */
11701     loadData : function(o, append){
11702         var r = this.reader.readRecords(o);
11703         this.loadRecords(r, {add: append}, true);
11704     },
11705
11706     /**
11707      * Gets the number of cached records.
11708      * <p>
11709      * <em>If using paging, this may not be the total size of the dataset. If the data object
11710      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11711      * the data set size</em>
11712      */
11713     getCount : function(){
11714         return this.data.length || 0;
11715     },
11716
11717     /**
11718      * Gets the total number of records in the dataset as returned by the server.
11719      * <p>
11720      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11721      * the dataset size</em>
11722      */
11723     getTotalCount : function(){
11724         return this.totalLength || 0;
11725     },
11726
11727     /**
11728      * Returns the sort state of the Store as an object with two properties:
11729      * <pre><code>
11730  field {String} The name of the field by which the Records are sorted
11731  direction {String} The sort order, "ASC" or "DESC"
11732      * </code></pre>
11733      */
11734     getSortState : function(){
11735         return this.sortInfo;
11736     },
11737
11738     // private
11739     applySort : function(){
11740         if(this.sortInfo && !this.remoteSort){
11741             var s = this.sortInfo, f = s.field;
11742             var st = this.fields.get(f).sortType;
11743             var fn = function(r1, r2){
11744                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11745                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11746             };
11747             this.data.sort(s.direction, fn);
11748             if(this.snapshot && this.snapshot != this.data){
11749                 this.snapshot.sort(s.direction, fn);
11750             }
11751         }
11752     },
11753
11754     /**
11755      * Sets the default sort column and order to be used by the next load operation.
11756      * @param {String} fieldName The name of the field to sort by.
11757      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11758      */
11759     setDefaultSort : function(field, dir){
11760         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11761     },
11762
11763     /**
11764      * Sort the Records.
11765      * If remote sorting is used, the sort is performed on the server, and the cache is
11766      * reloaded. If local sorting is used, the cache is sorted internally.
11767      * @param {String} fieldName The name of the field to sort by.
11768      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11769      */
11770     sort : function(fieldName, dir){
11771         var f = this.fields.get(fieldName);
11772         if(!dir){
11773             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11774             
11775             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11776                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11777             }else{
11778                 dir = f.sortDir;
11779             }
11780         }
11781         this.sortToggle[f.name] = dir;
11782         this.sortInfo = {field: f.name, direction: dir};
11783         if(!this.remoteSort){
11784             this.applySort();
11785             this.fireEvent("datachanged", this);
11786         }else{
11787             this.load(this.lastOptions);
11788         }
11789     },
11790
11791     /**
11792      * Calls the specified function for each of the Records in the cache.
11793      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11794      * Returning <em>false</em> aborts and exits the iteration.
11795      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11796      */
11797     each : function(fn, scope){
11798         this.data.each(fn, scope);
11799     },
11800
11801     /**
11802      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11803      * (e.g., during paging).
11804      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11805      */
11806     getModifiedRecords : function(){
11807         return this.modified;
11808     },
11809
11810     // private
11811     createFilterFn : function(property, value, anyMatch){
11812         if(!value.exec){ // not a regex
11813             value = String(value);
11814             if(value.length == 0){
11815                 return false;
11816             }
11817             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11818         }
11819         return function(r){
11820             return value.test(r.data[property]);
11821         };
11822     },
11823
11824     /**
11825      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11826      * @param {String} property A field on your records
11827      * @param {Number} start The record index to start at (defaults to 0)
11828      * @param {Number} end The last record index to include (defaults to length - 1)
11829      * @return {Number} The sum
11830      */
11831     sum : function(property, start, end){
11832         var rs = this.data.items, v = 0;
11833         start = start || 0;
11834         end = (end || end === 0) ? end : rs.length-1;
11835
11836         for(var i = start; i <= end; i++){
11837             v += (rs[i].data[property] || 0);
11838         }
11839         return v;
11840     },
11841
11842     /**
11843      * Filter the records by a specified property.
11844      * @param {String} field A field on your records
11845      * @param {String/RegExp} value Either a string that the field
11846      * should start with or a RegExp to test against the field
11847      * @param {Boolean} anyMatch True to match any part not just the beginning
11848      */
11849     filter : function(property, value, anyMatch){
11850         var fn = this.createFilterFn(property, value, anyMatch);
11851         return fn ? this.filterBy(fn) : this.clearFilter();
11852     },
11853
11854     /**
11855      * Filter by a function. The specified function will be called with each
11856      * record in this data source. If the function returns true the record is included,
11857      * otherwise it is filtered.
11858      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11859      * @param {Object} scope (optional) The scope of the function (defaults to this)
11860      */
11861     filterBy : function(fn, scope){
11862         this.snapshot = this.snapshot || this.data;
11863         this.data = this.queryBy(fn, scope||this);
11864         this.fireEvent("datachanged", this);
11865     },
11866
11867     /**
11868      * Query the records by a specified property.
11869      * @param {String} field A field on your records
11870      * @param {String/RegExp} value Either a string that the field
11871      * should start with or a RegExp to test against the field
11872      * @param {Boolean} anyMatch True to match any part not just the beginning
11873      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11874      */
11875     query : function(property, value, anyMatch){
11876         var fn = this.createFilterFn(property, value, anyMatch);
11877         return fn ? this.queryBy(fn) : this.data.clone();
11878     },
11879
11880     /**
11881      * Query by a function. The specified function will be called with each
11882      * record in this data source. If the function returns true the record is included
11883      * in the results.
11884      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11885      * @param {Object} scope (optional) The scope of the function (defaults to this)
11886       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11887      **/
11888     queryBy : function(fn, scope){
11889         var data = this.snapshot || this.data;
11890         return data.filterBy(fn, scope||this);
11891     },
11892
11893     /**
11894      * Collects unique values for a particular dataIndex from this store.
11895      * @param {String} dataIndex The property to collect
11896      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11897      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11898      * @return {Array} An array of the unique values
11899      **/
11900     collect : function(dataIndex, allowNull, bypassFilter){
11901         var d = (bypassFilter === true && this.snapshot) ?
11902                 this.snapshot.items : this.data.items;
11903         var v, sv, r = [], l = {};
11904         for(var i = 0, len = d.length; i < len; i++){
11905             v = d[i].data[dataIndex];
11906             sv = String(v);
11907             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11908                 l[sv] = true;
11909                 r[r.length] = v;
11910             }
11911         }
11912         return r;
11913     },
11914
11915     /**
11916      * Revert to a view of the Record cache with no filtering applied.
11917      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11918      */
11919     clearFilter : function(suppressEvent){
11920         if(this.snapshot && this.snapshot != this.data){
11921             this.data = this.snapshot;
11922             delete this.snapshot;
11923             if(suppressEvent !== true){
11924                 this.fireEvent("datachanged", this);
11925             }
11926         }
11927     },
11928
11929     // private
11930     afterEdit : function(record){
11931         if(this.modified.indexOf(record) == -1){
11932             this.modified.push(record);
11933         }
11934         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11935     },
11936     
11937     // private
11938     afterReject : function(record){
11939         this.modified.remove(record);
11940         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11941     },
11942
11943     // private
11944     afterCommit : function(record){
11945         this.modified.remove(record);
11946         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11947     },
11948
11949     /**
11950      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11951      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11952      */
11953     commitChanges : function(){
11954         var m = this.modified.slice(0);
11955         this.modified = [];
11956         for(var i = 0, len = m.length; i < len; i++){
11957             m[i].commit();
11958         }
11959     },
11960
11961     /**
11962      * Cancel outstanding changes on all changed records.
11963      */
11964     rejectChanges : function(){
11965         var m = this.modified.slice(0);
11966         this.modified = [];
11967         for(var i = 0, len = m.length; i < len; i++){
11968             m[i].reject();
11969         }
11970     },
11971
11972     onMetaChange : function(meta, rtype, o){
11973         this.recordType = rtype;
11974         this.fields = rtype.prototype.fields;
11975         delete this.snapshot;
11976         this.sortInfo = meta.sortInfo || this.sortInfo;
11977         this.modified = [];
11978         this.fireEvent('metachange', this, this.reader.meta);
11979     },
11980     
11981     moveIndex : function(data, type)
11982     {
11983         var index = this.indexOf(data);
11984         
11985         var newIndex = index + type;
11986         
11987         this.remove(data);
11988         
11989         this.insert(newIndex, data);
11990         
11991     }
11992 });/*
11993  * Based on:
11994  * Ext JS Library 1.1.1
11995  * Copyright(c) 2006-2007, Ext JS, LLC.
11996  *
11997  * Originally Released Under LGPL - original licence link has changed is not relivant.
11998  *
11999  * Fork - LGPL
12000  * <script type="text/javascript">
12001  */
12002
12003 /**
12004  * @class Roo.data.SimpleStore
12005  * @extends Roo.data.Store
12006  * Small helper class to make creating Stores from Array data easier.
12007  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12008  * @cfg {Array} fields An array of field definition objects, or field name strings.
12009  * @cfg {Array} data The multi-dimensional array of data
12010  * @constructor
12011  * @param {Object} config
12012  */
12013 Roo.data.SimpleStore = function(config){
12014     Roo.data.SimpleStore.superclass.constructor.call(this, {
12015         isLocal : true,
12016         reader: new Roo.data.ArrayReader({
12017                 id: config.id
12018             },
12019             Roo.data.Record.create(config.fields)
12020         ),
12021         proxy : new Roo.data.MemoryProxy(config.data)
12022     });
12023     this.load();
12024 };
12025 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12026  * Based on:
12027  * Ext JS Library 1.1.1
12028  * Copyright(c) 2006-2007, Ext JS, LLC.
12029  *
12030  * Originally Released Under LGPL - original licence link has changed is not relivant.
12031  *
12032  * Fork - LGPL
12033  * <script type="text/javascript">
12034  */
12035
12036 /**
12037 /**
12038  * @extends Roo.data.Store
12039  * @class Roo.data.JsonStore
12040  * Small helper class to make creating Stores for JSON data easier. <br/>
12041 <pre><code>
12042 var store = new Roo.data.JsonStore({
12043     url: 'get-images.php',
12044     root: 'images',
12045     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12046 });
12047 </code></pre>
12048  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12049  * JsonReader and HttpProxy (unless inline data is provided).</b>
12050  * @cfg {Array} fields An array of field definition objects, or field name strings.
12051  * @constructor
12052  * @param {Object} config
12053  */
12054 Roo.data.JsonStore = function(c){
12055     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12056         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12057         reader: new Roo.data.JsonReader(c, c.fields)
12058     }));
12059 };
12060 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12061  * Based on:
12062  * Ext JS Library 1.1.1
12063  * Copyright(c) 2006-2007, Ext JS, LLC.
12064  *
12065  * Originally Released Under LGPL - original licence link has changed is not relivant.
12066  *
12067  * Fork - LGPL
12068  * <script type="text/javascript">
12069  */
12070
12071  
12072 Roo.data.Field = function(config){
12073     if(typeof config == "string"){
12074         config = {name: config};
12075     }
12076     Roo.apply(this, config);
12077     
12078     if(!this.type){
12079         this.type = "auto";
12080     }
12081     
12082     var st = Roo.data.SortTypes;
12083     // named sortTypes are supported, here we look them up
12084     if(typeof this.sortType == "string"){
12085         this.sortType = st[this.sortType];
12086     }
12087     
12088     // set default sortType for strings and dates
12089     if(!this.sortType){
12090         switch(this.type){
12091             case "string":
12092                 this.sortType = st.asUCString;
12093                 break;
12094             case "date":
12095                 this.sortType = st.asDate;
12096                 break;
12097             default:
12098                 this.sortType = st.none;
12099         }
12100     }
12101
12102     // define once
12103     var stripRe = /[\$,%]/g;
12104
12105     // prebuilt conversion function for this field, instead of
12106     // switching every time we're reading a value
12107     if(!this.convert){
12108         var cv, dateFormat = this.dateFormat;
12109         switch(this.type){
12110             case "":
12111             case "auto":
12112             case undefined:
12113                 cv = function(v){ return v; };
12114                 break;
12115             case "string":
12116                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12117                 break;
12118             case "int":
12119                 cv = function(v){
12120                     return v !== undefined && v !== null && v !== '' ?
12121                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12122                     };
12123                 break;
12124             case "float":
12125                 cv = function(v){
12126                     return v !== undefined && v !== null && v !== '' ?
12127                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12128                     };
12129                 break;
12130             case "bool":
12131             case "boolean":
12132                 cv = function(v){ return v === true || v === "true" || v == 1; };
12133                 break;
12134             case "date":
12135                 cv = function(v){
12136                     if(!v){
12137                         return '';
12138                     }
12139                     if(v instanceof Date){
12140                         return v;
12141                     }
12142                     if(dateFormat){
12143                         if(dateFormat == "timestamp"){
12144                             return new Date(v*1000);
12145                         }
12146                         return Date.parseDate(v, dateFormat);
12147                     }
12148                     var parsed = Date.parse(v);
12149                     return parsed ? new Date(parsed) : null;
12150                 };
12151              break;
12152             
12153         }
12154         this.convert = cv;
12155     }
12156 };
12157
12158 Roo.data.Field.prototype = {
12159     dateFormat: null,
12160     defaultValue: "",
12161     mapping: null,
12162     sortType : null,
12163     sortDir : "ASC"
12164 };/*
12165  * Based on:
12166  * Ext JS Library 1.1.1
12167  * Copyright(c) 2006-2007, Ext JS, LLC.
12168  *
12169  * Originally Released Under LGPL - original licence link has changed is not relivant.
12170  *
12171  * Fork - LGPL
12172  * <script type="text/javascript">
12173  */
12174  
12175 // Base class for reading structured data from a data source.  This class is intended to be
12176 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12177
12178 /**
12179  * @class Roo.data.DataReader
12180  * Base class for reading structured data from a data source.  This class is intended to be
12181  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12182  */
12183
12184 Roo.data.DataReader = function(meta, recordType){
12185     
12186     this.meta = meta;
12187     
12188     this.recordType = recordType instanceof Array ? 
12189         Roo.data.Record.create(recordType) : recordType;
12190 };
12191
12192 Roo.data.DataReader.prototype = {
12193      /**
12194      * Create an empty record
12195      * @param {Object} data (optional) - overlay some values
12196      * @return {Roo.data.Record} record created.
12197      */
12198     newRow :  function(d) {
12199         var da =  {};
12200         this.recordType.prototype.fields.each(function(c) {
12201             switch( c.type) {
12202                 case 'int' : da[c.name] = 0; break;
12203                 case 'date' : da[c.name] = new Date(); break;
12204                 case 'float' : da[c.name] = 0.0; break;
12205                 case 'boolean' : da[c.name] = false; break;
12206                 default : da[c.name] = ""; break;
12207             }
12208             
12209         });
12210         return new this.recordType(Roo.apply(da, d));
12211     }
12212     
12213 };/*
12214  * Based on:
12215  * Ext JS Library 1.1.1
12216  * Copyright(c) 2006-2007, Ext JS, LLC.
12217  *
12218  * Originally Released Under LGPL - original licence link has changed is not relivant.
12219  *
12220  * Fork - LGPL
12221  * <script type="text/javascript">
12222  */
12223
12224 /**
12225  * @class Roo.data.DataProxy
12226  * @extends Roo.data.Observable
12227  * This class is an abstract base class for implementations which provide retrieval of
12228  * unformatted data objects.<br>
12229  * <p>
12230  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12231  * (of the appropriate type which knows how to parse the data object) to provide a block of
12232  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12233  * <p>
12234  * Custom implementations must implement the load method as described in
12235  * {@link Roo.data.HttpProxy#load}.
12236  */
12237 Roo.data.DataProxy = function(){
12238     this.addEvents({
12239         /**
12240          * @event beforeload
12241          * Fires before a network request is made to retrieve a data object.
12242          * @param {Object} This DataProxy object.
12243          * @param {Object} params The params parameter to the load function.
12244          */
12245         beforeload : true,
12246         /**
12247          * @event load
12248          * Fires before the load method's callback is called.
12249          * @param {Object} This DataProxy object.
12250          * @param {Object} o The data object.
12251          * @param {Object} arg The callback argument object passed to the load function.
12252          */
12253         load : true,
12254         /**
12255          * @event loadexception
12256          * Fires if an Exception occurs during data retrieval.
12257          * @param {Object} This DataProxy object.
12258          * @param {Object} o The data object.
12259          * @param {Object} arg The callback argument object passed to the load function.
12260          * @param {Object} e The Exception.
12261          */
12262         loadexception : true
12263     });
12264     Roo.data.DataProxy.superclass.constructor.call(this);
12265 };
12266
12267 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12268
12269     /**
12270      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12271      */
12272 /*
12273  * Based on:
12274  * Ext JS Library 1.1.1
12275  * Copyright(c) 2006-2007, Ext JS, LLC.
12276  *
12277  * Originally Released Under LGPL - original licence link has changed is not relivant.
12278  *
12279  * Fork - LGPL
12280  * <script type="text/javascript">
12281  */
12282 /**
12283  * @class Roo.data.MemoryProxy
12284  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12285  * to the Reader when its load method is called.
12286  * @constructor
12287  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12288  */
12289 Roo.data.MemoryProxy = function(data){
12290     if (data.data) {
12291         data = data.data;
12292     }
12293     Roo.data.MemoryProxy.superclass.constructor.call(this);
12294     this.data = data;
12295 };
12296
12297 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12298     
12299     /**
12300      * Load data from the requested source (in this case an in-memory
12301      * data object passed to the constructor), read the data object into
12302      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12303      * process that block using the passed callback.
12304      * @param {Object} params This parameter is not used by the MemoryProxy class.
12305      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12306      * object into a block of Roo.data.Records.
12307      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12308      * The function must be passed <ul>
12309      * <li>The Record block object</li>
12310      * <li>The "arg" argument from the load function</li>
12311      * <li>A boolean success indicator</li>
12312      * </ul>
12313      * @param {Object} scope The scope in which to call the callback
12314      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12315      */
12316     load : function(params, reader, callback, scope, arg){
12317         params = params || {};
12318         var result;
12319         try {
12320             result = reader.readRecords(this.data);
12321         }catch(e){
12322             this.fireEvent("loadexception", this, arg, null, e);
12323             callback.call(scope, null, arg, false);
12324             return;
12325         }
12326         callback.call(scope, result, arg, true);
12327     },
12328     
12329     // private
12330     update : function(params, records){
12331         
12332     }
12333 });/*
12334  * Based on:
12335  * Ext JS Library 1.1.1
12336  * Copyright(c) 2006-2007, Ext JS, LLC.
12337  *
12338  * Originally Released Under LGPL - original licence link has changed is not relivant.
12339  *
12340  * Fork - LGPL
12341  * <script type="text/javascript">
12342  */
12343 /**
12344  * @class Roo.data.HttpProxy
12345  * @extends Roo.data.DataProxy
12346  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12347  * configured to reference a certain URL.<br><br>
12348  * <p>
12349  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12350  * from which the running page was served.<br><br>
12351  * <p>
12352  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12353  * <p>
12354  * Be aware that to enable the browser to parse an XML document, the server must set
12355  * the Content-Type header in the HTTP response to "text/xml".
12356  * @constructor
12357  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12358  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12359  * will be used to make the request.
12360  */
12361 Roo.data.HttpProxy = function(conn){
12362     Roo.data.HttpProxy.superclass.constructor.call(this);
12363     // is conn a conn config or a real conn?
12364     this.conn = conn;
12365     this.useAjax = !conn || !conn.events;
12366   
12367 };
12368
12369 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12370     // thse are take from connection...
12371     
12372     /**
12373      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12374      */
12375     /**
12376      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12377      * extra parameters to each request made by this object. (defaults to undefined)
12378      */
12379     /**
12380      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12381      *  to each request made by this object. (defaults to undefined)
12382      */
12383     /**
12384      * @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)
12385      */
12386     /**
12387      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12388      */
12389      /**
12390      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12391      * @type Boolean
12392      */
12393   
12394
12395     /**
12396      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12397      * @type Boolean
12398      */
12399     /**
12400      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12401      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12402      * a finer-grained basis than the DataProxy events.
12403      */
12404     getConnection : function(){
12405         return this.useAjax ? Roo.Ajax : this.conn;
12406     },
12407
12408     /**
12409      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12410      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12411      * process that block using the passed callback.
12412      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12413      * for the request to the remote server.
12414      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12415      * object into a block of Roo.data.Records.
12416      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12417      * The function must be passed <ul>
12418      * <li>The Record block object</li>
12419      * <li>The "arg" argument from the load function</li>
12420      * <li>A boolean success indicator</li>
12421      * </ul>
12422      * @param {Object} scope The scope in which to call the callback
12423      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12424      */
12425     load : function(params, reader, callback, scope, arg){
12426         if(this.fireEvent("beforeload", this, params) !== false){
12427             var  o = {
12428                 params : params || {},
12429                 request: {
12430                     callback : callback,
12431                     scope : scope,
12432                     arg : arg
12433                 },
12434                 reader: reader,
12435                 callback : this.loadResponse,
12436                 scope: this
12437             };
12438             if(this.useAjax){
12439                 Roo.applyIf(o, this.conn);
12440                 if(this.activeRequest){
12441                     Roo.Ajax.abort(this.activeRequest);
12442                 }
12443                 this.activeRequest = Roo.Ajax.request(o);
12444             }else{
12445                 this.conn.request(o);
12446             }
12447         }else{
12448             callback.call(scope||this, null, arg, false);
12449         }
12450     },
12451
12452     // private
12453     loadResponse : function(o, success, response){
12454         delete this.activeRequest;
12455         if(!success){
12456             this.fireEvent("loadexception", this, o, response);
12457             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12458             return;
12459         }
12460         var result;
12461         try {
12462             result = o.reader.read(response);
12463         }catch(e){
12464             this.fireEvent("loadexception", this, o, response, e);
12465             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12466             return;
12467         }
12468         
12469         this.fireEvent("load", this, o, o.request.arg);
12470         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12471     },
12472
12473     // private
12474     update : function(dataSet){
12475
12476     },
12477
12478     // private
12479     updateResponse : function(dataSet){
12480
12481     }
12482 });/*
12483  * Based on:
12484  * Ext JS Library 1.1.1
12485  * Copyright(c) 2006-2007, Ext JS, LLC.
12486  *
12487  * Originally Released Under LGPL - original licence link has changed is not relivant.
12488  *
12489  * Fork - LGPL
12490  * <script type="text/javascript">
12491  */
12492
12493 /**
12494  * @class Roo.data.ScriptTagProxy
12495  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12496  * other than the originating domain of the running page.<br><br>
12497  * <p>
12498  * <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
12499  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12500  * <p>
12501  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12502  * source code that is used as the source inside a &lt;script> tag.<br><br>
12503  * <p>
12504  * In order for the browser to process the returned data, the server must wrap the data object
12505  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12506  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12507  * depending on whether the callback name was passed:
12508  * <p>
12509  * <pre><code>
12510 boolean scriptTag = false;
12511 String cb = request.getParameter("callback");
12512 if (cb != null) {
12513     scriptTag = true;
12514     response.setContentType("text/javascript");
12515 } else {
12516     response.setContentType("application/x-json");
12517 }
12518 Writer out = response.getWriter();
12519 if (scriptTag) {
12520     out.write(cb + "(");
12521 }
12522 out.print(dataBlock.toJsonString());
12523 if (scriptTag) {
12524     out.write(");");
12525 }
12526 </pre></code>
12527  *
12528  * @constructor
12529  * @param {Object} config A configuration object.
12530  */
12531 Roo.data.ScriptTagProxy = function(config){
12532     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12533     Roo.apply(this, config);
12534     this.head = document.getElementsByTagName("head")[0];
12535 };
12536
12537 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12538
12539 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12540     /**
12541      * @cfg {String} url The URL from which to request the data object.
12542      */
12543     /**
12544      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12545      */
12546     timeout : 30000,
12547     /**
12548      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12549      * the server the name of the callback function set up by the load call to process the returned data object.
12550      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12551      * javascript output which calls this named function passing the data object as its only parameter.
12552      */
12553     callbackParam : "callback",
12554     /**
12555      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12556      * name to the request.
12557      */
12558     nocache : true,
12559
12560     /**
12561      * Load data from the configured URL, read the data object into
12562      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12563      * process that block using the passed callback.
12564      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12565      * for the request to the remote server.
12566      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12567      * object into a block of Roo.data.Records.
12568      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12569      * The function must be passed <ul>
12570      * <li>The Record block object</li>
12571      * <li>The "arg" argument from the load function</li>
12572      * <li>A boolean success indicator</li>
12573      * </ul>
12574      * @param {Object} scope The scope in which to call the callback
12575      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12576      */
12577     load : function(params, reader, callback, scope, arg){
12578         if(this.fireEvent("beforeload", this, params) !== false){
12579
12580             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12581
12582             var url = this.url;
12583             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12584             if(this.nocache){
12585                 url += "&_dc=" + (new Date().getTime());
12586             }
12587             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12588             var trans = {
12589                 id : transId,
12590                 cb : "stcCallback"+transId,
12591                 scriptId : "stcScript"+transId,
12592                 params : params,
12593                 arg : arg,
12594                 url : url,
12595                 callback : callback,
12596                 scope : scope,
12597                 reader : reader
12598             };
12599             var conn = this;
12600
12601             window[trans.cb] = function(o){
12602                 conn.handleResponse(o, trans);
12603             };
12604
12605             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12606
12607             if(this.autoAbort !== false){
12608                 this.abort();
12609             }
12610
12611             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12612
12613             var script = document.createElement("script");
12614             script.setAttribute("src", url);
12615             script.setAttribute("type", "text/javascript");
12616             script.setAttribute("id", trans.scriptId);
12617             this.head.appendChild(script);
12618
12619             this.trans = trans;
12620         }else{
12621             callback.call(scope||this, null, arg, false);
12622         }
12623     },
12624
12625     // private
12626     isLoading : function(){
12627         return this.trans ? true : false;
12628     },
12629
12630     /**
12631      * Abort the current server request.
12632      */
12633     abort : function(){
12634         if(this.isLoading()){
12635             this.destroyTrans(this.trans);
12636         }
12637     },
12638
12639     // private
12640     destroyTrans : function(trans, isLoaded){
12641         this.head.removeChild(document.getElementById(trans.scriptId));
12642         clearTimeout(trans.timeoutId);
12643         if(isLoaded){
12644             window[trans.cb] = undefined;
12645             try{
12646                 delete window[trans.cb];
12647             }catch(e){}
12648         }else{
12649             // if hasn't been loaded, wait for load to remove it to prevent script error
12650             window[trans.cb] = function(){
12651                 window[trans.cb] = undefined;
12652                 try{
12653                     delete window[trans.cb];
12654                 }catch(e){}
12655             };
12656         }
12657     },
12658
12659     // private
12660     handleResponse : function(o, trans){
12661         this.trans = false;
12662         this.destroyTrans(trans, true);
12663         var result;
12664         try {
12665             result = trans.reader.readRecords(o);
12666         }catch(e){
12667             this.fireEvent("loadexception", this, o, trans.arg, e);
12668             trans.callback.call(trans.scope||window, null, trans.arg, false);
12669             return;
12670         }
12671         this.fireEvent("load", this, o, trans.arg);
12672         trans.callback.call(trans.scope||window, result, trans.arg, true);
12673     },
12674
12675     // private
12676     handleFailure : function(trans){
12677         this.trans = false;
12678         this.destroyTrans(trans, false);
12679         this.fireEvent("loadexception", this, null, trans.arg);
12680         trans.callback.call(trans.scope||window, null, trans.arg, false);
12681     }
12682 });/*
12683  * Based on:
12684  * Ext JS Library 1.1.1
12685  * Copyright(c) 2006-2007, Ext JS, LLC.
12686  *
12687  * Originally Released Under LGPL - original licence link has changed is not relivant.
12688  *
12689  * Fork - LGPL
12690  * <script type="text/javascript">
12691  */
12692
12693 /**
12694  * @class Roo.data.JsonReader
12695  * @extends Roo.data.DataReader
12696  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12697  * based on mappings in a provided Roo.data.Record constructor.
12698  * 
12699  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12700  * in the reply previously. 
12701  * 
12702  * <p>
12703  * Example code:
12704  * <pre><code>
12705 var RecordDef = Roo.data.Record.create([
12706     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12707     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12708 ]);
12709 var myReader = new Roo.data.JsonReader({
12710     totalProperty: "results",    // The property which contains the total dataset size (optional)
12711     root: "rows",                // The property which contains an Array of row objects
12712     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12713 }, RecordDef);
12714 </code></pre>
12715  * <p>
12716  * This would consume a JSON file like this:
12717  * <pre><code>
12718 { 'results': 2, 'rows': [
12719     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12720     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12721 }
12722 </code></pre>
12723  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12724  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12725  * paged from the remote server.
12726  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12727  * @cfg {String} root name of the property which contains the Array of row objects.
12728  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12729  * @cfg {Array} fields Array of field definition objects
12730  * @constructor
12731  * Create a new JsonReader
12732  * @param {Object} meta Metadata configuration options
12733  * @param {Object} recordType Either an Array of field definition objects,
12734  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12735  */
12736 Roo.data.JsonReader = function(meta, recordType){
12737     
12738     meta = meta || {};
12739     // set some defaults:
12740     Roo.applyIf(meta, {
12741         totalProperty: 'total',
12742         successProperty : 'success',
12743         root : 'data',
12744         id : 'id'
12745     });
12746     
12747     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12748 };
12749 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12750     
12751     /**
12752      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12753      * Used by Store query builder to append _requestMeta to params.
12754      * 
12755      */
12756     metaFromRemote : false,
12757     /**
12758      * This method is only used by a DataProxy which has retrieved data from a remote server.
12759      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12760      * @return {Object} data A data block which is used by an Roo.data.Store object as
12761      * a cache of Roo.data.Records.
12762      */
12763     read : function(response){
12764         var json = response.responseText;
12765        
12766         var o = /* eval:var:o */ eval("("+json+")");
12767         if(!o) {
12768             throw {message: "JsonReader.read: Json object not found"};
12769         }
12770         
12771         if(o.metaData){
12772             
12773             delete this.ef;
12774             this.metaFromRemote = true;
12775             this.meta = o.metaData;
12776             this.recordType = Roo.data.Record.create(o.metaData.fields);
12777             this.onMetaChange(this.meta, this.recordType, o);
12778         }
12779         return this.readRecords(o);
12780     },
12781
12782     // private function a store will implement
12783     onMetaChange : function(meta, recordType, o){
12784
12785     },
12786
12787     /**
12788          * @ignore
12789          */
12790     simpleAccess: function(obj, subsc) {
12791         return obj[subsc];
12792     },
12793
12794         /**
12795          * @ignore
12796          */
12797     getJsonAccessor: function(){
12798         var re = /[\[\.]/;
12799         return function(expr) {
12800             try {
12801                 return(re.test(expr))
12802                     ? new Function("obj", "return obj." + expr)
12803                     : function(obj){
12804                         return obj[expr];
12805                     };
12806             } catch(e){}
12807             return Roo.emptyFn;
12808         };
12809     }(),
12810
12811     /**
12812      * Create a data block containing Roo.data.Records from an XML document.
12813      * @param {Object} o An object which contains an Array of row objects in the property specified
12814      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12815      * which contains the total size of the dataset.
12816      * @return {Object} data A data block which is used by an Roo.data.Store object as
12817      * a cache of Roo.data.Records.
12818      */
12819     readRecords : function(o){
12820         /**
12821          * After any data loads, the raw JSON data is available for further custom processing.
12822          * @type Object
12823          */
12824         this.o = o;
12825         var s = this.meta, Record = this.recordType,
12826             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12827
12828 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12829         if (!this.ef) {
12830             if(s.totalProperty) {
12831                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12832                 }
12833                 if(s.successProperty) {
12834                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12835                 }
12836                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12837                 if (s.id) {
12838                         var g = this.getJsonAccessor(s.id);
12839                         this.getId = function(rec) {
12840                                 var r = g(rec);  
12841                                 return (r === undefined || r === "") ? null : r;
12842                         };
12843                 } else {
12844                         this.getId = function(){return null;};
12845                 }
12846             this.ef = [];
12847             for(var jj = 0; jj < fl; jj++){
12848                 f = fi[jj];
12849                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12850                 this.ef[jj] = this.getJsonAccessor(map);
12851             }
12852         }
12853
12854         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12855         if(s.totalProperty){
12856             var vt = parseInt(this.getTotal(o), 10);
12857             if(!isNaN(vt)){
12858                 totalRecords = vt;
12859             }
12860         }
12861         if(s.successProperty){
12862             var vs = this.getSuccess(o);
12863             if(vs === false || vs === 'false'){
12864                 success = false;
12865             }
12866         }
12867         var records = [];
12868         for(var i = 0; i < c; i++){
12869                 var n = root[i];
12870             var values = {};
12871             var id = this.getId(n);
12872             for(var j = 0; j < fl; j++){
12873                 f = fi[j];
12874             var v = this.ef[j](n);
12875             if (!f.convert) {
12876                 Roo.log('missing convert for ' + f.name);
12877                 Roo.log(f);
12878                 continue;
12879             }
12880             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12881             }
12882             var record = new Record(values, id);
12883             record.json = n;
12884             records[i] = record;
12885         }
12886         return {
12887             raw : o,
12888             success : success,
12889             records : records,
12890             totalRecords : totalRecords
12891         };
12892     }
12893 });/*
12894  * Based on:
12895  * Ext JS Library 1.1.1
12896  * Copyright(c) 2006-2007, Ext JS, LLC.
12897  *
12898  * Originally Released Under LGPL - original licence link has changed is not relivant.
12899  *
12900  * Fork - LGPL
12901  * <script type="text/javascript">
12902  */
12903
12904 /**
12905  * @class Roo.data.ArrayReader
12906  * @extends Roo.data.DataReader
12907  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12908  * Each element of that Array represents a row of data fields. The
12909  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12910  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12911  * <p>
12912  * Example code:.
12913  * <pre><code>
12914 var RecordDef = Roo.data.Record.create([
12915     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12916     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12917 ]);
12918 var myReader = new Roo.data.ArrayReader({
12919     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12920 }, RecordDef);
12921 </code></pre>
12922  * <p>
12923  * This would consume an Array like this:
12924  * <pre><code>
12925 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12926   </code></pre>
12927  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12928  * @constructor
12929  * Create a new JsonReader
12930  * @param {Object} meta Metadata configuration options.
12931  * @param {Object} recordType Either an Array of field definition objects
12932  * as specified to {@link Roo.data.Record#create},
12933  * or an {@link Roo.data.Record} object
12934  * created using {@link Roo.data.Record#create}.
12935  */
12936 Roo.data.ArrayReader = function(meta, recordType){
12937     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12938 };
12939
12940 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12941     /**
12942      * Create a data block containing Roo.data.Records from an XML document.
12943      * @param {Object} o An Array of row objects which represents the dataset.
12944      * @return {Object} data A data block which is used by an Roo.data.Store object as
12945      * a cache of Roo.data.Records.
12946      */
12947     readRecords : function(o){
12948         var sid = this.meta ? this.meta.id : null;
12949         var recordType = this.recordType, fields = recordType.prototype.fields;
12950         var records = [];
12951         var root = o;
12952             for(var i = 0; i < root.length; i++){
12953                     var n = root[i];
12954                 var values = {};
12955                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12956                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12957                 var f = fields.items[j];
12958                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12959                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12960                 v = f.convert(v);
12961                 values[f.name] = v;
12962             }
12963                 var record = new recordType(values, id);
12964                 record.json = n;
12965                 records[records.length] = record;
12966             }
12967             return {
12968                 records : records,
12969                 totalRecords : records.length
12970             };
12971     }
12972 });/*
12973  * - LGPL
12974  * * 
12975  */
12976
12977 /**
12978  * @class Roo.bootstrap.ComboBox
12979  * @extends Roo.bootstrap.TriggerField
12980  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12981  * @cfg {Boolean} append (true|false) default false
12982  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12983  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12984  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12985  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12986  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12987  * @cfg {Boolean} animate default true
12988  * @cfg {Boolean} emptyResultText only for touch device
12989  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12990  * @cfg {String} emptyTitle default ''
12991  * @constructor
12992  * Create a new ComboBox.
12993  * @param {Object} config Configuration options
12994  */
12995 Roo.bootstrap.ComboBox = function(config){
12996     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12997     this.addEvents({
12998         /**
12999          * @event expand
13000          * Fires when the dropdown list is expanded
13001         * @param {Roo.bootstrap.ComboBox} combo This combo box
13002         */
13003         'expand' : true,
13004         /**
13005          * @event collapse
13006          * Fires when the dropdown list is collapsed
13007         * @param {Roo.bootstrap.ComboBox} combo This combo box
13008         */
13009         'collapse' : true,
13010         /**
13011          * @event beforeselect
13012          * Fires before a list item is selected. Return false to cancel the selection.
13013         * @param {Roo.bootstrap.ComboBox} combo This combo box
13014         * @param {Roo.data.Record} record The data record returned from the underlying store
13015         * @param {Number} index The index of the selected item in the dropdown list
13016         */
13017         'beforeselect' : true,
13018         /**
13019          * @event select
13020          * Fires when a list item is selected
13021         * @param {Roo.bootstrap.ComboBox} combo This combo box
13022         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13023         * @param {Number} index The index of the selected item in the dropdown list
13024         */
13025         'select' : true,
13026         /**
13027          * @event beforequery
13028          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13029          * The event object passed has these properties:
13030         * @param {Roo.bootstrap.ComboBox} combo This combo box
13031         * @param {String} query The query
13032         * @param {Boolean} forceAll true to force "all" query
13033         * @param {Boolean} cancel true to cancel the query
13034         * @param {Object} e The query event object
13035         */
13036         'beforequery': true,
13037          /**
13038          * @event add
13039          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13040         * @param {Roo.bootstrap.ComboBox} combo This combo box
13041         */
13042         'add' : true,
13043         /**
13044          * @event edit
13045          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13046         * @param {Roo.bootstrap.ComboBox} combo This combo box
13047         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13048         */
13049         'edit' : true,
13050         /**
13051          * @event remove
13052          * Fires when the remove value from the combobox array
13053         * @param {Roo.bootstrap.ComboBox} combo This combo box
13054         */
13055         'remove' : true,
13056         /**
13057          * @event afterremove
13058          * Fires when the remove value from the combobox array
13059         * @param {Roo.bootstrap.ComboBox} combo This combo box
13060         */
13061         'afterremove' : true,
13062         /**
13063          * @event specialfilter
13064          * Fires when specialfilter
13065             * @param {Roo.bootstrap.ComboBox} combo This combo box
13066             */
13067         'specialfilter' : true,
13068         /**
13069          * @event tick
13070          * Fires when tick the element
13071             * @param {Roo.bootstrap.ComboBox} combo This combo box
13072             */
13073         'tick' : true,
13074         /**
13075          * @event touchviewdisplay
13076          * Fires when touch view require special display (default is using displayField)
13077             * @param {Roo.bootstrap.ComboBox} combo This combo box
13078             * @param {Object} cfg set html .
13079             */
13080         'touchviewdisplay' : true
13081         
13082     });
13083     
13084     this.item = [];
13085     this.tickItems = [];
13086     
13087     this.selectedIndex = -1;
13088     if(this.mode == 'local'){
13089         if(config.queryDelay === undefined){
13090             this.queryDelay = 10;
13091         }
13092         if(config.minChars === undefined){
13093             this.minChars = 0;
13094         }
13095     }
13096 };
13097
13098 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13099      
13100     /**
13101      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13102      * rendering into an Roo.Editor, defaults to false)
13103      */
13104     /**
13105      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13106      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13107      */
13108     /**
13109      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13110      */
13111     /**
13112      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13113      * the dropdown list (defaults to undefined, with no header element)
13114      */
13115
13116      /**
13117      * @cfg {String/Roo.Template} tpl The template to use to render the output
13118      */
13119      
13120      /**
13121      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13122      */
13123     listWidth: undefined,
13124     /**
13125      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13126      * mode = 'remote' or 'text' if mode = 'local')
13127      */
13128     displayField: undefined,
13129     
13130     /**
13131      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13132      * mode = 'remote' or 'value' if mode = 'local'). 
13133      * Note: use of a valueField requires the user make a selection
13134      * in order for a value to be mapped.
13135      */
13136     valueField: undefined,
13137     /**
13138      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13139      */
13140     modalTitle : '',
13141     
13142     /**
13143      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13144      * field's data value (defaults to the underlying DOM element's name)
13145      */
13146     hiddenName: undefined,
13147     /**
13148      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13149      */
13150     listClass: '',
13151     /**
13152      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13153      */
13154     selectedClass: 'active',
13155     
13156     /**
13157      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13158      */
13159     shadow:'sides',
13160     /**
13161      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13162      * anchor positions (defaults to 'tl-bl')
13163      */
13164     listAlign: 'tl-bl?',
13165     /**
13166      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13167      */
13168     maxHeight: 300,
13169     /**
13170      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13171      * query specified by the allQuery config option (defaults to 'query')
13172      */
13173     triggerAction: 'query',
13174     /**
13175      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13176      * (defaults to 4, does not apply if editable = false)
13177      */
13178     minChars : 4,
13179     /**
13180      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13181      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13182      */
13183     typeAhead: false,
13184     /**
13185      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13186      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13187      */
13188     queryDelay: 500,
13189     /**
13190      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13191      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13192      */
13193     pageSize: 0,
13194     /**
13195      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13196      * when editable = true (defaults to false)
13197      */
13198     selectOnFocus:false,
13199     /**
13200      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13201      */
13202     queryParam: 'query',
13203     /**
13204      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13205      * when mode = 'remote' (defaults to 'Loading...')
13206      */
13207     loadingText: 'Loading...',
13208     /**
13209      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13210      */
13211     resizable: false,
13212     /**
13213      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13214      */
13215     handleHeight : 8,
13216     /**
13217      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13218      * traditional select (defaults to true)
13219      */
13220     editable: true,
13221     /**
13222      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13223      */
13224     allQuery: '',
13225     /**
13226      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13227      */
13228     mode: 'remote',
13229     /**
13230      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13231      * listWidth has a higher value)
13232      */
13233     minListWidth : 70,
13234     /**
13235      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13236      * allow the user to set arbitrary text into the field (defaults to false)
13237      */
13238     forceSelection:false,
13239     /**
13240      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13241      * if typeAhead = true (defaults to 250)
13242      */
13243     typeAheadDelay : 250,
13244     /**
13245      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13246      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13247      */
13248     valueNotFoundText : undefined,
13249     /**
13250      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13251      */
13252     blockFocus : false,
13253     
13254     /**
13255      * @cfg {Boolean} disableClear Disable showing of clear button.
13256      */
13257     disableClear : false,
13258     /**
13259      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13260      */
13261     alwaysQuery : false,
13262     
13263     /**
13264      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13265      */
13266     multiple : false,
13267     
13268     /**
13269      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13270      */
13271     invalidClass : "has-warning",
13272     
13273     /**
13274      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13275      */
13276     validClass : "has-success",
13277     
13278     /**
13279      * @cfg {Boolean} specialFilter (true|false) special filter default false
13280      */
13281     specialFilter : false,
13282     
13283     /**
13284      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13285      */
13286     mobileTouchView : true,
13287     
13288     /**
13289      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13290      */
13291     useNativeIOS : false,
13292     
13293     /**
13294      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13295      */
13296     mobile_restrict_height : false,
13297     
13298     ios_options : false,
13299     
13300     //private
13301     addicon : false,
13302     editicon: false,
13303     
13304     page: 0,
13305     hasQuery: false,
13306     append: false,
13307     loadNext: false,
13308     autoFocus : true,
13309     tickable : false,
13310     btnPosition : 'right',
13311     triggerList : true,
13312     showToggleBtn : true,
13313     animate : true,
13314     emptyResultText: 'Empty',
13315     triggerText : 'Select',
13316     emptyTitle : '',
13317     
13318     // element that contains real text value.. (when hidden is used..)
13319     
13320     getAutoCreate : function()
13321     {   
13322         var cfg = false;
13323         //render
13324         /*
13325          * Render classic select for iso
13326          */
13327         
13328         if(Roo.isIOS && this.useNativeIOS){
13329             cfg = this.getAutoCreateNativeIOS();
13330             return cfg;
13331         }
13332         
13333         /*
13334          * Touch Devices
13335          */
13336         
13337         if(Roo.isTouch && this.mobileTouchView){
13338             cfg = this.getAutoCreateTouchView();
13339             return cfg;;
13340         }
13341         
13342         /*
13343          *  Normal ComboBox
13344          */
13345         if(!this.tickable){
13346             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13347             return cfg;
13348         }
13349         
13350         /*
13351          *  ComboBox with tickable selections
13352          */
13353              
13354         var align = this.labelAlign || this.parentLabelAlign();
13355         
13356         cfg = {
13357             cls : 'form-group roo-combobox-tickable' //input-group
13358         };
13359         
13360         var btn_text_select = '';
13361         var btn_text_done = '';
13362         var btn_text_cancel = '';
13363         
13364         if (this.btn_text_show) {
13365             btn_text_select = 'Select';
13366             btn_text_done = 'Done';
13367             btn_text_cancel = 'Cancel'; 
13368         }
13369         
13370         var buttons = {
13371             tag : 'div',
13372             cls : 'tickable-buttons',
13373             cn : [
13374                 {
13375                     tag : 'button',
13376                     type : 'button',
13377                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13378                     //html : this.triggerText
13379                     html: btn_text_select
13380                 },
13381                 {
13382                     tag : 'button',
13383                     type : 'button',
13384                     name : 'ok',
13385                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13386                     //html : 'Done'
13387                     html: btn_text_done
13388                 },
13389                 {
13390                     tag : 'button',
13391                     type : 'button',
13392                     name : 'cancel',
13393                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13394                     //html : 'Cancel'
13395                     html: btn_text_cancel
13396                 }
13397             ]
13398         };
13399         
13400         if(this.editable){
13401             buttons.cn.unshift({
13402                 tag: 'input',
13403                 cls: 'roo-select2-search-field-input'
13404             });
13405         }
13406         
13407         var _this = this;
13408         
13409         Roo.each(buttons.cn, function(c){
13410             if (_this.size) {
13411                 c.cls += ' btn-' + _this.size;
13412             }
13413
13414             if (_this.disabled) {
13415                 c.disabled = true;
13416             }
13417         });
13418         
13419         var box = {
13420             tag: 'div',
13421             style : 'display: contents',
13422             cn: [
13423                 {
13424                     tag: 'input',
13425                     type : 'hidden',
13426                     cls: 'form-hidden-field'
13427                 },
13428                 {
13429                     tag: 'ul',
13430                     cls: 'roo-select2-choices',
13431                     cn:[
13432                         {
13433                             tag: 'li',
13434                             cls: 'roo-select2-search-field',
13435                             cn: [
13436                                 buttons
13437                             ]
13438                         }
13439                     ]
13440                 }
13441             ]
13442         };
13443         
13444         var combobox = {
13445             cls: 'roo-select2-container input-group roo-select2-container-multi',
13446             cn: [
13447                 
13448                 box
13449 //                {
13450 //                    tag: 'ul',
13451 //                    cls: 'typeahead typeahead-long dropdown-menu',
13452 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13453 //                }
13454             ]
13455         };
13456         
13457         if(this.hasFeedback && !this.allowBlank){
13458             
13459             var feedback = {
13460                 tag: 'span',
13461                 cls: 'glyphicon form-control-feedback'
13462             };
13463
13464             combobox.cn.push(feedback);
13465         }
13466         
13467         var indicator = {
13468             tag : 'i',
13469             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13470             tooltip : 'This field is required'
13471         };
13472         if (Roo.bootstrap.version == 4) {
13473             indicator = {
13474                 tag : 'i',
13475                 style : 'display:none'
13476             };
13477         }
13478         if (align ==='left' && this.fieldLabel.length) {
13479             
13480             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13481             
13482             cfg.cn = [
13483                 indicator,
13484                 {
13485                     tag: 'label',
13486                     'for' :  id,
13487                     cls : 'control-label col-form-label',
13488                     html : this.fieldLabel
13489
13490                 },
13491                 {
13492                     cls : "", 
13493                     cn: [
13494                         combobox
13495                     ]
13496                 }
13497
13498             ];
13499             
13500             var labelCfg = cfg.cn[1];
13501             var contentCfg = cfg.cn[2];
13502             
13503
13504             if(this.indicatorpos == 'right'){
13505                 
13506                 cfg.cn = [
13507                     {
13508                         tag: 'label',
13509                         'for' :  id,
13510                         cls : 'control-label col-form-label',
13511                         cn : [
13512                             {
13513                                 tag : 'span',
13514                                 html : this.fieldLabel
13515                             },
13516                             indicator
13517                         ]
13518                     },
13519                     {
13520                         cls : "",
13521                         cn: [
13522                             combobox
13523                         ]
13524                     }
13525
13526                 ];
13527                 
13528                 
13529                 
13530                 labelCfg = cfg.cn[0];
13531                 contentCfg = cfg.cn[1];
13532             
13533             }
13534             
13535             if(this.labelWidth > 12){
13536                 labelCfg.style = "width: " + this.labelWidth + 'px';
13537             }
13538             
13539             if(this.labelWidth < 13 && this.labelmd == 0){
13540                 this.labelmd = this.labelWidth;
13541             }
13542             
13543             if(this.labellg > 0){
13544                 labelCfg.cls += ' col-lg-' + this.labellg;
13545                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13546             }
13547             
13548             if(this.labelmd > 0){
13549                 labelCfg.cls += ' col-md-' + this.labelmd;
13550                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13551             }
13552             
13553             if(this.labelsm > 0){
13554                 labelCfg.cls += ' col-sm-' + this.labelsm;
13555                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13556             }
13557             
13558             if(this.labelxs > 0){
13559                 labelCfg.cls += ' col-xs-' + this.labelxs;
13560                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13561             }
13562                 
13563                 
13564         } else if ( this.fieldLabel.length) {
13565 //                Roo.log(" label");
13566                  cfg.cn = [
13567                    indicator,
13568                     {
13569                         tag: 'label',
13570                         //cls : 'input-group-addon',
13571                         html : this.fieldLabel
13572                     },
13573                     combobox
13574                 ];
13575                 
13576                 if(this.indicatorpos == 'right'){
13577                     cfg.cn = [
13578                         {
13579                             tag: 'label',
13580                             //cls : 'input-group-addon',
13581                             html : this.fieldLabel
13582                         },
13583                         indicator,
13584                         combobox
13585                     ];
13586                     
13587                 }
13588
13589         } else {
13590             
13591 //                Roo.log(" no label && no align");
13592                 cfg = combobox
13593                      
13594                 
13595         }
13596          
13597         var settings=this;
13598         ['xs','sm','md','lg'].map(function(size){
13599             if (settings[size]) {
13600                 cfg.cls += ' col-' + size + '-' + settings[size];
13601             }
13602         });
13603         
13604         return cfg;
13605         
13606     },
13607     
13608     _initEventsCalled : false,
13609     
13610     // private
13611     initEvents: function()
13612     {   
13613         if (this._initEventsCalled) { // as we call render... prevent looping...
13614             return;
13615         }
13616         this._initEventsCalled = true;
13617         
13618         if (!this.store) {
13619             throw "can not find store for combo";
13620         }
13621         
13622         this.indicator = this.indicatorEl();
13623         
13624         this.store = Roo.factory(this.store, Roo.data);
13625         this.store.parent = this;
13626         
13627         // if we are building from html. then this element is so complex, that we can not really
13628         // use the rendered HTML.
13629         // so we have to trash and replace the previous code.
13630         if (Roo.XComponent.build_from_html) {
13631             // remove this element....
13632             var e = this.el.dom, k=0;
13633             while (e ) { e = e.previousSibling;  ++k;}
13634
13635             this.el.remove();
13636             
13637             this.el=false;
13638             this.rendered = false;
13639             
13640             this.render(this.parent().getChildContainer(true), k);
13641         }
13642         
13643         if(Roo.isIOS && this.useNativeIOS){
13644             this.initIOSView();
13645             return;
13646         }
13647         
13648         /*
13649          * Touch Devices
13650          */
13651         
13652         if(Roo.isTouch && this.mobileTouchView){
13653             this.initTouchView();
13654             return;
13655         }
13656         
13657         if(this.tickable){
13658             this.initTickableEvents();
13659             return;
13660         }
13661         
13662         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13663         
13664         if(this.hiddenName){
13665             
13666             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13667             
13668             this.hiddenField.dom.value =
13669                 this.hiddenValue !== undefined ? this.hiddenValue :
13670                 this.value !== undefined ? this.value : '';
13671
13672             // prevent input submission
13673             this.el.dom.removeAttribute('name');
13674             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13675              
13676              
13677         }
13678         //if(Roo.isGecko){
13679         //    this.el.dom.setAttribute('autocomplete', 'off');
13680         //}
13681         
13682         var cls = 'x-combo-list';
13683         
13684         //this.list = new Roo.Layer({
13685         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13686         //});
13687         
13688         var _this = this;
13689         
13690         (function(){
13691             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13692             _this.list.setWidth(lw);
13693         }).defer(100);
13694         
13695         this.list.on('mouseover', this.onViewOver, this);
13696         this.list.on('mousemove', this.onViewMove, this);
13697         this.list.on('scroll', this.onViewScroll, this);
13698         
13699         /*
13700         this.list.swallowEvent('mousewheel');
13701         this.assetHeight = 0;
13702
13703         if(this.title){
13704             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13705             this.assetHeight += this.header.getHeight();
13706         }
13707
13708         this.innerList = this.list.createChild({cls:cls+'-inner'});
13709         this.innerList.on('mouseover', this.onViewOver, this);
13710         this.innerList.on('mousemove', this.onViewMove, this);
13711         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13712         
13713         if(this.allowBlank && !this.pageSize && !this.disableClear){
13714             this.footer = this.list.createChild({cls:cls+'-ft'});
13715             this.pageTb = new Roo.Toolbar(this.footer);
13716            
13717         }
13718         if(this.pageSize){
13719             this.footer = this.list.createChild({cls:cls+'-ft'});
13720             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13721                     {pageSize: this.pageSize});
13722             
13723         }
13724         
13725         if (this.pageTb && this.allowBlank && !this.disableClear) {
13726             var _this = this;
13727             this.pageTb.add(new Roo.Toolbar.Fill(), {
13728                 cls: 'x-btn-icon x-btn-clear',
13729                 text: '&#160;',
13730                 handler: function()
13731                 {
13732                     _this.collapse();
13733                     _this.clearValue();
13734                     _this.onSelect(false, -1);
13735                 }
13736             });
13737         }
13738         if (this.footer) {
13739             this.assetHeight += this.footer.getHeight();
13740         }
13741         */
13742             
13743         if(!this.tpl){
13744             this.tpl = Roo.bootstrap.version == 4 ?
13745                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13746                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13747         }
13748
13749         this.view = new Roo.View(this.list, this.tpl, {
13750             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13751         });
13752         //this.view.wrapEl.setDisplayed(false);
13753         this.view.on('click', this.onViewClick, this);
13754         
13755         
13756         this.store.on('beforeload', this.onBeforeLoad, this);
13757         this.store.on('load', this.onLoad, this);
13758         this.store.on('loadexception', this.onLoadException, this);
13759         /*
13760         if(this.resizable){
13761             this.resizer = new Roo.Resizable(this.list,  {
13762                pinned:true, handles:'se'
13763             });
13764             this.resizer.on('resize', function(r, w, h){
13765                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13766                 this.listWidth = w;
13767                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13768                 this.restrictHeight();
13769             }, this);
13770             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13771         }
13772         */
13773         if(!this.editable){
13774             this.editable = true;
13775             this.setEditable(false);
13776         }
13777         
13778         /*
13779         
13780         if (typeof(this.events.add.listeners) != 'undefined') {
13781             
13782             this.addicon = this.wrap.createChild(
13783                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13784        
13785             this.addicon.on('click', function(e) {
13786                 this.fireEvent('add', this);
13787             }, this);
13788         }
13789         if (typeof(this.events.edit.listeners) != 'undefined') {
13790             
13791             this.editicon = this.wrap.createChild(
13792                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13793             if (this.addicon) {
13794                 this.editicon.setStyle('margin-left', '40px');
13795             }
13796             this.editicon.on('click', function(e) {
13797                 
13798                 // we fire even  if inothing is selected..
13799                 this.fireEvent('edit', this, this.lastData );
13800                 
13801             }, this);
13802         }
13803         */
13804         
13805         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13806             "up" : function(e){
13807                 this.inKeyMode = true;
13808                 this.selectPrev();
13809             },
13810
13811             "down" : function(e){
13812                 if(!this.isExpanded()){
13813                     this.onTriggerClick();
13814                 }else{
13815                     this.inKeyMode = true;
13816                     this.selectNext();
13817                 }
13818             },
13819
13820             "enter" : function(e){
13821 //                this.onViewClick();
13822                 //return true;
13823                 this.collapse();
13824                 
13825                 if(this.fireEvent("specialkey", this, e)){
13826                     this.onViewClick(false);
13827                 }
13828                 
13829                 return true;
13830             },
13831
13832             "esc" : function(e){
13833                 this.collapse();
13834             },
13835
13836             "tab" : function(e){
13837                 this.collapse();
13838                 
13839                 if(this.fireEvent("specialkey", this, e)){
13840                     this.onViewClick(false);
13841                 }
13842                 
13843                 return true;
13844             },
13845
13846             scope : this,
13847
13848             doRelay : function(foo, bar, hname){
13849                 if(hname == 'down' || this.scope.isExpanded()){
13850                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13851                 }
13852                 return true;
13853             },
13854
13855             forceKeyDown: true
13856         });
13857         
13858         
13859         this.queryDelay = Math.max(this.queryDelay || 10,
13860                 this.mode == 'local' ? 10 : 250);
13861         
13862         
13863         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13864         
13865         if(this.typeAhead){
13866             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13867         }
13868         if(this.editable !== false){
13869             this.inputEl().on("keyup", this.onKeyUp, this);
13870         }
13871         if(this.forceSelection){
13872             this.inputEl().on('blur', this.doForce, this);
13873         }
13874         
13875         if(this.multiple){
13876             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13877             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13878         }
13879     },
13880     
13881     initTickableEvents: function()
13882     {   
13883         this.createList();
13884         
13885         if(this.hiddenName){
13886             
13887             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13888             
13889             this.hiddenField.dom.value =
13890                 this.hiddenValue !== undefined ? this.hiddenValue :
13891                 this.value !== undefined ? this.value : '';
13892
13893             // prevent input submission
13894             this.el.dom.removeAttribute('name');
13895             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13896              
13897              
13898         }
13899         
13900 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13901         
13902         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13903         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13904         if(this.triggerList){
13905             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13906         }
13907          
13908         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13909         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13910         
13911         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13912         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13913         
13914         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13915         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13916         
13917         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13918         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13919         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13920         
13921         this.okBtn.hide();
13922         this.cancelBtn.hide();
13923         
13924         var _this = this;
13925         
13926         (function(){
13927             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13928             _this.list.setWidth(lw);
13929         }).defer(100);
13930         
13931         this.list.on('mouseover', this.onViewOver, this);
13932         this.list.on('mousemove', this.onViewMove, this);
13933         
13934         this.list.on('scroll', this.onViewScroll, this);
13935         
13936         if(!this.tpl){
13937             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13938                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13939         }
13940
13941         this.view = new Roo.View(this.list, this.tpl, {
13942             singleSelect:true,
13943             tickable:true,
13944             parent:this,
13945             store: this.store,
13946             selectedClass: this.selectedClass
13947         });
13948         
13949         //this.view.wrapEl.setDisplayed(false);
13950         this.view.on('click', this.onViewClick, this);
13951         
13952         
13953         
13954         this.store.on('beforeload', this.onBeforeLoad, this);
13955         this.store.on('load', this.onLoad, this);
13956         this.store.on('loadexception', this.onLoadException, this);
13957         
13958         if(this.editable){
13959             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13960                 "up" : function(e){
13961                     this.inKeyMode = true;
13962                     this.selectPrev();
13963                 },
13964
13965                 "down" : function(e){
13966                     this.inKeyMode = true;
13967                     this.selectNext();
13968                 },
13969
13970                 "enter" : function(e){
13971                     if(this.fireEvent("specialkey", this, e)){
13972                         this.onViewClick(false);
13973                     }
13974                     
13975                     return true;
13976                 },
13977
13978                 "esc" : function(e){
13979                     this.onTickableFooterButtonClick(e, false, false);
13980                 },
13981
13982                 "tab" : function(e){
13983                     this.fireEvent("specialkey", this, e);
13984                     
13985                     this.onTickableFooterButtonClick(e, false, false);
13986                     
13987                     return true;
13988                 },
13989
13990                 scope : this,
13991
13992                 doRelay : function(e, fn, key){
13993                     if(this.scope.isExpanded()){
13994                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13995                     }
13996                     return true;
13997                 },
13998
13999                 forceKeyDown: true
14000             });
14001         }
14002         
14003         this.queryDelay = Math.max(this.queryDelay || 10,
14004                 this.mode == 'local' ? 10 : 250);
14005         
14006         
14007         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14008         
14009         if(this.typeAhead){
14010             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14011         }
14012         
14013         if(this.editable !== false){
14014             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14015         }
14016         
14017         this.indicator = this.indicatorEl();
14018         
14019         if(this.indicator){
14020             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14021             this.indicator.hide();
14022         }
14023         
14024     },
14025
14026     onDestroy : function(){
14027         if(this.view){
14028             this.view.setStore(null);
14029             this.view.el.removeAllListeners();
14030             this.view.el.remove();
14031             this.view.purgeListeners();
14032         }
14033         if(this.list){
14034             this.list.dom.innerHTML  = '';
14035         }
14036         
14037         if(this.store){
14038             this.store.un('beforeload', this.onBeforeLoad, this);
14039             this.store.un('load', this.onLoad, this);
14040             this.store.un('loadexception', this.onLoadException, this);
14041         }
14042         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14043     },
14044
14045     // private
14046     fireKey : function(e){
14047         if(e.isNavKeyPress() && !this.list.isVisible()){
14048             this.fireEvent("specialkey", this, e);
14049         }
14050     },
14051
14052     // private
14053     onResize: function(w, h){
14054 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14055 //        
14056 //        if(typeof w != 'number'){
14057 //            // we do not handle it!?!?
14058 //            return;
14059 //        }
14060 //        var tw = this.trigger.getWidth();
14061 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14062 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14063 //        var x = w - tw;
14064 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14065 //            
14066 //        //this.trigger.setStyle('left', x+'px');
14067 //        
14068 //        if(this.list && this.listWidth === undefined){
14069 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14070 //            this.list.setWidth(lw);
14071 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14072 //        }
14073         
14074     
14075         
14076     },
14077
14078     /**
14079      * Allow or prevent the user from directly editing the field text.  If false is passed,
14080      * the user will only be able to select from the items defined in the dropdown list.  This method
14081      * is the runtime equivalent of setting the 'editable' config option at config time.
14082      * @param {Boolean} value True to allow the user to directly edit the field text
14083      */
14084     setEditable : function(value){
14085         if(value == this.editable){
14086             return;
14087         }
14088         this.editable = value;
14089         if(!value){
14090             this.inputEl().dom.setAttribute('readOnly', true);
14091             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14092             this.inputEl().addClass('x-combo-noedit');
14093         }else{
14094             this.inputEl().dom.setAttribute('readOnly', false);
14095             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14096             this.inputEl().removeClass('x-combo-noedit');
14097         }
14098     },
14099
14100     // private
14101     
14102     onBeforeLoad : function(combo,opts){
14103         if(!this.hasFocus){
14104             return;
14105         }
14106          if (!opts.add) {
14107             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14108          }
14109         this.restrictHeight();
14110         this.selectedIndex = -1;
14111     },
14112
14113     // private
14114     onLoad : function(){
14115         
14116         this.hasQuery = false;
14117         
14118         if(!this.hasFocus){
14119             return;
14120         }
14121         
14122         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14123             this.loading.hide();
14124         }
14125         
14126         if(this.store.getCount() > 0){
14127             
14128             this.expand();
14129             this.restrictHeight();
14130             if(this.lastQuery == this.allQuery){
14131                 if(this.editable && !this.tickable){
14132                     this.inputEl().dom.select();
14133                 }
14134                 
14135                 if(
14136                     !this.selectByValue(this.value, true) &&
14137                     this.autoFocus && 
14138                     (
14139                         !this.store.lastOptions ||
14140                         typeof(this.store.lastOptions.add) == 'undefined' || 
14141                         this.store.lastOptions.add != true
14142                     )
14143                 ){
14144                     this.select(0, true);
14145                 }
14146             }else{
14147                 if(this.autoFocus){
14148                     this.selectNext();
14149                 }
14150                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14151                     this.taTask.delay(this.typeAheadDelay);
14152                 }
14153             }
14154         }else{
14155             this.onEmptyResults();
14156         }
14157         
14158         //this.el.focus();
14159     },
14160     // private
14161     onLoadException : function()
14162     {
14163         this.hasQuery = false;
14164         
14165         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14166             this.loading.hide();
14167         }
14168         
14169         if(this.tickable && this.editable){
14170             return;
14171         }
14172         
14173         this.collapse();
14174         // only causes errors at present
14175         //Roo.log(this.store.reader.jsonData);
14176         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14177             // fixme
14178             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14179         //}
14180         
14181         
14182     },
14183     // private
14184     onTypeAhead : function(){
14185         if(this.store.getCount() > 0){
14186             var r = this.store.getAt(0);
14187             var newValue = r.data[this.displayField];
14188             var len = newValue.length;
14189             var selStart = this.getRawValue().length;
14190             
14191             if(selStart != len){
14192                 this.setRawValue(newValue);
14193                 this.selectText(selStart, newValue.length);
14194             }
14195         }
14196     },
14197
14198     // private
14199     onSelect : function(record, index){
14200         
14201         if(this.fireEvent('beforeselect', this, record, index) !== false){
14202         
14203             this.setFromData(index > -1 ? record.data : false);
14204             
14205             this.collapse();
14206             this.fireEvent('select', this, record, index);
14207         }
14208     },
14209
14210     /**
14211      * Returns the currently selected field value or empty string if no value is set.
14212      * @return {String} value The selected value
14213      */
14214     getValue : function()
14215     {
14216         if(Roo.isIOS && this.useNativeIOS){
14217             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14218         }
14219         
14220         if(this.multiple){
14221             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14222         }
14223         
14224         if(this.valueField){
14225             return typeof this.value != 'undefined' ? this.value : '';
14226         }else{
14227             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14228         }
14229     },
14230     
14231     getRawValue : function()
14232     {
14233         if(Roo.isIOS && this.useNativeIOS){
14234             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14235         }
14236         
14237         var v = this.inputEl().getValue();
14238         
14239         return v;
14240     },
14241
14242     /**
14243      * Clears any text/value currently set in the field
14244      */
14245     clearValue : function(){
14246         
14247         if(this.hiddenField){
14248             this.hiddenField.dom.value = '';
14249         }
14250         this.value = '';
14251         this.setRawValue('');
14252         this.lastSelectionText = '';
14253         this.lastData = false;
14254         
14255         var close = this.closeTriggerEl();
14256         
14257         if(close){
14258             close.hide();
14259         }
14260         
14261         this.validate();
14262         
14263     },
14264
14265     /**
14266      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14267      * will be displayed in the field.  If the value does not match the data value of an existing item,
14268      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14269      * Otherwise the field will be blank (although the value will still be set).
14270      * @param {String} value The value to match
14271      */
14272     setValue : function(v)
14273     {
14274         if(Roo.isIOS && this.useNativeIOS){
14275             this.setIOSValue(v);
14276             return;
14277         }
14278         
14279         if(this.multiple){
14280             this.syncValue();
14281             return;
14282         }
14283         
14284         var text = v;
14285         if(this.valueField){
14286             var r = this.findRecord(this.valueField, v);
14287             if(r){
14288                 text = r.data[this.displayField];
14289             }else if(this.valueNotFoundText !== undefined){
14290                 text = this.valueNotFoundText;
14291             }
14292         }
14293         this.lastSelectionText = text;
14294         if(this.hiddenField){
14295             this.hiddenField.dom.value = v;
14296         }
14297         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14298         this.value = v;
14299         
14300         var close = this.closeTriggerEl();
14301         
14302         if(close){
14303             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14304         }
14305         
14306         this.validate();
14307     },
14308     /**
14309      * @property {Object} the last set data for the element
14310      */
14311     
14312     lastData : false,
14313     /**
14314      * Sets the value of the field based on a object which is related to the record format for the store.
14315      * @param {Object} value the value to set as. or false on reset?
14316      */
14317     setFromData : function(o){
14318         
14319         if(this.multiple){
14320             this.addItem(o);
14321             return;
14322         }
14323             
14324         var dv = ''; // display value
14325         var vv = ''; // value value..
14326         this.lastData = o;
14327         if (this.displayField) {
14328             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14329         } else {
14330             // this is an error condition!!!
14331             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14332         }
14333         
14334         if(this.valueField){
14335             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14336         }
14337         
14338         var close = this.closeTriggerEl();
14339         
14340         if(close){
14341             if(dv.length || vv * 1 > 0){
14342                 close.show() ;
14343                 this.blockFocus=true;
14344             } else {
14345                 close.hide();
14346             }             
14347         }
14348         
14349         if(this.hiddenField){
14350             this.hiddenField.dom.value = vv;
14351             
14352             this.lastSelectionText = dv;
14353             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14354             this.value = vv;
14355             return;
14356         }
14357         // no hidden field.. - we store the value in 'value', but still display
14358         // display field!!!!
14359         this.lastSelectionText = dv;
14360         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14361         this.value = vv;
14362         
14363         
14364         
14365     },
14366     // private
14367     reset : function(){
14368         // overridden so that last data is reset..
14369         
14370         if(this.multiple){
14371             this.clearItem();
14372             return;
14373         }
14374         
14375         this.setValue(this.originalValue);
14376         //this.clearInvalid();
14377         this.lastData = false;
14378         if (this.view) {
14379             this.view.clearSelections();
14380         }
14381         
14382         this.validate();
14383     },
14384     // private
14385     findRecord : function(prop, value){
14386         var record;
14387         if(this.store.getCount() > 0){
14388             this.store.each(function(r){
14389                 if(r.data[prop] == value){
14390                     record = r;
14391                     return false;
14392                 }
14393                 return true;
14394             });
14395         }
14396         return record;
14397     },
14398     
14399     getName: function()
14400     {
14401         // returns hidden if it's set..
14402         if (!this.rendered) {return ''};
14403         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14404         
14405     },
14406     // private
14407     onViewMove : function(e, t){
14408         this.inKeyMode = false;
14409     },
14410
14411     // private
14412     onViewOver : function(e, t){
14413         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14414             return;
14415         }
14416         var item = this.view.findItemFromChild(t);
14417         
14418         if(item){
14419             var index = this.view.indexOf(item);
14420             this.select(index, false);
14421         }
14422     },
14423
14424     // private
14425     onViewClick : function(view, doFocus, el, e)
14426     {
14427         var index = this.view.getSelectedIndexes()[0];
14428         
14429         var r = this.store.getAt(index);
14430         
14431         if(this.tickable){
14432             
14433             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14434                 return;
14435             }
14436             
14437             var rm = false;
14438             var _this = this;
14439             
14440             Roo.each(this.tickItems, function(v,k){
14441                 
14442                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14443                     Roo.log(v);
14444                     _this.tickItems.splice(k, 1);
14445                     
14446                     if(typeof(e) == 'undefined' && view == false){
14447                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14448                     }
14449                     
14450                     rm = true;
14451                     return;
14452                 }
14453             });
14454             
14455             if(rm){
14456                 return;
14457             }
14458             
14459             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14460                 this.tickItems.push(r.data);
14461             }
14462             
14463             if(typeof(e) == 'undefined' && view == false){
14464                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14465             }
14466                     
14467             return;
14468         }
14469         
14470         if(r){
14471             this.onSelect(r, index);
14472         }
14473         if(doFocus !== false && !this.blockFocus){
14474             this.inputEl().focus();
14475         }
14476     },
14477
14478     // private
14479     restrictHeight : function(){
14480         //this.innerList.dom.style.height = '';
14481         //var inner = this.innerList.dom;
14482         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14483         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14484         //this.list.beginUpdate();
14485         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14486         this.list.alignTo(this.inputEl(), this.listAlign);
14487         this.list.alignTo(this.inputEl(), this.listAlign);
14488         //this.list.endUpdate();
14489     },
14490
14491     // private
14492     onEmptyResults : function(){
14493         
14494         if(this.tickable && this.editable){
14495             this.hasFocus = false;
14496             this.restrictHeight();
14497             return;
14498         }
14499         
14500         this.collapse();
14501     },
14502
14503     /**
14504      * Returns true if the dropdown list is expanded, else false.
14505      */
14506     isExpanded : function(){
14507         return this.list.isVisible();
14508     },
14509
14510     /**
14511      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14512      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14513      * @param {String} value The data value of the item to select
14514      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14515      * selected item if it is not currently in view (defaults to true)
14516      * @return {Boolean} True if the value matched an item in the list, else false
14517      */
14518     selectByValue : function(v, scrollIntoView){
14519         if(v !== undefined && v !== null){
14520             var r = this.findRecord(this.valueField || this.displayField, v);
14521             if(r){
14522                 this.select(this.store.indexOf(r), scrollIntoView);
14523                 return true;
14524             }
14525         }
14526         return false;
14527     },
14528
14529     /**
14530      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14531      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14532      * @param {Number} index The zero-based index of the list item to select
14533      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14534      * selected item if it is not currently in view (defaults to true)
14535      */
14536     select : function(index, scrollIntoView){
14537         this.selectedIndex = index;
14538         this.view.select(index);
14539         if(scrollIntoView !== false){
14540             var el = this.view.getNode(index);
14541             /*
14542              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14543              */
14544             if(el){
14545                 this.list.scrollChildIntoView(el, false);
14546             }
14547         }
14548     },
14549
14550     // private
14551     selectNext : function(){
14552         var ct = this.store.getCount();
14553         if(ct > 0){
14554             if(this.selectedIndex == -1){
14555                 this.select(0);
14556             }else if(this.selectedIndex < ct-1){
14557                 this.select(this.selectedIndex+1);
14558             }
14559         }
14560     },
14561
14562     // private
14563     selectPrev : function(){
14564         var ct = this.store.getCount();
14565         if(ct > 0){
14566             if(this.selectedIndex == -1){
14567                 this.select(0);
14568             }else if(this.selectedIndex != 0){
14569                 this.select(this.selectedIndex-1);
14570             }
14571         }
14572     },
14573
14574     // private
14575     onKeyUp : function(e){
14576         if(this.editable !== false && !e.isSpecialKey()){
14577             this.lastKey = e.getKey();
14578             this.dqTask.delay(this.queryDelay);
14579         }
14580     },
14581
14582     // private
14583     validateBlur : function(){
14584         return !this.list || !this.list.isVisible();   
14585     },
14586
14587     // private
14588     initQuery : function(){
14589         
14590         var v = this.getRawValue();
14591         
14592         if(this.tickable && this.editable){
14593             v = this.tickableInputEl().getValue();
14594         }
14595         
14596         this.doQuery(v);
14597     },
14598
14599     // private
14600     doForce : function(){
14601         if(this.inputEl().dom.value.length > 0){
14602             this.inputEl().dom.value =
14603                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14604              
14605         }
14606     },
14607
14608     /**
14609      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14610      * query allowing the query action to be canceled if needed.
14611      * @param {String} query The SQL query to execute
14612      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14613      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14614      * saved in the current store (defaults to false)
14615      */
14616     doQuery : function(q, forceAll){
14617         
14618         if(q === undefined || q === null){
14619             q = '';
14620         }
14621         var qe = {
14622             query: q,
14623             forceAll: forceAll,
14624             combo: this,
14625             cancel:false
14626         };
14627         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14628             return false;
14629         }
14630         q = qe.query;
14631         
14632         forceAll = qe.forceAll;
14633         if(forceAll === true || (q.length >= this.minChars)){
14634             
14635             this.hasQuery = true;
14636             
14637             if(this.lastQuery != q || this.alwaysQuery){
14638                 this.lastQuery = q;
14639                 if(this.mode == 'local'){
14640                     this.selectedIndex = -1;
14641                     if(forceAll){
14642                         this.store.clearFilter();
14643                     }else{
14644                         
14645                         if(this.specialFilter){
14646                             this.fireEvent('specialfilter', this);
14647                             this.onLoad();
14648                             return;
14649                         }
14650                         
14651                         this.store.filter(this.displayField, q);
14652                     }
14653                     
14654                     this.store.fireEvent("datachanged", this.store);
14655                     
14656                     this.onLoad();
14657                     
14658                     
14659                 }else{
14660                     
14661                     this.store.baseParams[this.queryParam] = q;
14662                     
14663                     var options = {params : this.getParams(q)};
14664                     
14665                     if(this.loadNext){
14666                         options.add = true;
14667                         options.params.start = this.page * this.pageSize;
14668                     }
14669                     
14670                     this.store.load(options);
14671                     
14672                     /*
14673                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14674                      *  we should expand the list on onLoad
14675                      *  so command out it
14676                      */
14677 //                    this.expand();
14678                 }
14679             }else{
14680                 this.selectedIndex = -1;
14681                 this.onLoad();   
14682             }
14683         }
14684         
14685         this.loadNext = false;
14686     },
14687     
14688     // private
14689     getParams : function(q){
14690         var p = {};
14691         //p[this.queryParam] = q;
14692         
14693         if(this.pageSize){
14694             p.start = 0;
14695             p.limit = this.pageSize;
14696         }
14697         return p;
14698     },
14699
14700     /**
14701      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14702      */
14703     collapse : function(){
14704         if(!this.isExpanded()){
14705             return;
14706         }
14707         
14708         this.list.hide();
14709         
14710         this.hasFocus = false;
14711         
14712         if(this.tickable){
14713             this.okBtn.hide();
14714             this.cancelBtn.hide();
14715             this.trigger.show();
14716             
14717             if(this.editable){
14718                 this.tickableInputEl().dom.value = '';
14719                 this.tickableInputEl().blur();
14720             }
14721             
14722         }
14723         
14724         Roo.get(document).un('mousedown', this.collapseIf, this);
14725         Roo.get(document).un('mousewheel', this.collapseIf, this);
14726         if (!this.editable) {
14727             Roo.get(document).un('keydown', this.listKeyPress, this);
14728         }
14729         this.fireEvent('collapse', this);
14730         
14731         this.validate();
14732     },
14733
14734     // private
14735     collapseIf : function(e){
14736         var in_combo  = e.within(this.el);
14737         var in_list =  e.within(this.list);
14738         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14739         
14740         if (in_combo || in_list || is_list) {
14741             //e.stopPropagation();
14742             return;
14743         }
14744         
14745         if(this.tickable){
14746             this.onTickableFooterButtonClick(e, false, false);
14747         }
14748
14749         this.collapse();
14750         
14751     },
14752
14753     /**
14754      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14755      */
14756     expand : function(){
14757        
14758         if(this.isExpanded() || !this.hasFocus){
14759             return;
14760         }
14761         
14762         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14763         this.list.setWidth(lw);
14764         
14765         Roo.log('expand');
14766         
14767         this.list.show();
14768         
14769         this.restrictHeight();
14770         
14771         if(this.tickable){
14772             
14773             this.tickItems = Roo.apply([], this.item);
14774             
14775             this.okBtn.show();
14776             this.cancelBtn.show();
14777             this.trigger.hide();
14778             
14779             if(this.editable){
14780                 this.tickableInputEl().focus();
14781             }
14782             
14783         }
14784         
14785         Roo.get(document).on('mousedown', this.collapseIf, this);
14786         Roo.get(document).on('mousewheel', this.collapseIf, this);
14787         if (!this.editable) {
14788             Roo.get(document).on('keydown', this.listKeyPress, this);
14789         }
14790         
14791         this.fireEvent('expand', this);
14792     },
14793
14794     // private
14795     // Implements the default empty TriggerField.onTriggerClick function
14796     onTriggerClick : function(e)
14797     {
14798         Roo.log('trigger click');
14799         
14800         if(this.disabled || !this.triggerList){
14801             return;
14802         }
14803         
14804         this.page = 0;
14805         this.loadNext = false;
14806         
14807         if(this.isExpanded()){
14808             this.collapse();
14809             if (!this.blockFocus) {
14810                 this.inputEl().focus();
14811             }
14812             
14813         }else {
14814             this.hasFocus = true;
14815             if(this.triggerAction == 'all') {
14816                 this.doQuery(this.allQuery, true);
14817             } else {
14818                 this.doQuery(this.getRawValue());
14819             }
14820             if (!this.blockFocus) {
14821                 this.inputEl().focus();
14822             }
14823         }
14824     },
14825     
14826     onTickableTriggerClick : function(e)
14827     {
14828         if(this.disabled){
14829             return;
14830         }
14831         
14832         this.page = 0;
14833         this.loadNext = false;
14834         this.hasFocus = true;
14835         
14836         if(this.triggerAction == 'all') {
14837             this.doQuery(this.allQuery, true);
14838         } else {
14839             this.doQuery(this.getRawValue());
14840         }
14841     },
14842     
14843     onSearchFieldClick : function(e)
14844     {
14845         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14846             this.onTickableFooterButtonClick(e, false, false);
14847             return;
14848         }
14849         
14850         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14851             return;
14852         }
14853         
14854         this.page = 0;
14855         this.loadNext = false;
14856         this.hasFocus = true;
14857         
14858         if(this.triggerAction == 'all') {
14859             this.doQuery(this.allQuery, true);
14860         } else {
14861             this.doQuery(this.getRawValue());
14862         }
14863     },
14864     
14865     listKeyPress : function(e)
14866     {
14867         //Roo.log('listkeypress');
14868         // scroll to first matching element based on key pres..
14869         if (e.isSpecialKey()) {
14870             return false;
14871         }
14872         var k = String.fromCharCode(e.getKey()).toUpperCase();
14873         //Roo.log(k);
14874         var match  = false;
14875         var csel = this.view.getSelectedNodes();
14876         var cselitem = false;
14877         if (csel.length) {
14878             var ix = this.view.indexOf(csel[0]);
14879             cselitem  = this.store.getAt(ix);
14880             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14881                 cselitem = false;
14882             }
14883             
14884         }
14885         
14886         this.store.each(function(v) { 
14887             if (cselitem) {
14888                 // start at existing selection.
14889                 if (cselitem.id == v.id) {
14890                     cselitem = false;
14891                 }
14892                 return true;
14893             }
14894                 
14895             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14896                 match = this.store.indexOf(v);
14897                 return false;
14898             }
14899             return true;
14900         }, this);
14901         
14902         if (match === false) {
14903             return true; // no more action?
14904         }
14905         // scroll to?
14906         this.view.select(match);
14907         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14908         sn.scrollIntoView(sn.dom.parentNode, false);
14909     },
14910     
14911     onViewScroll : function(e, t){
14912         
14913         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){
14914             return;
14915         }
14916         
14917         this.hasQuery = true;
14918         
14919         this.loading = this.list.select('.loading', true).first();
14920         
14921         if(this.loading === null){
14922             this.list.createChild({
14923                 tag: 'div',
14924                 cls: 'loading roo-select2-more-results roo-select2-active',
14925                 html: 'Loading more results...'
14926             });
14927             
14928             this.loading = this.list.select('.loading', true).first();
14929             
14930             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14931             
14932             this.loading.hide();
14933         }
14934         
14935         this.loading.show();
14936         
14937         var _combo = this;
14938         
14939         this.page++;
14940         this.loadNext = true;
14941         
14942         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14943         
14944         return;
14945     },
14946     
14947     addItem : function(o)
14948     {   
14949         var dv = ''; // display value
14950         
14951         if (this.displayField) {
14952             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14953         } else {
14954             // this is an error condition!!!
14955             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14956         }
14957         
14958         if(!dv.length){
14959             return;
14960         }
14961         
14962         var choice = this.choices.createChild({
14963             tag: 'li',
14964             cls: 'roo-select2-search-choice',
14965             cn: [
14966                 {
14967                     tag: 'div',
14968                     html: dv
14969                 },
14970                 {
14971                     tag: 'a',
14972                     href: '#',
14973                     cls: 'roo-select2-search-choice-close fa fa-times',
14974                     tabindex: '-1'
14975                 }
14976             ]
14977             
14978         }, this.searchField);
14979         
14980         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14981         
14982         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14983         
14984         this.item.push(o);
14985         
14986         this.lastData = o;
14987         
14988         this.syncValue();
14989         
14990         this.inputEl().dom.value = '';
14991         
14992         this.validate();
14993     },
14994     
14995     onRemoveItem : function(e, _self, o)
14996     {
14997         e.preventDefault();
14998         
14999         this.lastItem = Roo.apply([], this.item);
15000         
15001         var index = this.item.indexOf(o.data) * 1;
15002         
15003         if( index < 0){
15004             Roo.log('not this item?!');
15005             return;
15006         }
15007         
15008         this.item.splice(index, 1);
15009         o.item.remove();
15010         
15011         this.syncValue();
15012         
15013         this.fireEvent('remove', this, e);
15014         
15015         this.validate();
15016         
15017     },
15018     
15019     syncValue : function()
15020     {
15021         if(!this.item.length){
15022             this.clearValue();
15023             return;
15024         }
15025             
15026         var value = [];
15027         var _this = this;
15028         Roo.each(this.item, function(i){
15029             if(_this.valueField){
15030                 value.push(i[_this.valueField]);
15031                 return;
15032             }
15033
15034             value.push(i);
15035         });
15036
15037         this.value = value.join(',');
15038
15039         if(this.hiddenField){
15040             this.hiddenField.dom.value = this.value;
15041         }
15042         
15043         this.store.fireEvent("datachanged", this.store);
15044         
15045         this.validate();
15046     },
15047     
15048     clearItem : function()
15049     {
15050         if(!this.multiple){
15051             return;
15052         }
15053         
15054         this.item = [];
15055         
15056         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15057            c.remove();
15058         });
15059         
15060         this.syncValue();
15061         
15062         this.validate();
15063         
15064         if(this.tickable && !Roo.isTouch){
15065             this.view.refresh();
15066         }
15067     },
15068     
15069     inputEl: function ()
15070     {
15071         if(Roo.isIOS && this.useNativeIOS){
15072             return this.el.select('select.roo-ios-select', true).first();
15073         }
15074         
15075         if(Roo.isTouch && this.mobileTouchView){
15076             return this.el.select('input.form-control',true).first();
15077         }
15078         
15079         if(this.tickable){
15080             return this.searchField;
15081         }
15082         
15083         return this.el.select('input.form-control',true).first();
15084     },
15085     
15086     onTickableFooterButtonClick : function(e, btn, el)
15087     {
15088         e.preventDefault();
15089         
15090         this.lastItem = Roo.apply([], this.item);
15091         
15092         if(btn && btn.name == 'cancel'){
15093             this.tickItems = Roo.apply([], this.item);
15094             this.collapse();
15095             return;
15096         }
15097         
15098         this.clearItem();
15099         
15100         var _this = this;
15101         
15102         Roo.each(this.tickItems, function(o){
15103             _this.addItem(o);
15104         });
15105         
15106         this.collapse();
15107         
15108     },
15109     
15110     validate : function()
15111     {
15112         if(this.getVisibilityEl().hasClass('hidden')){
15113             return true;
15114         }
15115         
15116         var v = this.getRawValue();
15117         
15118         if(this.multiple){
15119             v = this.getValue();
15120         }
15121         
15122         if(this.disabled || this.allowBlank || v.length){
15123             this.markValid();
15124             return true;
15125         }
15126         
15127         this.markInvalid();
15128         return false;
15129     },
15130     
15131     tickableInputEl : function()
15132     {
15133         if(!this.tickable || !this.editable){
15134             return this.inputEl();
15135         }
15136         
15137         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15138     },
15139     
15140     
15141     getAutoCreateTouchView : function()
15142     {
15143         var id = Roo.id();
15144         
15145         var cfg = {
15146             cls: 'form-group' //input-group
15147         };
15148         
15149         var input =  {
15150             tag: 'input',
15151             id : id,
15152             type : this.inputType,
15153             cls : 'form-control x-combo-noedit',
15154             autocomplete: 'new-password',
15155             placeholder : this.placeholder || '',
15156             readonly : true
15157         };
15158         
15159         if (this.name) {
15160             input.name = this.name;
15161         }
15162         
15163         if (this.size) {
15164             input.cls += ' input-' + this.size;
15165         }
15166         
15167         if (this.disabled) {
15168             input.disabled = true;
15169         }
15170         
15171         var inputblock = {
15172             cls : '',
15173             cn : [
15174                 input
15175             ]
15176         };
15177         
15178         if(this.before){
15179             inputblock.cls += ' input-group';
15180             
15181             inputblock.cn.unshift({
15182                 tag :'span',
15183                 cls : 'input-group-addon input-group-prepend input-group-text',
15184                 html : this.before
15185             });
15186         }
15187         
15188         if(this.removable && !this.multiple){
15189             inputblock.cls += ' roo-removable';
15190             
15191             inputblock.cn.push({
15192                 tag: 'button',
15193                 html : 'x',
15194                 cls : 'roo-combo-removable-btn close'
15195             });
15196         }
15197
15198         if(this.hasFeedback && !this.allowBlank){
15199             
15200             inputblock.cls += ' has-feedback';
15201             
15202             inputblock.cn.push({
15203                 tag: 'span',
15204                 cls: 'glyphicon form-control-feedback'
15205             });
15206             
15207         }
15208         
15209         if (this.after) {
15210             
15211             inputblock.cls += (this.before) ? '' : ' input-group';
15212             
15213             inputblock.cn.push({
15214                 tag :'span',
15215                 cls : 'input-group-addon input-group-append input-group-text',
15216                 html : this.after
15217             });
15218         }
15219
15220         
15221         var ibwrap = inputblock;
15222         
15223         if(this.multiple){
15224             ibwrap = {
15225                 tag: 'ul',
15226                 cls: 'roo-select2-choices',
15227                 cn:[
15228                     {
15229                         tag: 'li',
15230                         cls: 'roo-select2-search-field',
15231                         cn: [
15232
15233                             inputblock
15234                         ]
15235                     }
15236                 ]
15237             };
15238         
15239             
15240         }
15241         
15242         var combobox = {
15243             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15244             cn: [
15245                 {
15246                     tag: 'input',
15247                     type : 'hidden',
15248                     cls: 'form-hidden-field'
15249                 },
15250                 ibwrap
15251             ]
15252         };
15253         
15254         if(!this.multiple && this.showToggleBtn){
15255             
15256             var caret = {
15257                         tag: 'span',
15258                         cls: 'caret'
15259             };
15260             
15261             if (this.caret != false) {
15262                 caret = {
15263                      tag: 'i',
15264                      cls: 'fa fa-' + this.caret
15265                 };
15266                 
15267             }
15268             
15269             combobox.cn.push({
15270                 tag :'span',
15271                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15272                 cn : [
15273                     caret,
15274                     {
15275                         tag: 'span',
15276                         cls: 'combobox-clear',
15277                         cn  : [
15278                             {
15279                                 tag : 'i',
15280                                 cls: 'icon-remove'
15281                             }
15282                         ]
15283                     }
15284                 ]
15285
15286             })
15287         }
15288         
15289         if(this.multiple){
15290             combobox.cls += ' roo-select2-container-multi';
15291         }
15292         
15293         var align = this.labelAlign || this.parentLabelAlign();
15294         
15295         if (align ==='left' && this.fieldLabel.length) {
15296
15297             cfg.cn = [
15298                 {
15299                    tag : 'i',
15300                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15301                    tooltip : 'This field is required'
15302                 },
15303                 {
15304                     tag: 'label',
15305                     cls : 'control-label col-form-label',
15306                     html : this.fieldLabel
15307
15308                 },
15309                 {
15310                     cls : '', 
15311                     cn: [
15312                         combobox
15313                     ]
15314                 }
15315             ];
15316             
15317             var labelCfg = cfg.cn[1];
15318             var contentCfg = cfg.cn[2];
15319             
15320
15321             if(this.indicatorpos == 'right'){
15322                 cfg.cn = [
15323                     {
15324                         tag: 'label',
15325                         'for' :  id,
15326                         cls : 'control-label col-form-label',
15327                         cn : [
15328                             {
15329                                 tag : 'span',
15330                                 html : this.fieldLabel
15331                             },
15332                             {
15333                                 tag : 'i',
15334                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15335                                 tooltip : 'This field is required'
15336                             }
15337                         ]
15338                     },
15339                     {
15340                         cls : "",
15341                         cn: [
15342                             combobox
15343                         ]
15344                     }
15345
15346                 ];
15347                 
15348                 labelCfg = cfg.cn[0];
15349                 contentCfg = cfg.cn[1];
15350             }
15351             
15352            
15353             
15354             if(this.labelWidth > 12){
15355                 labelCfg.style = "width: " + this.labelWidth + 'px';
15356             }
15357             
15358             if(this.labelWidth < 13 && this.labelmd == 0){
15359                 this.labelmd = this.labelWidth;
15360             }
15361             
15362             if(this.labellg > 0){
15363                 labelCfg.cls += ' col-lg-' + this.labellg;
15364                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15365             }
15366             
15367             if(this.labelmd > 0){
15368                 labelCfg.cls += ' col-md-' + this.labelmd;
15369                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15370             }
15371             
15372             if(this.labelsm > 0){
15373                 labelCfg.cls += ' col-sm-' + this.labelsm;
15374                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15375             }
15376             
15377             if(this.labelxs > 0){
15378                 labelCfg.cls += ' col-xs-' + this.labelxs;
15379                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15380             }
15381                 
15382                 
15383         } else if ( this.fieldLabel.length) {
15384             cfg.cn = [
15385                 {
15386                    tag : 'i',
15387                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15388                    tooltip : 'This field is required'
15389                 },
15390                 {
15391                     tag: 'label',
15392                     cls : 'control-label',
15393                     html : this.fieldLabel
15394
15395                 },
15396                 {
15397                     cls : '', 
15398                     cn: [
15399                         combobox
15400                     ]
15401                 }
15402             ];
15403             
15404             if(this.indicatorpos == 'right'){
15405                 cfg.cn = [
15406                     {
15407                         tag: 'label',
15408                         cls : 'control-label',
15409                         html : this.fieldLabel,
15410                         cn : [
15411                             {
15412                                tag : 'i',
15413                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15414                                tooltip : 'This field is required'
15415                             }
15416                         ]
15417                     },
15418                     {
15419                         cls : '', 
15420                         cn: [
15421                             combobox
15422                         ]
15423                     }
15424                 ];
15425             }
15426         } else {
15427             cfg.cn = combobox;    
15428         }
15429         
15430         
15431         var settings = this;
15432         
15433         ['xs','sm','md','lg'].map(function(size){
15434             if (settings[size]) {
15435                 cfg.cls += ' col-' + size + '-' + settings[size];
15436             }
15437         });
15438         
15439         return cfg;
15440     },
15441     
15442     initTouchView : function()
15443     {
15444         this.renderTouchView();
15445         
15446         this.touchViewEl.on('scroll', function(){
15447             this.el.dom.scrollTop = 0;
15448         }, this);
15449         
15450         this.originalValue = this.getValue();
15451         
15452         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15453         
15454         this.inputEl().on("click", this.showTouchView, this);
15455         if (this.triggerEl) {
15456             this.triggerEl.on("click", this.showTouchView, this);
15457         }
15458         
15459         
15460         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15461         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15462         
15463         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15464         
15465         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15466         this.store.on('load', this.onTouchViewLoad, this);
15467         this.store.on('loadexception', this.onTouchViewLoadException, this);
15468         
15469         if(this.hiddenName){
15470             
15471             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15472             
15473             this.hiddenField.dom.value =
15474                 this.hiddenValue !== undefined ? this.hiddenValue :
15475                 this.value !== undefined ? this.value : '';
15476         
15477             this.el.dom.removeAttribute('name');
15478             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15479         }
15480         
15481         if(this.multiple){
15482             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15483             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15484         }
15485         
15486         if(this.removable && !this.multiple){
15487             var close = this.closeTriggerEl();
15488             if(close){
15489                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15490                 close.on('click', this.removeBtnClick, this, close);
15491             }
15492         }
15493         /*
15494          * fix the bug in Safari iOS8
15495          */
15496         this.inputEl().on("focus", function(e){
15497             document.activeElement.blur();
15498         }, this);
15499         
15500         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15501         
15502         return;
15503         
15504         
15505     },
15506     
15507     renderTouchView : function()
15508     {
15509         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15510         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15511         
15512         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15513         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15514         
15515         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15516         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15517         this.touchViewBodyEl.setStyle('overflow', 'auto');
15518         
15519         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15520         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15521         
15522         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15523         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15524         
15525     },
15526     
15527     showTouchView : function()
15528     {
15529         if(this.disabled){
15530             return;
15531         }
15532         
15533         this.touchViewHeaderEl.hide();
15534
15535         if(this.modalTitle.length){
15536             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15537             this.touchViewHeaderEl.show();
15538         }
15539
15540         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15541         this.touchViewEl.show();
15542
15543         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15544         
15545         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15546         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15547
15548         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15549
15550         if(this.modalTitle.length){
15551             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15552         }
15553         
15554         this.touchViewBodyEl.setHeight(bodyHeight);
15555
15556         if(this.animate){
15557             var _this = this;
15558             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15559         }else{
15560             this.touchViewEl.addClass('in');
15561         }
15562         
15563         if(this._touchViewMask){
15564             Roo.get(document.body).addClass("x-body-masked");
15565             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15566             this._touchViewMask.setStyle('z-index', 10000);
15567             this._touchViewMask.addClass('show');
15568         }
15569         
15570         this.doTouchViewQuery();
15571         
15572     },
15573     
15574     hideTouchView : function()
15575     {
15576         this.touchViewEl.removeClass('in');
15577
15578         if(this.animate){
15579             var _this = this;
15580             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15581         }else{
15582             this.touchViewEl.setStyle('display', 'none');
15583         }
15584         
15585         if(this._touchViewMask){
15586             this._touchViewMask.removeClass('show');
15587             Roo.get(document.body).removeClass("x-body-masked");
15588         }
15589     },
15590     
15591     setTouchViewValue : function()
15592     {
15593         if(this.multiple){
15594             this.clearItem();
15595         
15596             var _this = this;
15597
15598             Roo.each(this.tickItems, function(o){
15599                 this.addItem(o);
15600             }, this);
15601         }
15602         
15603         this.hideTouchView();
15604     },
15605     
15606     doTouchViewQuery : function()
15607     {
15608         var qe = {
15609             query: '',
15610             forceAll: true,
15611             combo: this,
15612             cancel:false
15613         };
15614         
15615         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15616             return false;
15617         }
15618         
15619         if(!this.alwaysQuery || this.mode == 'local'){
15620             this.onTouchViewLoad();
15621             return;
15622         }
15623         
15624         this.store.load();
15625     },
15626     
15627     onTouchViewBeforeLoad : function(combo,opts)
15628     {
15629         return;
15630     },
15631
15632     // private
15633     onTouchViewLoad : function()
15634     {
15635         if(this.store.getCount() < 1){
15636             this.onTouchViewEmptyResults();
15637             return;
15638         }
15639         
15640         this.clearTouchView();
15641         
15642         var rawValue = this.getRawValue();
15643         
15644         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15645         
15646         this.tickItems = [];
15647         
15648         this.store.data.each(function(d, rowIndex){
15649             var row = this.touchViewListGroup.createChild(template);
15650             
15651             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15652                 row.addClass(d.data.cls);
15653             }
15654             
15655             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15656                 var cfg = {
15657                     data : d.data,
15658                     html : d.data[this.displayField]
15659                 };
15660                 
15661                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15662                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15663                 }
15664             }
15665             row.removeClass('selected');
15666             if(!this.multiple && this.valueField &&
15667                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15668             {
15669                 // radio buttons..
15670                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15671                 row.addClass('selected');
15672             }
15673             
15674             if(this.multiple && this.valueField &&
15675                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15676             {
15677                 
15678                 // checkboxes...
15679                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15680                 this.tickItems.push(d.data);
15681             }
15682             
15683             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15684             
15685         }, this);
15686         
15687         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15688         
15689         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15690
15691         if(this.modalTitle.length){
15692             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15693         }
15694
15695         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15696         
15697         if(this.mobile_restrict_height && listHeight < bodyHeight){
15698             this.touchViewBodyEl.setHeight(listHeight);
15699         }
15700         
15701         var _this = this;
15702         
15703         if(firstChecked && listHeight > bodyHeight){
15704             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15705         }
15706         
15707     },
15708     
15709     onTouchViewLoadException : function()
15710     {
15711         this.hideTouchView();
15712     },
15713     
15714     onTouchViewEmptyResults : function()
15715     {
15716         this.clearTouchView();
15717         
15718         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15719         
15720         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15721         
15722     },
15723     
15724     clearTouchView : function()
15725     {
15726         this.touchViewListGroup.dom.innerHTML = '';
15727     },
15728     
15729     onTouchViewClick : function(e, el, o)
15730     {
15731         e.preventDefault();
15732         
15733         var row = o.row;
15734         var rowIndex = o.rowIndex;
15735         
15736         var r = this.store.getAt(rowIndex);
15737         
15738         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15739             
15740             if(!this.multiple){
15741                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15742                     c.dom.removeAttribute('checked');
15743                 }, this);
15744
15745                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15746
15747                 this.setFromData(r.data);
15748
15749                 var close = this.closeTriggerEl();
15750
15751                 if(close){
15752                     close.show();
15753                 }
15754
15755                 this.hideTouchView();
15756
15757                 this.fireEvent('select', this, r, rowIndex);
15758
15759                 return;
15760             }
15761
15762             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15763                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15764                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15765                 return;
15766             }
15767
15768             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15769             this.addItem(r.data);
15770             this.tickItems.push(r.data);
15771         }
15772     },
15773     
15774     getAutoCreateNativeIOS : function()
15775     {
15776         var cfg = {
15777             cls: 'form-group' //input-group,
15778         };
15779         
15780         var combobox =  {
15781             tag: 'select',
15782             cls : 'roo-ios-select'
15783         };
15784         
15785         if (this.name) {
15786             combobox.name = this.name;
15787         }
15788         
15789         if (this.disabled) {
15790             combobox.disabled = true;
15791         }
15792         
15793         var settings = this;
15794         
15795         ['xs','sm','md','lg'].map(function(size){
15796             if (settings[size]) {
15797                 cfg.cls += ' col-' + size + '-' + settings[size];
15798             }
15799         });
15800         
15801         cfg.cn = combobox;
15802         
15803         return cfg;
15804         
15805     },
15806     
15807     initIOSView : function()
15808     {
15809         this.store.on('load', this.onIOSViewLoad, this);
15810         
15811         return;
15812     },
15813     
15814     onIOSViewLoad : function()
15815     {
15816         if(this.store.getCount() < 1){
15817             return;
15818         }
15819         
15820         this.clearIOSView();
15821         
15822         if(this.allowBlank) {
15823             
15824             var default_text = '-- SELECT --';
15825             
15826             if(this.placeholder.length){
15827                 default_text = this.placeholder;
15828             }
15829             
15830             if(this.emptyTitle.length){
15831                 default_text += ' - ' + this.emptyTitle + ' -';
15832             }
15833             
15834             var opt = this.inputEl().createChild({
15835                 tag: 'option',
15836                 value : 0,
15837                 html : default_text
15838             });
15839             
15840             var o = {};
15841             o[this.valueField] = 0;
15842             o[this.displayField] = default_text;
15843             
15844             this.ios_options.push({
15845                 data : o,
15846                 el : opt
15847             });
15848             
15849         }
15850         
15851         this.store.data.each(function(d, rowIndex){
15852             
15853             var html = '';
15854             
15855             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15856                 html = d.data[this.displayField];
15857             }
15858             
15859             var value = '';
15860             
15861             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15862                 value = d.data[this.valueField];
15863             }
15864             
15865             var option = {
15866                 tag: 'option',
15867                 value : value,
15868                 html : html
15869             };
15870             
15871             if(this.value == d.data[this.valueField]){
15872                 option['selected'] = true;
15873             }
15874             
15875             var opt = this.inputEl().createChild(option);
15876             
15877             this.ios_options.push({
15878                 data : d.data,
15879                 el : opt
15880             });
15881             
15882         }, this);
15883         
15884         this.inputEl().on('change', function(){
15885            this.fireEvent('select', this);
15886         }, this);
15887         
15888     },
15889     
15890     clearIOSView: function()
15891     {
15892         this.inputEl().dom.innerHTML = '';
15893         
15894         this.ios_options = [];
15895     },
15896     
15897     setIOSValue: function(v)
15898     {
15899         this.value = v;
15900         
15901         if(!this.ios_options){
15902             return;
15903         }
15904         
15905         Roo.each(this.ios_options, function(opts){
15906            
15907            opts.el.dom.removeAttribute('selected');
15908            
15909            if(opts.data[this.valueField] != v){
15910                return;
15911            }
15912            
15913            opts.el.dom.setAttribute('selected', true);
15914            
15915         }, this);
15916     }
15917
15918     /** 
15919     * @cfg {Boolean} grow 
15920     * @hide 
15921     */
15922     /** 
15923     * @cfg {Number} growMin 
15924     * @hide 
15925     */
15926     /** 
15927     * @cfg {Number} growMax 
15928     * @hide 
15929     */
15930     /**
15931      * @hide
15932      * @method autoSize
15933      */
15934 });
15935
15936 Roo.apply(Roo.bootstrap.ComboBox,  {
15937     
15938     header : {
15939         tag: 'div',
15940         cls: 'modal-header',
15941         cn: [
15942             {
15943                 tag: 'h4',
15944                 cls: 'modal-title'
15945             }
15946         ]
15947     },
15948     
15949     body : {
15950         tag: 'div',
15951         cls: 'modal-body',
15952         cn: [
15953             {
15954                 tag: 'ul',
15955                 cls: 'list-group'
15956             }
15957         ]
15958     },
15959     
15960     listItemRadio : {
15961         tag: 'li',
15962         cls: 'list-group-item',
15963         cn: [
15964             {
15965                 tag: 'span',
15966                 cls: 'roo-combobox-list-group-item-value'
15967             },
15968             {
15969                 tag: 'div',
15970                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15971                 cn: [
15972                     {
15973                         tag: 'input',
15974                         type: 'radio'
15975                     },
15976                     {
15977                         tag: 'label'
15978                     }
15979                 ]
15980             }
15981         ]
15982     },
15983     
15984     listItemCheckbox : {
15985         tag: 'li',
15986         cls: 'list-group-item',
15987         cn: [
15988             {
15989                 tag: 'span',
15990                 cls: 'roo-combobox-list-group-item-value'
15991             },
15992             {
15993                 tag: 'div',
15994                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15995                 cn: [
15996                     {
15997                         tag: 'input',
15998                         type: 'checkbox'
15999                     },
16000                     {
16001                         tag: 'label'
16002                     }
16003                 ]
16004             }
16005         ]
16006     },
16007     
16008     emptyResult : {
16009         tag: 'div',
16010         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16011     },
16012     
16013     footer : {
16014         tag: 'div',
16015         cls: 'modal-footer',
16016         cn: [
16017             {
16018                 tag: 'div',
16019                 cls: 'row',
16020                 cn: [
16021                     {
16022                         tag: 'div',
16023                         cls: 'col-xs-6 text-left',
16024                         cn: {
16025                             tag: 'button',
16026                             cls: 'btn btn-danger roo-touch-view-cancel',
16027                             html: 'Cancel'
16028                         }
16029                     },
16030                     {
16031                         tag: 'div',
16032                         cls: 'col-xs-6 text-right',
16033                         cn: {
16034                             tag: 'button',
16035                             cls: 'btn btn-success roo-touch-view-ok',
16036                             html: 'OK'
16037                         }
16038                     }
16039                 ]
16040             }
16041         ]
16042         
16043     }
16044 });
16045
16046 Roo.apply(Roo.bootstrap.ComboBox,  {
16047     
16048     touchViewTemplate : {
16049         tag: 'div',
16050         cls: 'modal fade roo-combobox-touch-view',
16051         cn: [
16052             {
16053                 tag: 'div',
16054                 cls: 'modal-dialog',
16055                 style : 'position:fixed', // we have to fix position....
16056                 cn: [
16057                     {
16058                         tag: 'div',
16059                         cls: 'modal-content',
16060                         cn: [
16061                             Roo.bootstrap.ComboBox.header,
16062                             Roo.bootstrap.ComboBox.body,
16063                             Roo.bootstrap.ComboBox.footer
16064                         ]
16065                     }
16066                 ]
16067             }
16068         ]
16069     }
16070 });/*
16071  * Based on:
16072  * Ext JS Library 1.1.1
16073  * Copyright(c) 2006-2007, Ext JS, LLC.
16074  *
16075  * Originally Released Under LGPL - original licence link has changed is not relivant.
16076  *
16077  * Fork - LGPL
16078  * <script type="text/javascript">
16079  */
16080
16081 /**
16082  * @class Roo.View
16083  * @extends Roo.util.Observable
16084  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16085  * This class also supports single and multi selection modes. <br>
16086  * Create a data model bound view:
16087  <pre><code>
16088  var store = new Roo.data.Store(...);
16089
16090  var view = new Roo.View({
16091     el : "my-element",
16092     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16093  
16094     singleSelect: true,
16095     selectedClass: "ydataview-selected",
16096     store: store
16097  });
16098
16099  // listen for node click?
16100  view.on("click", function(vw, index, node, e){
16101  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16102  });
16103
16104  // load XML data
16105  dataModel.load("foobar.xml");
16106  </code></pre>
16107  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16108  * <br><br>
16109  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16110  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16111  * 
16112  * Note: old style constructor is still suported (container, template, config)
16113  * 
16114  * @constructor
16115  * Create a new View
16116  * @param {Object} config The config object
16117  * 
16118  */
16119 Roo.View = function(config, depreciated_tpl, depreciated_config){
16120     
16121     this.parent = false;
16122     
16123     if (typeof(depreciated_tpl) == 'undefined') {
16124         // new way.. - universal constructor.
16125         Roo.apply(this, config);
16126         this.el  = Roo.get(this.el);
16127     } else {
16128         // old format..
16129         this.el  = Roo.get(config);
16130         this.tpl = depreciated_tpl;
16131         Roo.apply(this, depreciated_config);
16132     }
16133     this.wrapEl  = this.el.wrap().wrap();
16134     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16135     
16136     
16137     if(typeof(this.tpl) == "string"){
16138         this.tpl = new Roo.Template(this.tpl);
16139     } else {
16140         // support xtype ctors..
16141         this.tpl = new Roo.factory(this.tpl, Roo);
16142     }
16143     
16144     
16145     this.tpl.compile();
16146     
16147     /** @private */
16148     this.addEvents({
16149         /**
16150          * @event beforeclick
16151          * Fires before a click is processed. Returns false to cancel the default action.
16152          * @param {Roo.View} this
16153          * @param {Number} index The index of the target node
16154          * @param {HTMLElement} node The target node
16155          * @param {Roo.EventObject} e The raw event object
16156          */
16157             "beforeclick" : true,
16158         /**
16159          * @event click
16160          * Fires when a template node is clicked.
16161          * @param {Roo.View} this
16162          * @param {Number} index The index of the target node
16163          * @param {HTMLElement} node The target node
16164          * @param {Roo.EventObject} e The raw event object
16165          */
16166             "click" : true,
16167         /**
16168          * @event dblclick
16169          * Fires when a template node is double clicked.
16170          * @param {Roo.View} this
16171          * @param {Number} index The index of the target node
16172          * @param {HTMLElement} node The target node
16173          * @param {Roo.EventObject} e The raw event object
16174          */
16175             "dblclick" : true,
16176         /**
16177          * @event contextmenu
16178          * Fires when a template node is right clicked.
16179          * @param {Roo.View} this
16180          * @param {Number} index The index of the target node
16181          * @param {HTMLElement} node The target node
16182          * @param {Roo.EventObject} e The raw event object
16183          */
16184             "contextmenu" : true,
16185         /**
16186          * @event selectionchange
16187          * Fires when the selected nodes change.
16188          * @param {Roo.View} this
16189          * @param {Array} selections Array of the selected nodes
16190          */
16191             "selectionchange" : true,
16192     
16193         /**
16194          * @event beforeselect
16195          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16196          * @param {Roo.View} this
16197          * @param {HTMLElement} node The node to be selected
16198          * @param {Array} selections Array of currently selected nodes
16199          */
16200             "beforeselect" : true,
16201         /**
16202          * @event preparedata
16203          * Fires on every row to render, to allow you to change the data.
16204          * @param {Roo.View} this
16205          * @param {Object} data to be rendered (change this)
16206          */
16207           "preparedata" : true
16208           
16209           
16210         });
16211
16212
16213
16214     this.el.on({
16215         "click": this.onClick,
16216         "dblclick": this.onDblClick,
16217         "contextmenu": this.onContextMenu,
16218         scope:this
16219     });
16220
16221     this.selections = [];
16222     this.nodes = [];
16223     this.cmp = new Roo.CompositeElementLite([]);
16224     if(this.store){
16225         this.store = Roo.factory(this.store, Roo.data);
16226         this.setStore(this.store, true);
16227     }
16228     
16229     if ( this.footer && this.footer.xtype) {
16230            
16231          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16232         
16233         this.footer.dataSource = this.store;
16234         this.footer.container = fctr;
16235         this.footer = Roo.factory(this.footer, Roo);
16236         fctr.insertFirst(this.el);
16237         
16238         // this is a bit insane - as the paging toolbar seems to detach the el..
16239 //        dom.parentNode.parentNode.parentNode
16240          // they get detached?
16241     }
16242     
16243     
16244     Roo.View.superclass.constructor.call(this);
16245     
16246     
16247 };
16248
16249 Roo.extend(Roo.View, Roo.util.Observable, {
16250     
16251      /**
16252      * @cfg {Roo.data.Store} store Data store to load data from.
16253      */
16254     store : false,
16255     
16256     /**
16257      * @cfg {String|Roo.Element} el The container element.
16258      */
16259     el : '',
16260     
16261     /**
16262      * @cfg {String|Roo.Template} tpl The template used by this View 
16263      */
16264     tpl : false,
16265     /**
16266      * @cfg {String} dataName the named area of the template to use as the data area
16267      *                          Works with domtemplates roo-name="name"
16268      */
16269     dataName: false,
16270     /**
16271      * @cfg {String} selectedClass The css class to add to selected nodes
16272      */
16273     selectedClass : "x-view-selected",
16274      /**
16275      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16276      */
16277     emptyText : "",
16278     
16279     /**
16280      * @cfg {String} text to display on mask (default Loading)
16281      */
16282     mask : false,
16283     /**
16284      * @cfg {Boolean} multiSelect Allow multiple selection
16285      */
16286     multiSelect : false,
16287     /**
16288      * @cfg {Boolean} singleSelect Allow single selection
16289      */
16290     singleSelect:  false,
16291     
16292     /**
16293      * @cfg {Boolean} toggleSelect - selecting 
16294      */
16295     toggleSelect : false,
16296     
16297     /**
16298      * @cfg {Boolean} tickable - selecting 
16299      */
16300     tickable : false,
16301     
16302     /**
16303      * Returns the element this view is bound to.
16304      * @return {Roo.Element}
16305      */
16306     getEl : function(){
16307         return this.wrapEl;
16308     },
16309     
16310     
16311
16312     /**
16313      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16314      */
16315     refresh : function(){
16316         //Roo.log('refresh');
16317         var t = this.tpl;
16318         
16319         // if we are using something like 'domtemplate', then
16320         // the what gets used is:
16321         // t.applySubtemplate(NAME, data, wrapping data..)
16322         // the outer template then get' applied with
16323         //     the store 'extra data'
16324         // and the body get's added to the
16325         //      roo-name="data" node?
16326         //      <span class='roo-tpl-{name}'></span> ?????
16327         
16328         
16329         
16330         this.clearSelections();
16331         this.el.update("");
16332         var html = [];
16333         var records = this.store.getRange();
16334         if(records.length < 1) {
16335             
16336             // is this valid??  = should it render a template??
16337             
16338             this.el.update(this.emptyText);
16339             return;
16340         }
16341         var el = this.el;
16342         if (this.dataName) {
16343             this.el.update(t.apply(this.store.meta)); //????
16344             el = this.el.child('.roo-tpl-' + this.dataName);
16345         }
16346         
16347         for(var i = 0, len = records.length; i < len; i++){
16348             var data = this.prepareData(records[i].data, i, records[i]);
16349             this.fireEvent("preparedata", this, data, i, records[i]);
16350             
16351             var d = Roo.apply({}, data);
16352             
16353             if(this.tickable){
16354                 Roo.apply(d, {'roo-id' : Roo.id()});
16355                 
16356                 var _this = this;
16357             
16358                 Roo.each(this.parent.item, function(item){
16359                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16360                         return;
16361                     }
16362                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16363                 });
16364             }
16365             
16366             html[html.length] = Roo.util.Format.trim(
16367                 this.dataName ?
16368                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16369                     t.apply(d)
16370             );
16371         }
16372         
16373         
16374         
16375         el.update(html.join(""));
16376         this.nodes = el.dom.childNodes;
16377         this.updateIndexes(0);
16378     },
16379     
16380
16381     /**
16382      * Function to override to reformat the data that is sent to
16383      * the template for each node.
16384      * DEPRICATED - use the preparedata event handler.
16385      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16386      * a JSON object for an UpdateManager bound view).
16387      */
16388     prepareData : function(data, index, record)
16389     {
16390         this.fireEvent("preparedata", this, data, index, record);
16391         return data;
16392     },
16393
16394     onUpdate : function(ds, record){
16395         // Roo.log('on update');   
16396         this.clearSelections();
16397         var index = this.store.indexOf(record);
16398         var n = this.nodes[index];
16399         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16400         n.parentNode.removeChild(n);
16401         this.updateIndexes(index, index);
16402     },
16403
16404     
16405     
16406 // --------- FIXME     
16407     onAdd : function(ds, records, index)
16408     {
16409         //Roo.log(['on Add', ds, records, index] );        
16410         this.clearSelections();
16411         if(this.nodes.length == 0){
16412             this.refresh();
16413             return;
16414         }
16415         var n = this.nodes[index];
16416         for(var i = 0, len = records.length; i < len; i++){
16417             var d = this.prepareData(records[i].data, i, records[i]);
16418             if(n){
16419                 this.tpl.insertBefore(n, d);
16420             }else{
16421                 
16422                 this.tpl.append(this.el, d);
16423             }
16424         }
16425         this.updateIndexes(index);
16426     },
16427
16428     onRemove : function(ds, record, index){
16429        // Roo.log('onRemove');
16430         this.clearSelections();
16431         var el = this.dataName  ?
16432             this.el.child('.roo-tpl-' + this.dataName) :
16433             this.el; 
16434         
16435         el.dom.removeChild(this.nodes[index]);
16436         this.updateIndexes(index);
16437     },
16438
16439     /**
16440      * Refresh an individual node.
16441      * @param {Number} index
16442      */
16443     refreshNode : function(index){
16444         this.onUpdate(this.store, this.store.getAt(index));
16445     },
16446
16447     updateIndexes : function(startIndex, endIndex){
16448         var ns = this.nodes;
16449         startIndex = startIndex || 0;
16450         endIndex = endIndex || ns.length - 1;
16451         for(var i = startIndex; i <= endIndex; i++){
16452             ns[i].nodeIndex = i;
16453         }
16454     },
16455
16456     /**
16457      * Changes the data store this view uses and refresh the view.
16458      * @param {Store} store
16459      */
16460     setStore : function(store, initial){
16461         if(!initial && this.store){
16462             this.store.un("datachanged", this.refresh);
16463             this.store.un("add", this.onAdd);
16464             this.store.un("remove", this.onRemove);
16465             this.store.un("update", this.onUpdate);
16466             this.store.un("clear", this.refresh);
16467             this.store.un("beforeload", this.onBeforeLoad);
16468             this.store.un("load", this.onLoad);
16469             this.store.un("loadexception", this.onLoad);
16470         }
16471         if(store){
16472           
16473             store.on("datachanged", this.refresh, this);
16474             store.on("add", this.onAdd, this);
16475             store.on("remove", this.onRemove, this);
16476             store.on("update", this.onUpdate, this);
16477             store.on("clear", this.refresh, this);
16478             store.on("beforeload", this.onBeforeLoad, this);
16479             store.on("load", this.onLoad, this);
16480             store.on("loadexception", this.onLoad, this);
16481         }
16482         
16483         if(store){
16484             this.refresh();
16485         }
16486     },
16487     /**
16488      * onbeforeLoad - masks the loading area.
16489      *
16490      */
16491     onBeforeLoad : function(store,opts)
16492     {
16493          //Roo.log('onBeforeLoad');   
16494         if (!opts.add) {
16495             this.el.update("");
16496         }
16497         this.el.mask(this.mask ? this.mask : "Loading" ); 
16498     },
16499     onLoad : function ()
16500     {
16501         this.el.unmask();
16502     },
16503     
16504
16505     /**
16506      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16507      * @param {HTMLElement} node
16508      * @return {HTMLElement} The template node
16509      */
16510     findItemFromChild : function(node){
16511         var el = this.dataName  ?
16512             this.el.child('.roo-tpl-' + this.dataName,true) :
16513             this.el.dom; 
16514         
16515         if(!node || node.parentNode == el){
16516                     return node;
16517             }
16518             var p = node.parentNode;
16519             while(p && p != el){
16520             if(p.parentNode == el){
16521                 return p;
16522             }
16523             p = p.parentNode;
16524         }
16525             return null;
16526     },
16527
16528     /** @ignore */
16529     onClick : function(e){
16530         var item = this.findItemFromChild(e.getTarget());
16531         if(item){
16532             var index = this.indexOf(item);
16533             if(this.onItemClick(item, index, e) !== false){
16534                 this.fireEvent("click", this, index, item, e);
16535             }
16536         }else{
16537             this.clearSelections();
16538         }
16539     },
16540
16541     /** @ignore */
16542     onContextMenu : function(e){
16543         var item = this.findItemFromChild(e.getTarget());
16544         if(item){
16545             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16546         }
16547     },
16548
16549     /** @ignore */
16550     onDblClick : function(e){
16551         var item = this.findItemFromChild(e.getTarget());
16552         if(item){
16553             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16554         }
16555     },
16556
16557     onItemClick : function(item, index, e)
16558     {
16559         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16560             return false;
16561         }
16562         if (this.toggleSelect) {
16563             var m = this.isSelected(item) ? 'unselect' : 'select';
16564             //Roo.log(m);
16565             var _t = this;
16566             _t[m](item, true, false);
16567             return true;
16568         }
16569         if(this.multiSelect || this.singleSelect){
16570             if(this.multiSelect && e.shiftKey && this.lastSelection){
16571                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16572             }else{
16573                 this.select(item, this.multiSelect && e.ctrlKey);
16574                 this.lastSelection = item;
16575             }
16576             
16577             if(!this.tickable){
16578                 e.preventDefault();
16579             }
16580             
16581         }
16582         return true;
16583     },
16584
16585     /**
16586      * Get the number of selected nodes.
16587      * @return {Number}
16588      */
16589     getSelectionCount : function(){
16590         return this.selections.length;
16591     },
16592
16593     /**
16594      * Get the currently selected nodes.
16595      * @return {Array} An array of HTMLElements
16596      */
16597     getSelectedNodes : function(){
16598         return this.selections;
16599     },
16600
16601     /**
16602      * Get the indexes of the selected nodes.
16603      * @return {Array}
16604      */
16605     getSelectedIndexes : function(){
16606         var indexes = [], s = this.selections;
16607         for(var i = 0, len = s.length; i < len; i++){
16608             indexes.push(s[i].nodeIndex);
16609         }
16610         return indexes;
16611     },
16612
16613     /**
16614      * Clear all selections
16615      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16616      */
16617     clearSelections : function(suppressEvent){
16618         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16619             this.cmp.elements = this.selections;
16620             this.cmp.removeClass(this.selectedClass);
16621             this.selections = [];
16622             if(!suppressEvent){
16623                 this.fireEvent("selectionchange", this, this.selections);
16624             }
16625         }
16626     },
16627
16628     /**
16629      * Returns true if the passed node is selected
16630      * @param {HTMLElement/Number} node The node or node index
16631      * @return {Boolean}
16632      */
16633     isSelected : function(node){
16634         var s = this.selections;
16635         if(s.length < 1){
16636             return false;
16637         }
16638         node = this.getNode(node);
16639         return s.indexOf(node) !== -1;
16640     },
16641
16642     /**
16643      * Selects nodes.
16644      * @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
16645      * @param {Boolean} keepExisting (optional) true to keep existing selections
16646      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16647      */
16648     select : function(nodeInfo, keepExisting, suppressEvent){
16649         if(nodeInfo instanceof Array){
16650             if(!keepExisting){
16651                 this.clearSelections(true);
16652             }
16653             for(var i = 0, len = nodeInfo.length; i < len; i++){
16654                 this.select(nodeInfo[i], true, true);
16655             }
16656             return;
16657         } 
16658         var node = this.getNode(nodeInfo);
16659         if(!node || this.isSelected(node)){
16660             return; // already selected.
16661         }
16662         if(!keepExisting){
16663             this.clearSelections(true);
16664         }
16665         
16666         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16667             Roo.fly(node).addClass(this.selectedClass);
16668             this.selections.push(node);
16669             if(!suppressEvent){
16670                 this.fireEvent("selectionchange", this, this.selections);
16671             }
16672         }
16673         
16674         
16675     },
16676       /**
16677      * Unselects nodes.
16678      * @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
16679      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16680      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16681      */
16682     unselect : function(nodeInfo, keepExisting, suppressEvent)
16683     {
16684         if(nodeInfo instanceof Array){
16685             Roo.each(this.selections, function(s) {
16686                 this.unselect(s, nodeInfo);
16687             }, this);
16688             return;
16689         }
16690         var node = this.getNode(nodeInfo);
16691         if(!node || !this.isSelected(node)){
16692             //Roo.log("not selected");
16693             return; // not selected.
16694         }
16695         // fireevent???
16696         var ns = [];
16697         Roo.each(this.selections, function(s) {
16698             if (s == node ) {
16699                 Roo.fly(node).removeClass(this.selectedClass);
16700
16701                 return;
16702             }
16703             ns.push(s);
16704         },this);
16705         
16706         this.selections= ns;
16707         this.fireEvent("selectionchange", this, this.selections);
16708     },
16709
16710     /**
16711      * Gets a template node.
16712      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16713      * @return {HTMLElement} The node or null if it wasn't found
16714      */
16715     getNode : function(nodeInfo){
16716         if(typeof nodeInfo == "string"){
16717             return document.getElementById(nodeInfo);
16718         }else if(typeof nodeInfo == "number"){
16719             return this.nodes[nodeInfo];
16720         }
16721         return nodeInfo;
16722     },
16723
16724     /**
16725      * Gets a range template nodes.
16726      * @param {Number} startIndex
16727      * @param {Number} endIndex
16728      * @return {Array} An array of nodes
16729      */
16730     getNodes : function(start, end){
16731         var ns = this.nodes;
16732         start = start || 0;
16733         end = typeof end == "undefined" ? ns.length - 1 : end;
16734         var nodes = [];
16735         if(start <= end){
16736             for(var i = start; i <= end; i++){
16737                 nodes.push(ns[i]);
16738             }
16739         } else{
16740             for(var i = start; i >= end; i--){
16741                 nodes.push(ns[i]);
16742             }
16743         }
16744         return nodes;
16745     },
16746
16747     /**
16748      * Finds the index of the passed node
16749      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16750      * @return {Number} The index of the node or -1
16751      */
16752     indexOf : function(node){
16753         node = this.getNode(node);
16754         if(typeof node.nodeIndex == "number"){
16755             return node.nodeIndex;
16756         }
16757         var ns = this.nodes;
16758         for(var i = 0, len = ns.length; i < len; i++){
16759             if(ns[i] == node){
16760                 return i;
16761             }
16762         }
16763         return -1;
16764     }
16765 });
16766 /*
16767  * - LGPL
16768  *
16769  * based on jquery fullcalendar
16770  * 
16771  */
16772
16773 Roo.bootstrap = Roo.bootstrap || {};
16774 /**
16775  * @class Roo.bootstrap.Calendar
16776  * @extends Roo.bootstrap.Component
16777  * Bootstrap Calendar class
16778  * @cfg {Boolean} loadMask (true|false) default false
16779  * @cfg {Object} header generate the user specific header of the calendar, default false
16780
16781  * @constructor
16782  * Create a new Container
16783  * @param {Object} config The config object
16784  */
16785
16786
16787
16788 Roo.bootstrap.Calendar = function(config){
16789     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16790      this.addEvents({
16791         /**
16792              * @event select
16793              * Fires when a date is selected
16794              * @param {DatePicker} this
16795              * @param {Date} date The selected date
16796              */
16797         'select': true,
16798         /**
16799              * @event monthchange
16800              * Fires when the displayed month changes 
16801              * @param {DatePicker} this
16802              * @param {Date} date The selected month
16803              */
16804         'monthchange': true,
16805         /**
16806              * @event evententer
16807              * Fires when mouse over an event
16808              * @param {Calendar} this
16809              * @param {event} Event
16810              */
16811         'evententer': true,
16812         /**
16813              * @event eventleave
16814              * Fires when the mouse leaves an
16815              * @param {Calendar} this
16816              * @param {event}
16817              */
16818         'eventleave': true,
16819         /**
16820              * @event eventclick
16821              * Fires when the mouse click an
16822              * @param {Calendar} this
16823              * @param {event}
16824              */
16825         'eventclick': true
16826         
16827     });
16828
16829 };
16830
16831 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16832     
16833      /**
16834      * @cfg {Number} startDay
16835      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16836      */
16837     startDay : 0,
16838     
16839     loadMask : false,
16840     
16841     header : false,
16842       
16843     getAutoCreate : function(){
16844         
16845         
16846         var fc_button = function(name, corner, style, content ) {
16847             return Roo.apply({},{
16848                 tag : 'span',
16849                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16850                          (corner.length ?
16851                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16852                             ''
16853                         ),
16854                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16855                 unselectable: 'on'
16856             });
16857         };
16858         
16859         var header = {};
16860         
16861         if(!this.header){
16862             header = {
16863                 tag : 'table',
16864                 cls : 'fc-header',
16865                 style : 'width:100%',
16866                 cn : [
16867                     {
16868                         tag: 'tr',
16869                         cn : [
16870                             {
16871                                 tag : 'td',
16872                                 cls : 'fc-header-left',
16873                                 cn : [
16874                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16875                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16876                                     { tag: 'span', cls: 'fc-header-space' },
16877                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16878
16879
16880                                 ]
16881                             },
16882
16883                             {
16884                                 tag : 'td',
16885                                 cls : 'fc-header-center',
16886                                 cn : [
16887                                     {
16888                                         tag: 'span',
16889                                         cls: 'fc-header-title',
16890                                         cn : {
16891                                             tag: 'H2',
16892                                             html : 'month / year'
16893                                         }
16894                                     }
16895
16896                                 ]
16897                             },
16898                             {
16899                                 tag : 'td',
16900                                 cls : 'fc-header-right',
16901                                 cn : [
16902                               /*      fc_button('month', 'left', '', 'month' ),
16903                                     fc_button('week', '', '', 'week' ),
16904                                     fc_button('day', 'right', '', 'day' )
16905                                 */    
16906
16907                                 ]
16908                             }
16909
16910                         ]
16911                     }
16912                 ]
16913             };
16914         }
16915         
16916         header = this.header;
16917         
16918        
16919         var cal_heads = function() {
16920             var ret = [];
16921             // fixme - handle this.
16922             
16923             for (var i =0; i < Date.dayNames.length; i++) {
16924                 var d = Date.dayNames[i];
16925                 ret.push({
16926                     tag: 'th',
16927                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16928                     html : d.substring(0,3)
16929                 });
16930                 
16931             }
16932             ret[0].cls += ' fc-first';
16933             ret[6].cls += ' fc-last';
16934             return ret;
16935         };
16936         var cal_cell = function(n) {
16937             return  {
16938                 tag: 'td',
16939                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16940                 cn : [
16941                     {
16942                         cn : [
16943                             {
16944                                 cls: 'fc-day-number',
16945                                 html: 'D'
16946                             },
16947                             {
16948                                 cls: 'fc-day-content',
16949                              
16950                                 cn : [
16951                                      {
16952                                         style: 'position: relative;' // height: 17px;
16953                                     }
16954                                 ]
16955                             }
16956                             
16957                             
16958                         ]
16959                     }
16960                 ]
16961                 
16962             }
16963         };
16964         var cal_rows = function() {
16965             
16966             var ret = [];
16967             for (var r = 0; r < 6; r++) {
16968                 var row= {
16969                     tag : 'tr',
16970                     cls : 'fc-week',
16971                     cn : []
16972                 };
16973                 
16974                 for (var i =0; i < Date.dayNames.length; i++) {
16975                     var d = Date.dayNames[i];
16976                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16977
16978                 }
16979                 row.cn[0].cls+=' fc-first';
16980                 row.cn[0].cn[0].style = 'min-height:90px';
16981                 row.cn[6].cls+=' fc-last';
16982                 ret.push(row);
16983                 
16984             }
16985             ret[0].cls += ' fc-first';
16986             ret[4].cls += ' fc-prev-last';
16987             ret[5].cls += ' fc-last';
16988             return ret;
16989             
16990         };
16991         
16992         var cal_table = {
16993             tag: 'table',
16994             cls: 'fc-border-separate',
16995             style : 'width:100%',
16996             cellspacing  : 0,
16997             cn : [
16998                 { 
16999                     tag: 'thead',
17000                     cn : [
17001                         { 
17002                             tag: 'tr',
17003                             cls : 'fc-first fc-last',
17004                             cn : cal_heads()
17005                         }
17006                     ]
17007                 },
17008                 { 
17009                     tag: 'tbody',
17010                     cn : cal_rows()
17011                 }
17012                   
17013             ]
17014         };
17015          
17016          var cfg = {
17017             cls : 'fc fc-ltr',
17018             cn : [
17019                 header,
17020                 {
17021                     cls : 'fc-content',
17022                     style : "position: relative;",
17023                     cn : [
17024                         {
17025                             cls : 'fc-view fc-view-month fc-grid',
17026                             style : 'position: relative',
17027                             unselectable : 'on',
17028                             cn : [
17029                                 {
17030                                     cls : 'fc-event-container',
17031                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17032                                 },
17033                                 cal_table
17034                             ]
17035                         }
17036                     ]
17037     
17038                 }
17039            ] 
17040             
17041         };
17042         
17043          
17044         
17045         return cfg;
17046     },
17047     
17048     
17049     initEvents : function()
17050     {
17051         if(!this.store){
17052             throw "can not find store for calendar";
17053         }
17054         
17055         var mark = {
17056             tag: "div",
17057             cls:"x-dlg-mask",
17058             style: "text-align:center",
17059             cn: [
17060                 {
17061                     tag: "div",
17062                     style: "background-color:white;width:50%;margin:250 auto",
17063                     cn: [
17064                         {
17065                             tag: "img",
17066                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17067                         },
17068                         {
17069                             tag: "span",
17070                             html: "Loading"
17071                         }
17072                         
17073                     ]
17074                 }
17075             ]
17076         };
17077         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17078         
17079         var size = this.el.select('.fc-content', true).first().getSize();
17080         this.maskEl.setSize(size.width, size.height);
17081         this.maskEl.enableDisplayMode("block");
17082         if(!this.loadMask){
17083             this.maskEl.hide();
17084         }
17085         
17086         this.store = Roo.factory(this.store, Roo.data);
17087         this.store.on('load', this.onLoad, this);
17088         this.store.on('beforeload', this.onBeforeLoad, this);
17089         
17090         this.resize();
17091         
17092         this.cells = this.el.select('.fc-day',true);
17093         //Roo.log(this.cells);
17094         this.textNodes = this.el.query('.fc-day-number');
17095         this.cells.addClassOnOver('fc-state-hover');
17096         
17097         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17098         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17099         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17100         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17101         
17102         this.on('monthchange', this.onMonthChange, this);
17103         
17104         this.update(new Date().clearTime());
17105     },
17106     
17107     resize : function() {
17108         var sz  = this.el.getSize();
17109         
17110         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17111         this.el.select('.fc-day-content div',true).setHeight(34);
17112     },
17113     
17114     
17115     // private
17116     showPrevMonth : function(e){
17117         this.update(this.activeDate.add("mo", -1));
17118     },
17119     showToday : function(e){
17120         this.update(new Date().clearTime());
17121     },
17122     // private
17123     showNextMonth : function(e){
17124         this.update(this.activeDate.add("mo", 1));
17125     },
17126
17127     // private
17128     showPrevYear : function(){
17129         this.update(this.activeDate.add("y", -1));
17130     },
17131
17132     // private
17133     showNextYear : function(){
17134         this.update(this.activeDate.add("y", 1));
17135     },
17136
17137     
17138    // private
17139     update : function(date)
17140     {
17141         var vd = this.activeDate;
17142         this.activeDate = date;
17143 //        if(vd && this.el){
17144 //            var t = date.getTime();
17145 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17146 //                Roo.log('using add remove');
17147 //                
17148 //                this.fireEvent('monthchange', this, date);
17149 //                
17150 //                this.cells.removeClass("fc-state-highlight");
17151 //                this.cells.each(function(c){
17152 //                   if(c.dateValue == t){
17153 //                       c.addClass("fc-state-highlight");
17154 //                       setTimeout(function(){
17155 //                            try{c.dom.firstChild.focus();}catch(e){}
17156 //                       }, 50);
17157 //                       return false;
17158 //                   }
17159 //                   return true;
17160 //                });
17161 //                return;
17162 //            }
17163 //        }
17164         
17165         var days = date.getDaysInMonth();
17166         
17167         var firstOfMonth = date.getFirstDateOfMonth();
17168         var startingPos = firstOfMonth.getDay()-this.startDay;
17169         
17170         if(startingPos < this.startDay){
17171             startingPos += 7;
17172         }
17173         
17174         var pm = date.add(Date.MONTH, -1);
17175         var prevStart = pm.getDaysInMonth()-startingPos;
17176 //        
17177         this.cells = this.el.select('.fc-day',true);
17178         this.textNodes = this.el.query('.fc-day-number');
17179         this.cells.addClassOnOver('fc-state-hover');
17180         
17181         var cells = this.cells.elements;
17182         var textEls = this.textNodes;
17183         
17184         Roo.each(cells, function(cell){
17185             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17186         });
17187         
17188         days += startingPos;
17189
17190         // convert everything to numbers so it's fast
17191         var day = 86400000;
17192         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17193         //Roo.log(d);
17194         //Roo.log(pm);
17195         //Roo.log(prevStart);
17196         
17197         var today = new Date().clearTime().getTime();
17198         var sel = date.clearTime().getTime();
17199         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17200         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17201         var ddMatch = this.disabledDatesRE;
17202         var ddText = this.disabledDatesText;
17203         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17204         var ddaysText = this.disabledDaysText;
17205         var format = this.format;
17206         
17207         var setCellClass = function(cal, cell){
17208             cell.row = 0;
17209             cell.events = [];
17210             cell.more = [];
17211             //Roo.log('set Cell Class');
17212             cell.title = "";
17213             var t = d.getTime();
17214             
17215             //Roo.log(d);
17216             
17217             cell.dateValue = t;
17218             if(t == today){
17219                 cell.className += " fc-today";
17220                 cell.className += " fc-state-highlight";
17221                 cell.title = cal.todayText;
17222             }
17223             if(t == sel){
17224                 // disable highlight in other month..
17225                 //cell.className += " fc-state-highlight";
17226                 
17227             }
17228             // disabling
17229             if(t < min) {
17230                 cell.className = " fc-state-disabled";
17231                 cell.title = cal.minText;
17232                 return;
17233             }
17234             if(t > max) {
17235                 cell.className = " fc-state-disabled";
17236                 cell.title = cal.maxText;
17237                 return;
17238             }
17239             if(ddays){
17240                 if(ddays.indexOf(d.getDay()) != -1){
17241                     cell.title = ddaysText;
17242                     cell.className = " fc-state-disabled";
17243                 }
17244             }
17245             if(ddMatch && format){
17246                 var fvalue = d.dateFormat(format);
17247                 if(ddMatch.test(fvalue)){
17248                     cell.title = ddText.replace("%0", fvalue);
17249                     cell.className = " fc-state-disabled";
17250                 }
17251             }
17252             
17253             if (!cell.initialClassName) {
17254                 cell.initialClassName = cell.dom.className;
17255             }
17256             
17257             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17258         };
17259
17260         var i = 0;
17261         
17262         for(; i < startingPos; i++) {
17263             textEls[i].innerHTML = (++prevStart);
17264             d.setDate(d.getDate()+1);
17265             
17266             cells[i].className = "fc-past fc-other-month";
17267             setCellClass(this, cells[i]);
17268         }
17269         
17270         var intDay = 0;
17271         
17272         for(; i < days; i++){
17273             intDay = i - startingPos + 1;
17274             textEls[i].innerHTML = (intDay);
17275             d.setDate(d.getDate()+1);
17276             
17277             cells[i].className = ''; // "x-date-active";
17278             setCellClass(this, cells[i]);
17279         }
17280         var extraDays = 0;
17281         
17282         for(; i < 42; i++) {
17283             textEls[i].innerHTML = (++extraDays);
17284             d.setDate(d.getDate()+1);
17285             
17286             cells[i].className = "fc-future fc-other-month";
17287             setCellClass(this, cells[i]);
17288         }
17289         
17290         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17291         
17292         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17293         
17294         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17295         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17296         
17297         if(totalRows != 6){
17298             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17299             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17300         }
17301         
17302         this.fireEvent('monthchange', this, date);
17303         
17304         
17305         /*
17306         if(!this.internalRender){
17307             var main = this.el.dom.firstChild;
17308             var w = main.offsetWidth;
17309             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17310             Roo.fly(main).setWidth(w);
17311             this.internalRender = true;
17312             // opera does not respect the auto grow header center column
17313             // then, after it gets a width opera refuses to recalculate
17314             // without a second pass
17315             if(Roo.isOpera && !this.secondPass){
17316                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17317                 this.secondPass = true;
17318                 this.update.defer(10, this, [date]);
17319             }
17320         }
17321         */
17322         
17323     },
17324     
17325     findCell : function(dt) {
17326         dt = dt.clearTime().getTime();
17327         var ret = false;
17328         this.cells.each(function(c){
17329             //Roo.log("check " +c.dateValue + '?=' + dt);
17330             if(c.dateValue == dt){
17331                 ret = c;
17332                 return false;
17333             }
17334             return true;
17335         });
17336         
17337         return ret;
17338     },
17339     
17340     findCells : function(ev) {
17341         var s = ev.start.clone().clearTime().getTime();
17342        // Roo.log(s);
17343         var e= ev.end.clone().clearTime().getTime();
17344        // Roo.log(e);
17345         var ret = [];
17346         this.cells.each(function(c){
17347              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17348             
17349             if(c.dateValue > e){
17350                 return ;
17351             }
17352             if(c.dateValue < s){
17353                 return ;
17354             }
17355             ret.push(c);
17356         });
17357         
17358         return ret;    
17359     },
17360     
17361 //    findBestRow: function(cells)
17362 //    {
17363 //        var ret = 0;
17364 //        
17365 //        for (var i =0 ; i < cells.length;i++) {
17366 //            ret  = Math.max(cells[i].rows || 0,ret);
17367 //        }
17368 //        return ret;
17369 //        
17370 //    },
17371     
17372     
17373     addItem : function(ev)
17374     {
17375         // look for vertical location slot in
17376         var cells = this.findCells(ev);
17377         
17378 //        ev.row = this.findBestRow(cells);
17379         
17380         // work out the location.
17381         
17382         var crow = false;
17383         var rows = [];
17384         for(var i =0; i < cells.length; i++) {
17385             
17386             cells[i].row = cells[0].row;
17387             
17388             if(i == 0){
17389                 cells[i].row = cells[i].row + 1;
17390             }
17391             
17392             if (!crow) {
17393                 crow = {
17394                     start : cells[i],
17395                     end :  cells[i]
17396                 };
17397                 continue;
17398             }
17399             if (crow.start.getY() == cells[i].getY()) {
17400                 // on same row.
17401                 crow.end = cells[i];
17402                 continue;
17403             }
17404             // different row.
17405             rows.push(crow);
17406             crow = {
17407                 start: cells[i],
17408                 end : cells[i]
17409             };
17410             
17411         }
17412         
17413         rows.push(crow);
17414         ev.els = [];
17415         ev.rows = rows;
17416         ev.cells = cells;
17417         
17418         cells[0].events.push(ev);
17419         
17420         this.calevents.push(ev);
17421     },
17422     
17423     clearEvents: function() {
17424         
17425         if(!this.calevents){
17426             return;
17427         }
17428         
17429         Roo.each(this.cells.elements, function(c){
17430             c.row = 0;
17431             c.events = [];
17432             c.more = [];
17433         });
17434         
17435         Roo.each(this.calevents, function(e) {
17436             Roo.each(e.els, function(el) {
17437                 el.un('mouseenter' ,this.onEventEnter, this);
17438                 el.un('mouseleave' ,this.onEventLeave, this);
17439                 el.remove();
17440             },this);
17441         },this);
17442         
17443         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17444             e.remove();
17445         });
17446         
17447     },
17448     
17449     renderEvents: function()
17450     {   
17451         var _this = this;
17452         
17453         this.cells.each(function(c) {
17454             
17455             if(c.row < 5){
17456                 return;
17457             }
17458             
17459             var ev = c.events;
17460             
17461             var r = 4;
17462             if(c.row != c.events.length){
17463                 r = 4 - (4 - (c.row - c.events.length));
17464             }
17465             
17466             c.events = ev.slice(0, r);
17467             c.more = ev.slice(r);
17468             
17469             if(c.more.length && c.more.length == 1){
17470                 c.events.push(c.more.pop());
17471             }
17472             
17473             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17474             
17475         });
17476             
17477         this.cells.each(function(c) {
17478             
17479             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17480             
17481             
17482             for (var e = 0; e < c.events.length; e++){
17483                 var ev = c.events[e];
17484                 var rows = ev.rows;
17485                 
17486                 for(var i = 0; i < rows.length; i++) {
17487                 
17488                     // how many rows should it span..
17489
17490                     var  cfg = {
17491                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17492                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17493
17494                         unselectable : "on",
17495                         cn : [
17496                             {
17497                                 cls: 'fc-event-inner',
17498                                 cn : [
17499     //                                {
17500     //                                  tag:'span',
17501     //                                  cls: 'fc-event-time',
17502     //                                  html : cells.length > 1 ? '' : ev.time
17503     //                                },
17504                                     {
17505                                       tag:'span',
17506                                       cls: 'fc-event-title',
17507                                       html : String.format('{0}', ev.title)
17508                                     }
17509
17510
17511                                 ]
17512                             },
17513                             {
17514                                 cls: 'ui-resizable-handle ui-resizable-e',
17515                                 html : '&nbsp;&nbsp;&nbsp'
17516                             }
17517
17518                         ]
17519                     };
17520
17521                     if (i == 0) {
17522                         cfg.cls += ' fc-event-start';
17523                     }
17524                     if ((i+1) == rows.length) {
17525                         cfg.cls += ' fc-event-end';
17526                     }
17527
17528                     var ctr = _this.el.select('.fc-event-container',true).first();
17529                     var cg = ctr.createChild(cfg);
17530
17531                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17532                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17533
17534                     var r = (c.more.length) ? 1 : 0;
17535                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17536                     cg.setWidth(ebox.right - sbox.x -2);
17537
17538                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17539                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17540                     cg.on('click', _this.onEventClick, _this, ev);
17541
17542                     ev.els.push(cg);
17543                     
17544                 }
17545                 
17546             }
17547             
17548             
17549             if(c.more.length){
17550                 var  cfg = {
17551                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17552                     style : 'position: absolute',
17553                     unselectable : "on",
17554                     cn : [
17555                         {
17556                             cls: 'fc-event-inner',
17557                             cn : [
17558                                 {
17559                                   tag:'span',
17560                                   cls: 'fc-event-title',
17561                                   html : 'More'
17562                                 }
17563
17564
17565                             ]
17566                         },
17567                         {
17568                             cls: 'ui-resizable-handle ui-resizable-e',
17569                             html : '&nbsp;&nbsp;&nbsp'
17570                         }
17571
17572                     ]
17573                 };
17574
17575                 var ctr = _this.el.select('.fc-event-container',true).first();
17576                 var cg = ctr.createChild(cfg);
17577
17578                 var sbox = c.select('.fc-day-content',true).first().getBox();
17579                 var ebox = c.select('.fc-day-content',true).first().getBox();
17580                 //Roo.log(cg);
17581                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17582                 cg.setWidth(ebox.right - sbox.x -2);
17583
17584                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17585                 
17586             }
17587             
17588         });
17589         
17590         
17591         
17592     },
17593     
17594     onEventEnter: function (e, el,event,d) {
17595         this.fireEvent('evententer', this, el, event);
17596     },
17597     
17598     onEventLeave: function (e, el,event,d) {
17599         this.fireEvent('eventleave', this, el, event);
17600     },
17601     
17602     onEventClick: function (e, el,event,d) {
17603         this.fireEvent('eventclick', this, el, event);
17604     },
17605     
17606     onMonthChange: function () {
17607         this.store.load();
17608     },
17609     
17610     onMoreEventClick: function(e, el, more)
17611     {
17612         var _this = this;
17613         
17614         this.calpopover.placement = 'right';
17615         this.calpopover.setTitle('More');
17616         
17617         this.calpopover.setContent('');
17618         
17619         var ctr = this.calpopover.el.select('.popover-content', true).first();
17620         
17621         Roo.each(more, function(m){
17622             var cfg = {
17623                 cls : 'fc-event-hori fc-event-draggable',
17624                 html : m.title
17625             };
17626             var cg = ctr.createChild(cfg);
17627             
17628             cg.on('click', _this.onEventClick, _this, m);
17629         });
17630         
17631         this.calpopover.show(el);
17632         
17633         
17634     },
17635     
17636     onLoad: function () 
17637     {   
17638         this.calevents = [];
17639         var cal = this;
17640         
17641         if(this.store.getCount() > 0){
17642             this.store.data.each(function(d){
17643                cal.addItem({
17644                     id : d.data.id,
17645                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17646                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17647                     time : d.data.start_time,
17648                     title : d.data.title,
17649                     description : d.data.description,
17650                     venue : d.data.venue
17651                 });
17652             });
17653         }
17654         
17655         this.renderEvents();
17656         
17657         if(this.calevents.length && this.loadMask){
17658             this.maskEl.hide();
17659         }
17660     },
17661     
17662     onBeforeLoad: function()
17663     {
17664         this.clearEvents();
17665         if(this.loadMask){
17666             this.maskEl.show();
17667         }
17668     }
17669 });
17670
17671  
17672  /*
17673  * - LGPL
17674  *
17675  * element
17676  * 
17677  */
17678
17679 /**
17680  * @class Roo.bootstrap.Popover
17681  * @extends Roo.bootstrap.Component
17682  * Bootstrap Popover class
17683  * @cfg {String} html contents of the popover   (or false to use children..)
17684  * @cfg {String} title of popover (or false to hide)
17685  * @cfg {String} placement how it is placed
17686  * @cfg {String} trigger click || hover (or false to trigger manually)
17687  * @cfg {String} over what (parent or false to trigger manually.)
17688  * @cfg {Number} delay - delay before showing
17689  
17690  * @constructor
17691  * Create a new Popover
17692  * @param {Object} config The config object
17693  */
17694
17695 Roo.bootstrap.Popover = function(config){
17696     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17697     
17698     this.addEvents({
17699         // raw events
17700          /**
17701          * @event show
17702          * After the popover show
17703          * 
17704          * @param {Roo.bootstrap.Popover} this
17705          */
17706         "show" : true,
17707         /**
17708          * @event hide
17709          * After the popover hide
17710          * 
17711          * @param {Roo.bootstrap.Popover} this
17712          */
17713         "hide" : true
17714     });
17715 };
17716
17717 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17718     
17719     title: 'Fill in a title',
17720     html: false,
17721     
17722     placement : 'right',
17723     trigger : 'hover', // hover
17724     
17725     delay : 0,
17726     
17727     over: 'parent',
17728     
17729     can_build_overlaid : false,
17730     
17731     getChildContainer : function()
17732     {
17733         return this.el.select('.popover-content',true).first();
17734     },
17735     
17736     getAutoCreate : function(){
17737          
17738         var cfg = {
17739            cls : 'popover roo-dynamic',
17740            style: 'display:block',
17741            cn : [
17742                 {
17743                     cls : 'arrow'
17744                 },
17745                 {
17746                     cls : 'popover-inner',
17747                     cn : [
17748                         {
17749                             tag: 'h3',
17750                             cls: 'popover-title popover-header',
17751                             html : this.title
17752                         },
17753                         {
17754                             cls : 'popover-content popover-body',
17755                             html : this.html
17756                         }
17757                     ]
17758                     
17759                 }
17760            ]
17761         };
17762         
17763         return cfg;
17764     },
17765     setTitle: function(str)
17766     {
17767         this.title = str;
17768         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17769     },
17770     setContent: function(str)
17771     {
17772         this.html = str;
17773         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17774     },
17775     // as it get's added to the bottom of the page.
17776     onRender : function(ct, position)
17777     {
17778         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17779         if(!this.el){
17780             var cfg = Roo.apply({},  this.getAutoCreate());
17781             cfg.id = Roo.id();
17782             
17783             if (this.cls) {
17784                 cfg.cls += ' ' + this.cls;
17785             }
17786             if (this.style) {
17787                 cfg.style = this.style;
17788             }
17789             //Roo.log("adding to ");
17790             this.el = Roo.get(document.body).createChild(cfg, position);
17791 //            Roo.log(this.el);
17792         }
17793         this.initEvents();
17794     },
17795     
17796     initEvents : function()
17797     {
17798         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17799         this.el.enableDisplayMode('block');
17800         this.el.hide();
17801         if (this.over === false) {
17802             return; 
17803         }
17804         if (this.triggers === false) {
17805             return;
17806         }
17807         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17808         var triggers = this.trigger ? this.trigger.split(' ') : [];
17809         Roo.each(triggers, function(trigger) {
17810         
17811             if (trigger == 'click') {
17812                 on_el.on('click', this.toggle, this);
17813             } else if (trigger != 'manual') {
17814                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17815                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17816       
17817                 on_el.on(eventIn  ,this.enter, this);
17818                 on_el.on(eventOut, this.leave, this);
17819             }
17820         }, this);
17821         
17822     },
17823     
17824     
17825     // private
17826     timeout : null,
17827     hoverState : null,
17828     
17829     toggle : function () {
17830         this.hoverState == 'in' ? this.leave() : this.enter();
17831     },
17832     
17833     enter : function () {
17834         
17835         clearTimeout(this.timeout);
17836     
17837         this.hoverState = 'in';
17838     
17839         if (!this.delay || !this.delay.show) {
17840             this.show();
17841             return;
17842         }
17843         var _t = this;
17844         this.timeout = setTimeout(function () {
17845             if (_t.hoverState == 'in') {
17846                 _t.show();
17847             }
17848         }, this.delay.show)
17849     },
17850     
17851     leave : function() {
17852         clearTimeout(this.timeout);
17853     
17854         this.hoverState = 'out';
17855     
17856         if (!this.delay || !this.delay.hide) {
17857             this.hide();
17858             return;
17859         }
17860         var _t = this;
17861         this.timeout = setTimeout(function () {
17862             if (_t.hoverState == 'out') {
17863                 _t.hide();
17864             }
17865         }, this.delay.hide)
17866     },
17867     
17868     show : function (on_el)
17869     {
17870         if (!on_el) {
17871             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17872         }
17873         
17874         // set content.
17875         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17876         if (this.html !== false) {
17877             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17878         }
17879         this.el.removeClass([
17880             'fade','top','bottom', 'left', 'right','in',
17881             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17882         ]);
17883         if (!this.title.length) {
17884             this.el.select('.popover-title',true).hide();
17885         }
17886         
17887         var placement = typeof this.placement == 'function' ?
17888             this.placement.call(this, this.el, on_el) :
17889             this.placement;
17890             
17891         var autoToken = /\s?auto?\s?/i;
17892         var autoPlace = autoToken.test(placement);
17893         if (autoPlace) {
17894             placement = placement.replace(autoToken, '') || 'top';
17895         }
17896         
17897         //this.el.detach()
17898         //this.el.setXY([0,0]);
17899         this.el.show();
17900         this.el.dom.style.display='block';
17901         this.el.addClass(placement);
17902         
17903         //this.el.appendTo(on_el);
17904         
17905         var p = this.getPosition();
17906         var box = this.el.getBox();
17907         
17908         if (autoPlace) {
17909             // fixme..
17910         }
17911         var align = Roo.bootstrap.Popover.alignment[placement];
17912         
17913 //        Roo.log(align);
17914         this.el.alignTo(on_el, align[0],align[1]);
17915         //var arrow = this.el.select('.arrow',true).first();
17916         //arrow.set(align[2], 
17917         
17918         this.el.addClass('in');
17919         
17920         
17921         if (this.el.hasClass('fade')) {
17922             // fade it?
17923         }
17924         
17925         this.hoverState = 'in';
17926         
17927         this.fireEvent('show', this);
17928         
17929     },
17930     hide : function()
17931     {
17932         this.el.setXY([0,0]);
17933         this.el.removeClass('in');
17934         this.el.hide();
17935         this.hoverState = null;
17936         
17937         this.fireEvent('hide', this);
17938     }
17939     
17940 });
17941
17942 Roo.bootstrap.Popover.alignment = {
17943     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17944     'right' : ['l-r', [10,0], 'left bs-popover-left'],
17945     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17946     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17947 };
17948
17949  /*
17950  * - LGPL
17951  *
17952  * Progress
17953  * 
17954  */
17955
17956 /**
17957  * @class Roo.bootstrap.Progress
17958  * @extends Roo.bootstrap.Component
17959  * Bootstrap Progress class
17960  * @cfg {Boolean} striped striped of the progress bar
17961  * @cfg {Boolean} active animated of the progress bar
17962  * 
17963  * 
17964  * @constructor
17965  * Create a new Progress
17966  * @param {Object} config The config object
17967  */
17968
17969 Roo.bootstrap.Progress = function(config){
17970     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17971 };
17972
17973 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17974     
17975     striped : false,
17976     active: false,
17977     
17978     getAutoCreate : function(){
17979         var cfg = {
17980             tag: 'div',
17981             cls: 'progress'
17982         };
17983         
17984         
17985         if(this.striped){
17986             cfg.cls += ' progress-striped';
17987         }
17988       
17989         if(this.active){
17990             cfg.cls += ' active';
17991         }
17992         
17993         
17994         return cfg;
17995     }
17996    
17997 });
17998
17999  
18000
18001  /*
18002  * - LGPL
18003  *
18004  * ProgressBar
18005  * 
18006  */
18007
18008 /**
18009  * @class Roo.bootstrap.ProgressBar
18010  * @extends Roo.bootstrap.Component
18011  * Bootstrap ProgressBar class
18012  * @cfg {Number} aria_valuenow aria-value now
18013  * @cfg {Number} aria_valuemin aria-value min
18014  * @cfg {Number} aria_valuemax aria-value max
18015  * @cfg {String} label label for the progress bar
18016  * @cfg {String} panel (success | info | warning | danger )
18017  * @cfg {String} role role of the progress bar
18018  * @cfg {String} sr_only text
18019  * 
18020  * 
18021  * @constructor
18022  * Create a new ProgressBar
18023  * @param {Object} config The config object
18024  */
18025
18026 Roo.bootstrap.ProgressBar = function(config){
18027     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18028 };
18029
18030 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18031     
18032     aria_valuenow : 0,
18033     aria_valuemin : 0,
18034     aria_valuemax : 100,
18035     label : false,
18036     panel : false,
18037     role : false,
18038     sr_only: false,
18039     
18040     getAutoCreate : function()
18041     {
18042         
18043         var cfg = {
18044             tag: 'div',
18045             cls: 'progress-bar',
18046             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18047         };
18048         
18049         if(this.sr_only){
18050             cfg.cn = {
18051                 tag: 'span',
18052                 cls: 'sr-only',
18053                 html: this.sr_only
18054             }
18055         }
18056         
18057         if(this.role){
18058             cfg.role = this.role;
18059         }
18060         
18061         if(this.aria_valuenow){
18062             cfg['aria-valuenow'] = this.aria_valuenow;
18063         }
18064         
18065         if(this.aria_valuemin){
18066             cfg['aria-valuemin'] = this.aria_valuemin;
18067         }
18068         
18069         if(this.aria_valuemax){
18070             cfg['aria-valuemax'] = this.aria_valuemax;
18071         }
18072         
18073         if(this.label && !this.sr_only){
18074             cfg.html = this.label;
18075         }
18076         
18077         if(this.panel){
18078             cfg.cls += ' progress-bar-' + this.panel;
18079         }
18080         
18081         return cfg;
18082     },
18083     
18084     update : function(aria_valuenow)
18085     {
18086         this.aria_valuenow = aria_valuenow;
18087         
18088         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18089     }
18090    
18091 });
18092
18093  
18094
18095  /*
18096  * - LGPL
18097  *
18098  * column
18099  * 
18100  */
18101
18102 /**
18103  * @class Roo.bootstrap.TabGroup
18104  * @extends Roo.bootstrap.Column
18105  * Bootstrap Column class
18106  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18107  * @cfg {Boolean} carousel true to make the group behave like a carousel
18108  * @cfg {Boolean} bullets show bullets for the panels
18109  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18110  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18111  * @cfg {Boolean} showarrow (true|false) show arrow default true
18112  * 
18113  * @constructor
18114  * Create a new TabGroup
18115  * @param {Object} config The config object
18116  */
18117
18118 Roo.bootstrap.TabGroup = function(config){
18119     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18120     if (!this.navId) {
18121         this.navId = Roo.id();
18122     }
18123     this.tabs = [];
18124     Roo.bootstrap.TabGroup.register(this);
18125     
18126 };
18127
18128 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18129     
18130     carousel : false,
18131     transition : false,
18132     bullets : 0,
18133     timer : 0,
18134     autoslide : false,
18135     slideFn : false,
18136     slideOnTouch : false,
18137     showarrow : true,
18138     
18139     getAutoCreate : function()
18140     {
18141         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18142         
18143         cfg.cls += ' tab-content';
18144         
18145         if (this.carousel) {
18146             cfg.cls += ' carousel slide';
18147             
18148             cfg.cn = [{
18149                cls : 'carousel-inner',
18150                cn : []
18151             }];
18152         
18153             if(this.bullets  && !Roo.isTouch){
18154                 
18155                 var bullets = {
18156                     cls : 'carousel-bullets',
18157                     cn : []
18158                 };
18159                
18160                 if(this.bullets_cls){
18161                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18162                 }
18163                 
18164                 bullets.cn.push({
18165                     cls : 'clear'
18166                 });
18167                 
18168                 cfg.cn[0].cn.push(bullets);
18169             }
18170             
18171             if(this.showarrow){
18172                 cfg.cn[0].cn.push({
18173                     tag : 'div',
18174                     class : 'carousel-arrow',
18175                     cn : [
18176                         {
18177                             tag : 'div',
18178                             class : 'carousel-prev',
18179                             cn : [
18180                                 {
18181                                     tag : 'i',
18182                                     class : 'fa fa-chevron-left'
18183                                 }
18184                             ]
18185                         },
18186                         {
18187                             tag : 'div',
18188                             class : 'carousel-next',
18189                             cn : [
18190                                 {
18191                                     tag : 'i',
18192                                     class : 'fa fa-chevron-right'
18193                                 }
18194                             ]
18195                         }
18196                     ]
18197                 });
18198             }
18199             
18200         }
18201         
18202         return cfg;
18203     },
18204     
18205     initEvents:  function()
18206     {
18207 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18208 //            this.el.on("touchstart", this.onTouchStart, this);
18209 //        }
18210         
18211         if(this.autoslide){
18212             var _this = this;
18213             
18214             this.slideFn = window.setInterval(function() {
18215                 _this.showPanelNext();
18216             }, this.timer);
18217         }
18218         
18219         if(this.showarrow){
18220             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18221             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18222         }
18223         
18224         
18225     },
18226     
18227 //    onTouchStart : function(e, el, o)
18228 //    {
18229 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18230 //            return;
18231 //        }
18232 //        
18233 //        this.showPanelNext();
18234 //    },
18235     
18236     
18237     getChildContainer : function()
18238     {
18239         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18240     },
18241     
18242     /**
18243     * register a Navigation item
18244     * @param {Roo.bootstrap.NavItem} the navitem to add
18245     */
18246     register : function(item)
18247     {
18248         this.tabs.push( item);
18249         item.navId = this.navId; // not really needed..
18250         this.addBullet();
18251     
18252     },
18253     
18254     getActivePanel : function()
18255     {
18256         var r = false;
18257         Roo.each(this.tabs, function(t) {
18258             if (t.active) {
18259                 r = t;
18260                 return false;
18261             }
18262             return null;
18263         });
18264         return r;
18265         
18266     },
18267     getPanelByName : function(n)
18268     {
18269         var r = false;
18270         Roo.each(this.tabs, function(t) {
18271             if (t.tabId == n) {
18272                 r = t;
18273                 return false;
18274             }
18275             return null;
18276         });
18277         return r;
18278     },
18279     indexOfPanel : function(p)
18280     {
18281         var r = false;
18282         Roo.each(this.tabs, function(t,i) {
18283             if (t.tabId == p.tabId) {
18284                 r = i;
18285                 return false;
18286             }
18287             return null;
18288         });
18289         return r;
18290     },
18291     /**
18292      * show a specific panel
18293      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18294      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18295      */
18296     showPanel : function (pan)
18297     {
18298         if(this.transition || typeof(pan) == 'undefined'){
18299             Roo.log("waiting for the transitionend");
18300             return;
18301         }
18302         
18303         if (typeof(pan) == 'number') {
18304             pan = this.tabs[pan];
18305         }
18306         
18307         if (typeof(pan) == 'string') {
18308             pan = this.getPanelByName(pan);
18309         }
18310         
18311         var cur = this.getActivePanel();
18312         
18313         if(!pan || !cur){
18314             Roo.log('pan or acitve pan is undefined');
18315             return false;
18316         }
18317         
18318         if (pan.tabId == this.getActivePanel().tabId) {
18319             return true;
18320         }
18321         
18322         if (false === cur.fireEvent('beforedeactivate')) {
18323             return false;
18324         }
18325         
18326         if(this.bullets > 0 && !Roo.isTouch){
18327             this.setActiveBullet(this.indexOfPanel(pan));
18328         }
18329         
18330         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18331             
18332             this.transition = true;
18333             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18334             var lr = dir == 'next' ? 'left' : 'right';
18335             pan.el.addClass(dir); // or prev
18336             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18337             cur.el.addClass(lr); // or right
18338             pan.el.addClass(lr);
18339             
18340             var _this = this;
18341             cur.el.on('transitionend', function() {
18342                 Roo.log("trans end?");
18343                 
18344                 pan.el.removeClass([lr,dir]);
18345                 pan.setActive(true);
18346                 
18347                 cur.el.removeClass([lr]);
18348                 cur.setActive(false);
18349                 
18350                 _this.transition = false;
18351                 
18352             }, this, { single:  true } );
18353             
18354             return true;
18355         }
18356         
18357         cur.setActive(false);
18358         pan.setActive(true);
18359         
18360         return true;
18361         
18362     },
18363     showPanelNext : function()
18364     {
18365         var i = this.indexOfPanel(this.getActivePanel());
18366         
18367         if (i >= this.tabs.length - 1 && !this.autoslide) {
18368             return;
18369         }
18370         
18371         if (i >= this.tabs.length - 1 && this.autoslide) {
18372             i = -1;
18373         }
18374         
18375         this.showPanel(this.tabs[i+1]);
18376     },
18377     
18378     showPanelPrev : function()
18379     {
18380         var i = this.indexOfPanel(this.getActivePanel());
18381         
18382         if (i  < 1 && !this.autoslide) {
18383             return;
18384         }
18385         
18386         if (i < 1 && this.autoslide) {
18387             i = this.tabs.length;
18388         }
18389         
18390         this.showPanel(this.tabs[i-1]);
18391     },
18392     
18393     
18394     addBullet: function()
18395     {
18396         if(!this.bullets || Roo.isTouch){
18397             return;
18398         }
18399         var ctr = this.el.select('.carousel-bullets',true).first();
18400         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18401         var bullet = ctr.createChild({
18402             cls : 'bullet bullet-' + i
18403         },ctr.dom.lastChild);
18404         
18405         
18406         var _this = this;
18407         
18408         bullet.on('click', (function(e, el, o, ii, t){
18409
18410             e.preventDefault();
18411
18412             this.showPanel(ii);
18413
18414             if(this.autoslide && this.slideFn){
18415                 clearInterval(this.slideFn);
18416                 this.slideFn = window.setInterval(function() {
18417                     _this.showPanelNext();
18418                 }, this.timer);
18419             }
18420
18421         }).createDelegate(this, [i, bullet], true));
18422                 
18423         
18424     },
18425      
18426     setActiveBullet : function(i)
18427     {
18428         if(Roo.isTouch){
18429             return;
18430         }
18431         
18432         Roo.each(this.el.select('.bullet', true).elements, function(el){
18433             el.removeClass('selected');
18434         });
18435
18436         var bullet = this.el.select('.bullet-' + i, true).first();
18437         
18438         if(!bullet){
18439             return;
18440         }
18441         
18442         bullet.addClass('selected');
18443     }
18444     
18445     
18446   
18447 });
18448
18449  
18450
18451  
18452  
18453 Roo.apply(Roo.bootstrap.TabGroup, {
18454     
18455     groups: {},
18456      /**
18457     * register a Navigation Group
18458     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18459     */
18460     register : function(navgrp)
18461     {
18462         this.groups[navgrp.navId] = navgrp;
18463         
18464     },
18465     /**
18466     * fetch a Navigation Group based on the navigation ID
18467     * if one does not exist , it will get created.
18468     * @param {string} the navgroup to add
18469     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18470     */
18471     get: function(navId) {
18472         if (typeof(this.groups[navId]) == 'undefined') {
18473             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18474         }
18475         return this.groups[navId] ;
18476     }
18477     
18478     
18479     
18480 });
18481
18482  /*
18483  * - LGPL
18484  *
18485  * TabPanel
18486  * 
18487  */
18488
18489 /**
18490  * @class Roo.bootstrap.TabPanel
18491  * @extends Roo.bootstrap.Component
18492  * Bootstrap TabPanel class
18493  * @cfg {Boolean} active panel active
18494  * @cfg {String} html panel content
18495  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18496  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18497  * @cfg {String} href click to link..
18498  * 
18499  * 
18500  * @constructor
18501  * Create a new TabPanel
18502  * @param {Object} config The config object
18503  */
18504
18505 Roo.bootstrap.TabPanel = function(config){
18506     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18507     this.addEvents({
18508         /**
18509              * @event changed
18510              * Fires when the active status changes
18511              * @param {Roo.bootstrap.TabPanel} this
18512              * @param {Boolean} state the new state
18513             
18514          */
18515         'changed': true,
18516         /**
18517              * @event beforedeactivate
18518              * Fires before a tab is de-activated - can be used to do validation on a form.
18519              * @param {Roo.bootstrap.TabPanel} this
18520              * @return {Boolean} false if there is an error
18521             
18522          */
18523         'beforedeactivate': true
18524      });
18525     
18526     this.tabId = this.tabId || Roo.id();
18527   
18528 };
18529
18530 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18531     
18532     active: false,
18533     html: false,
18534     tabId: false,
18535     navId : false,
18536     href : '',
18537     
18538     getAutoCreate : function(){
18539         var cfg = {
18540             tag: 'div',
18541             // item is needed for carousel - not sure if it has any effect otherwise
18542             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18543             html: this.html || ''
18544         };
18545         
18546         if(this.active){
18547             cfg.cls += ' active';
18548         }
18549         
18550         if(this.tabId){
18551             cfg.tabId = this.tabId;
18552         }
18553         
18554         
18555         return cfg;
18556     },
18557     
18558     initEvents:  function()
18559     {
18560         var p = this.parent();
18561         
18562         this.navId = this.navId || p.navId;
18563         
18564         if (typeof(this.navId) != 'undefined') {
18565             // not really needed.. but just in case.. parent should be a NavGroup.
18566             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18567             
18568             tg.register(this);
18569             
18570             var i = tg.tabs.length - 1;
18571             
18572             if(this.active && tg.bullets > 0 && i < tg.bullets){
18573                 tg.setActiveBullet(i);
18574             }
18575         }
18576         
18577         this.el.on('click', this.onClick, this);
18578         
18579         if(Roo.isTouch){
18580             this.el.on("touchstart", this.onTouchStart, this);
18581             this.el.on("touchmove", this.onTouchMove, this);
18582             this.el.on("touchend", this.onTouchEnd, this);
18583         }
18584         
18585     },
18586     
18587     onRender : function(ct, position)
18588     {
18589         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18590     },
18591     
18592     setActive : function(state)
18593     {
18594         Roo.log("panel - set active " + this.tabId + "=" + state);
18595         
18596         this.active = state;
18597         if (!state) {
18598             this.el.removeClass('active');
18599             
18600         } else  if (!this.el.hasClass('active')) {
18601             this.el.addClass('active');
18602         }
18603         
18604         this.fireEvent('changed', this, state);
18605     },
18606     
18607     onClick : function(e)
18608     {
18609         e.preventDefault();
18610         
18611         if(!this.href.length){
18612             return;
18613         }
18614         
18615         window.location.href = this.href;
18616     },
18617     
18618     startX : 0,
18619     startY : 0,
18620     endX : 0,
18621     endY : 0,
18622     swiping : false,
18623     
18624     onTouchStart : function(e)
18625     {
18626         this.swiping = false;
18627         
18628         this.startX = e.browserEvent.touches[0].clientX;
18629         this.startY = e.browserEvent.touches[0].clientY;
18630     },
18631     
18632     onTouchMove : function(e)
18633     {
18634         this.swiping = true;
18635         
18636         this.endX = e.browserEvent.touches[0].clientX;
18637         this.endY = e.browserEvent.touches[0].clientY;
18638     },
18639     
18640     onTouchEnd : function(e)
18641     {
18642         if(!this.swiping){
18643             this.onClick(e);
18644             return;
18645         }
18646         
18647         var tabGroup = this.parent();
18648         
18649         if(this.endX > this.startX){ // swiping right
18650             tabGroup.showPanelPrev();
18651             return;
18652         }
18653         
18654         if(this.startX > this.endX){ // swiping left
18655             tabGroup.showPanelNext();
18656             return;
18657         }
18658     }
18659     
18660     
18661 });
18662  
18663
18664  
18665
18666  /*
18667  * - LGPL
18668  *
18669  * DateField
18670  * 
18671  */
18672
18673 /**
18674  * @class Roo.bootstrap.DateField
18675  * @extends Roo.bootstrap.Input
18676  * Bootstrap DateField class
18677  * @cfg {Number} weekStart default 0
18678  * @cfg {String} viewMode default empty, (months|years)
18679  * @cfg {String} minViewMode default empty, (months|years)
18680  * @cfg {Number} startDate default -Infinity
18681  * @cfg {Number} endDate default Infinity
18682  * @cfg {Boolean} todayHighlight default false
18683  * @cfg {Boolean} todayBtn default false
18684  * @cfg {Boolean} calendarWeeks default false
18685  * @cfg {Object} daysOfWeekDisabled default empty
18686  * @cfg {Boolean} singleMode default false (true | false)
18687  * 
18688  * @cfg {Boolean} keyboardNavigation default true
18689  * @cfg {String} language default en
18690  * 
18691  * @constructor
18692  * Create a new DateField
18693  * @param {Object} config The config object
18694  */
18695
18696 Roo.bootstrap.DateField = function(config){
18697     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18698      this.addEvents({
18699             /**
18700              * @event show
18701              * Fires when this field show.
18702              * @param {Roo.bootstrap.DateField} this
18703              * @param {Mixed} date The date value
18704              */
18705             show : true,
18706             /**
18707              * @event show
18708              * Fires when this field hide.
18709              * @param {Roo.bootstrap.DateField} this
18710              * @param {Mixed} date The date value
18711              */
18712             hide : true,
18713             /**
18714              * @event select
18715              * Fires when select a date.
18716              * @param {Roo.bootstrap.DateField} this
18717              * @param {Mixed} date The date value
18718              */
18719             select : true,
18720             /**
18721              * @event beforeselect
18722              * Fires when before select a date.
18723              * @param {Roo.bootstrap.DateField} this
18724              * @param {Mixed} date The date value
18725              */
18726             beforeselect : true
18727         });
18728 };
18729
18730 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18731     
18732     /**
18733      * @cfg {String} format
18734      * The default date format string which can be overriden for localization support.  The format must be
18735      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18736      */
18737     format : "m/d/y",
18738     /**
18739      * @cfg {String} altFormats
18740      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18741      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18742      */
18743     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18744     
18745     weekStart : 0,
18746     
18747     viewMode : '',
18748     
18749     minViewMode : '',
18750     
18751     todayHighlight : false,
18752     
18753     todayBtn: false,
18754     
18755     language: 'en',
18756     
18757     keyboardNavigation: true,
18758     
18759     calendarWeeks: false,
18760     
18761     startDate: -Infinity,
18762     
18763     endDate: Infinity,
18764     
18765     daysOfWeekDisabled: [],
18766     
18767     _events: [],
18768     
18769     singleMode : false,
18770     
18771     UTCDate: function()
18772     {
18773         return new Date(Date.UTC.apply(Date, arguments));
18774     },
18775     
18776     UTCToday: function()
18777     {
18778         var today = new Date();
18779         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18780     },
18781     
18782     getDate: function() {
18783             var d = this.getUTCDate();
18784             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18785     },
18786     
18787     getUTCDate: function() {
18788             return this.date;
18789     },
18790     
18791     setDate: function(d) {
18792             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18793     },
18794     
18795     setUTCDate: function(d) {
18796             this.date = d;
18797             this.setValue(this.formatDate(this.date));
18798     },
18799         
18800     onRender: function(ct, position)
18801     {
18802         
18803         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18804         
18805         this.language = this.language || 'en';
18806         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18807         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18808         
18809         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18810         this.format = this.format || 'm/d/y';
18811         this.isInline = false;
18812         this.isInput = true;
18813         this.component = this.el.select('.add-on', true).first() || false;
18814         this.component = (this.component && this.component.length === 0) ? false : this.component;
18815         this.hasInput = this.component && this.inputEl().length;
18816         
18817         if (typeof(this.minViewMode === 'string')) {
18818             switch (this.minViewMode) {
18819                 case 'months':
18820                     this.minViewMode = 1;
18821                     break;
18822                 case 'years':
18823                     this.minViewMode = 2;
18824                     break;
18825                 default:
18826                     this.minViewMode = 0;
18827                     break;
18828             }
18829         }
18830         
18831         if (typeof(this.viewMode === 'string')) {
18832             switch (this.viewMode) {
18833                 case 'months':
18834                     this.viewMode = 1;
18835                     break;
18836                 case 'years':
18837                     this.viewMode = 2;
18838                     break;
18839                 default:
18840                     this.viewMode = 0;
18841                     break;
18842             }
18843         }
18844                 
18845         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18846         
18847 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18848         
18849         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18850         
18851         this.picker().on('mousedown', this.onMousedown, this);
18852         this.picker().on('click', this.onClick, this);
18853         
18854         this.picker().addClass('datepicker-dropdown');
18855         
18856         this.startViewMode = this.viewMode;
18857         
18858         if(this.singleMode){
18859             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18860                 v.setVisibilityMode(Roo.Element.DISPLAY);
18861                 v.hide();
18862             });
18863             
18864             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18865                 v.setStyle('width', '189px');
18866             });
18867         }
18868         
18869         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18870             if(!this.calendarWeeks){
18871                 v.remove();
18872                 return;
18873             }
18874             
18875             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18876             v.attr('colspan', function(i, val){
18877                 return parseInt(val) + 1;
18878             });
18879         });
18880                         
18881         
18882         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18883         
18884         this.setStartDate(this.startDate);
18885         this.setEndDate(this.endDate);
18886         
18887         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18888         
18889         this.fillDow();
18890         this.fillMonths();
18891         this.update();
18892         this.showMode();
18893         
18894         if(this.isInline) {
18895             this.showPopup();
18896         }
18897     },
18898     
18899     picker : function()
18900     {
18901         return this.pickerEl;
18902 //        return this.el.select('.datepicker', true).first();
18903     },
18904     
18905     fillDow: function()
18906     {
18907         var dowCnt = this.weekStart;
18908         
18909         var dow = {
18910             tag: 'tr',
18911             cn: [
18912                 
18913             ]
18914         };
18915         
18916         if(this.calendarWeeks){
18917             dow.cn.push({
18918                 tag: 'th',
18919                 cls: 'cw',
18920                 html: '&nbsp;'
18921             })
18922         }
18923         
18924         while (dowCnt < this.weekStart + 7) {
18925             dow.cn.push({
18926                 tag: 'th',
18927                 cls: 'dow',
18928                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18929             });
18930         }
18931         
18932         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18933     },
18934     
18935     fillMonths: function()
18936     {    
18937         var i = 0;
18938         var months = this.picker().select('>.datepicker-months td', true).first();
18939         
18940         months.dom.innerHTML = '';
18941         
18942         while (i < 12) {
18943             var month = {
18944                 tag: 'span',
18945                 cls: 'month',
18946                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18947             };
18948             
18949             months.createChild(month);
18950         }
18951         
18952     },
18953     
18954     update: function()
18955     {
18956         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;
18957         
18958         if (this.date < this.startDate) {
18959             this.viewDate = new Date(this.startDate);
18960         } else if (this.date > this.endDate) {
18961             this.viewDate = new Date(this.endDate);
18962         } else {
18963             this.viewDate = new Date(this.date);
18964         }
18965         
18966         this.fill();
18967     },
18968     
18969     fill: function() 
18970     {
18971         var d = new Date(this.viewDate),
18972                 year = d.getUTCFullYear(),
18973                 month = d.getUTCMonth(),
18974                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18975                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18976                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18977                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18978                 currentDate = this.date && this.date.valueOf(),
18979                 today = this.UTCToday();
18980         
18981         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18982         
18983 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18984         
18985 //        this.picker.select('>tfoot th.today').
18986 //                                              .text(dates[this.language].today)
18987 //                                              .toggle(this.todayBtn !== false);
18988     
18989         this.updateNavArrows();
18990         this.fillMonths();
18991                                                 
18992         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18993         
18994         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18995          
18996         prevMonth.setUTCDate(day);
18997         
18998         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18999         
19000         var nextMonth = new Date(prevMonth);
19001         
19002         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19003         
19004         nextMonth = nextMonth.valueOf();
19005         
19006         var fillMonths = false;
19007         
19008         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19009         
19010         while(prevMonth.valueOf() <= nextMonth) {
19011             var clsName = '';
19012             
19013             if (prevMonth.getUTCDay() === this.weekStart) {
19014                 if(fillMonths){
19015                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19016                 }
19017                     
19018                 fillMonths = {
19019                     tag: 'tr',
19020                     cn: []
19021                 };
19022                 
19023                 if(this.calendarWeeks){
19024                     // ISO 8601: First week contains first thursday.
19025                     // ISO also states week starts on Monday, but we can be more abstract here.
19026                     var
19027                     // Start of current week: based on weekstart/current date
19028                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19029                     // Thursday of this week
19030                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19031                     // First Thursday of year, year from thursday
19032                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19033                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19034                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19035                     
19036                     fillMonths.cn.push({
19037                         tag: 'td',
19038                         cls: 'cw',
19039                         html: calWeek
19040                     });
19041                 }
19042             }
19043             
19044             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19045                 clsName += ' old';
19046             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19047                 clsName += ' new';
19048             }
19049             if (this.todayHighlight &&
19050                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19051                 prevMonth.getUTCMonth() == today.getMonth() &&
19052                 prevMonth.getUTCDate() == today.getDate()) {
19053                 clsName += ' today';
19054             }
19055             
19056             if (currentDate && prevMonth.valueOf() === currentDate) {
19057                 clsName += ' active';
19058             }
19059             
19060             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19061                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19062                     clsName += ' disabled';
19063             }
19064             
19065             fillMonths.cn.push({
19066                 tag: 'td',
19067                 cls: 'day ' + clsName,
19068                 html: prevMonth.getDate()
19069             });
19070             
19071             prevMonth.setDate(prevMonth.getDate()+1);
19072         }
19073           
19074         var currentYear = this.date && this.date.getUTCFullYear();
19075         var currentMonth = this.date && this.date.getUTCMonth();
19076         
19077         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19078         
19079         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19080             v.removeClass('active');
19081             
19082             if(currentYear === year && k === currentMonth){
19083                 v.addClass('active');
19084             }
19085             
19086             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19087                 v.addClass('disabled');
19088             }
19089             
19090         });
19091         
19092         
19093         year = parseInt(year/10, 10) * 10;
19094         
19095         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19096         
19097         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19098         
19099         year -= 1;
19100         for (var i = -1; i < 11; i++) {
19101             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19102                 tag: 'span',
19103                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19104                 html: year
19105             });
19106             
19107             year += 1;
19108         }
19109     },
19110     
19111     showMode: function(dir) 
19112     {
19113         if (dir) {
19114             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19115         }
19116         
19117         Roo.each(this.picker().select('>div',true).elements, function(v){
19118             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19119             v.hide();
19120         });
19121         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19122     },
19123     
19124     place: function()
19125     {
19126         if(this.isInline) {
19127             return;
19128         }
19129         
19130         this.picker().removeClass(['bottom', 'top']);
19131         
19132         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19133             /*
19134              * place to the top of element!
19135              *
19136              */
19137             
19138             this.picker().addClass('top');
19139             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19140             
19141             return;
19142         }
19143         
19144         this.picker().addClass('bottom');
19145         
19146         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19147     },
19148     
19149     parseDate : function(value)
19150     {
19151         if(!value || value instanceof Date){
19152             return value;
19153         }
19154         var v = Date.parseDate(value, this.format);
19155         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19156             v = Date.parseDate(value, 'Y-m-d');
19157         }
19158         if(!v && this.altFormats){
19159             if(!this.altFormatsArray){
19160                 this.altFormatsArray = this.altFormats.split("|");
19161             }
19162             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19163                 v = Date.parseDate(value, this.altFormatsArray[i]);
19164             }
19165         }
19166         return v;
19167     },
19168     
19169     formatDate : function(date, fmt)
19170     {   
19171         return (!date || !(date instanceof Date)) ?
19172         date : date.dateFormat(fmt || this.format);
19173     },
19174     
19175     onFocus : function()
19176     {
19177         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19178         this.showPopup();
19179     },
19180     
19181     onBlur : function()
19182     {
19183         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19184         
19185         var d = this.inputEl().getValue();
19186         
19187         this.setValue(d);
19188                 
19189         this.hidePopup();
19190     },
19191     
19192     showPopup : function()
19193     {
19194         this.picker().show();
19195         this.update();
19196         this.place();
19197         
19198         this.fireEvent('showpopup', this, this.date);
19199     },
19200     
19201     hidePopup : function()
19202     {
19203         if(this.isInline) {
19204             return;
19205         }
19206         this.picker().hide();
19207         this.viewMode = this.startViewMode;
19208         this.showMode();
19209         
19210         this.fireEvent('hidepopup', this, this.date);
19211         
19212     },
19213     
19214     onMousedown: function(e)
19215     {
19216         e.stopPropagation();
19217         e.preventDefault();
19218     },
19219     
19220     keyup: function(e)
19221     {
19222         Roo.bootstrap.DateField.superclass.keyup.call(this);
19223         this.update();
19224     },
19225
19226     setValue: function(v)
19227     {
19228         if(this.fireEvent('beforeselect', this, v) !== false){
19229             var d = new Date(this.parseDate(v) ).clearTime();
19230         
19231             if(isNaN(d.getTime())){
19232                 this.date = this.viewDate = '';
19233                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19234                 return;
19235             }
19236
19237             v = this.formatDate(d);
19238
19239             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19240
19241             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19242
19243             this.update();
19244
19245             this.fireEvent('select', this, this.date);
19246         }
19247     },
19248     
19249     getValue: function()
19250     {
19251         return this.formatDate(this.date);
19252     },
19253     
19254     fireKey: function(e)
19255     {
19256         if (!this.picker().isVisible()){
19257             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19258                 this.showPopup();
19259             }
19260             return;
19261         }
19262         
19263         var dateChanged = false,
19264         dir, day, month,
19265         newDate, newViewDate;
19266         
19267         switch(e.keyCode){
19268             case 27: // escape
19269                 this.hidePopup();
19270                 e.preventDefault();
19271                 break;
19272             case 37: // left
19273             case 39: // right
19274                 if (!this.keyboardNavigation) {
19275                     break;
19276                 }
19277                 dir = e.keyCode == 37 ? -1 : 1;
19278                 
19279                 if (e.ctrlKey){
19280                     newDate = this.moveYear(this.date, dir);
19281                     newViewDate = this.moveYear(this.viewDate, dir);
19282                 } else if (e.shiftKey){
19283                     newDate = this.moveMonth(this.date, dir);
19284                     newViewDate = this.moveMonth(this.viewDate, dir);
19285                 } else {
19286                     newDate = new Date(this.date);
19287                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19288                     newViewDate = new Date(this.viewDate);
19289                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19290                 }
19291                 if (this.dateWithinRange(newDate)){
19292                     this.date = newDate;
19293                     this.viewDate = newViewDate;
19294                     this.setValue(this.formatDate(this.date));
19295 //                    this.update();
19296                     e.preventDefault();
19297                     dateChanged = true;
19298                 }
19299                 break;
19300             case 38: // up
19301             case 40: // down
19302                 if (!this.keyboardNavigation) {
19303                     break;
19304                 }
19305                 dir = e.keyCode == 38 ? -1 : 1;
19306                 if (e.ctrlKey){
19307                     newDate = this.moveYear(this.date, dir);
19308                     newViewDate = this.moveYear(this.viewDate, dir);
19309                 } else if (e.shiftKey){
19310                     newDate = this.moveMonth(this.date, dir);
19311                     newViewDate = this.moveMonth(this.viewDate, dir);
19312                 } else {
19313                     newDate = new Date(this.date);
19314                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19315                     newViewDate = new Date(this.viewDate);
19316                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19317                 }
19318                 if (this.dateWithinRange(newDate)){
19319                     this.date = newDate;
19320                     this.viewDate = newViewDate;
19321                     this.setValue(this.formatDate(this.date));
19322 //                    this.update();
19323                     e.preventDefault();
19324                     dateChanged = true;
19325                 }
19326                 break;
19327             case 13: // enter
19328                 this.setValue(this.formatDate(this.date));
19329                 this.hidePopup();
19330                 e.preventDefault();
19331                 break;
19332             case 9: // tab
19333                 this.setValue(this.formatDate(this.date));
19334                 this.hidePopup();
19335                 break;
19336             case 16: // shift
19337             case 17: // ctrl
19338             case 18: // alt
19339                 break;
19340             default :
19341                 this.hidePopup();
19342                 
19343         }
19344     },
19345     
19346     
19347     onClick: function(e) 
19348     {
19349         e.stopPropagation();
19350         e.preventDefault();
19351         
19352         var target = e.getTarget();
19353         
19354         if(target.nodeName.toLowerCase() === 'i'){
19355             target = Roo.get(target).dom.parentNode;
19356         }
19357         
19358         var nodeName = target.nodeName;
19359         var className = target.className;
19360         var html = target.innerHTML;
19361         //Roo.log(nodeName);
19362         
19363         switch(nodeName.toLowerCase()) {
19364             case 'th':
19365                 switch(className) {
19366                     case 'switch':
19367                         this.showMode(1);
19368                         break;
19369                     case 'prev':
19370                     case 'next':
19371                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19372                         switch(this.viewMode){
19373                                 case 0:
19374                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19375                                         break;
19376                                 case 1:
19377                                 case 2:
19378                                         this.viewDate = this.moveYear(this.viewDate, dir);
19379                                         break;
19380                         }
19381                         this.fill();
19382                         break;
19383                     case 'today':
19384                         var date = new Date();
19385                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19386 //                        this.fill()
19387                         this.setValue(this.formatDate(this.date));
19388                         
19389                         this.hidePopup();
19390                         break;
19391                 }
19392                 break;
19393             case 'span':
19394                 if (className.indexOf('disabled') < 0) {
19395                     this.viewDate.setUTCDate(1);
19396                     if (className.indexOf('month') > -1) {
19397                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19398                     } else {
19399                         var year = parseInt(html, 10) || 0;
19400                         this.viewDate.setUTCFullYear(year);
19401                         
19402                     }
19403                     
19404                     if(this.singleMode){
19405                         this.setValue(this.formatDate(this.viewDate));
19406                         this.hidePopup();
19407                         return;
19408                     }
19409                     
19410                     this.showMode(-1);
19411                     this.fill();
19412                 }
19413                 break;
19414                 
19415             case 'td':
19416                 //Roo.log(className);
19417                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19418                     var day = parseInt(html, 10) || 1;
19419                     var year = this.viewDate.getUTCFullYear(),
19420                         month = this.viewDate.getUTCMonth();
19421
19422                     if (className.indexOf('old') > -1) {
19423                         if(month === 0 ){
19424                             month = 11;
19425                             year -= 1;
19426                         }else{
19427                             month -= 1;
19428                         }
19429                     } else if (className.indexOf('new') > -1) {
19430                         if (month == 11) {
19431                             month = 0;
19432                             year += 1;
19433                         } else {
19434                             month += 1;
19435                         }
19436                     }
19437                     //Roo.log([year,month,day]);
19438                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19439                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19440 //                    this.fill();
19441                     //Roo.log(this.formatDate(this.date));
19442                     this.setValue(this.formatDate(this.date));
19443                     this.hidePopup();
19444                 }
19445                 break;
19446         }
19447     },
19448     
19449     setStartDate: function(startDate)
19450     {
19451         this.startDate = startDate || -Infinity;
19452         if (this.startDate !== -Infinity) {
19453             this.startDate = this.parseDate(this.startDate);
19454         }
19455         this.update();
19456         this.updateNavArrows();
19457     },
19458
19459     setEndDate: function(endDate)
19460     {
19461         this.endDate = endDate || Infinity;
19462         if (this.endDate !== Infinity) {
19463             this.endDate = this.parseDate(this.endDate);
19464         }
19465         this.update();
19466         this.updateNavArrows();
19467     },
19468     
19469     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19470     {
19471         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19472         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19473             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19474         }
19475         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19476             return parseInt(d, 10);
19477         });
19478         this.update();
19479         this.updateNavArrows();
19480     },
19481     
19482     updateNavArrows: function() 
19483     {
19484         if(this.singleMode){
19485             return;
19486         }
19487         
19488         var d = new Date(this.viewDate),
19489         year = d.getUTCFullYear(),
19490         month = d.getUTCMonth();
19491         
19492         Roo.each(this.picker().select('.prev', true).elements, function(v){
19493             v.show();
19494             switch (this.viewMode) {
19495                 case 0:
19496
19497                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19498                         v.hide();
19499                     }
19500                     break;
19501                 case 1:
19502                 case 2:
19503                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19504                         v.hide();
19505                     }
19506                     break;
19507             }
19508         });
19509         
19510         Roo.each(this.picker().select('.next', true).elements, function(v){
19511             v.show();
19512             switch (this.viewMode) {
19513                 case 0:
19514
19515                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19516                         v.hide();
19517                     }
19518                     break;
19519                 case 1:
19520                 case 2:
19521                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19522                         v.hide();
19523                     }
19524                     break;
19525             }
19526         })
19527     },
19528     
19529     moveMonth: function(date, dir)
19530     {
19531         if (!dir) {
19532             return date;
19533         }
19534         var new_date = new Date(date.valueOf()),
19535         day = new_date.getUTCDate(),
19536         month = new_date.getUTCMonth(),
19537         mag = Math.abs(dir),
19538         new_month, test;
19539         dir = dir > 0 ? 1 : -1;
19540         if (mag == 1){
19541             test = dir == -1
19542             // If going back one month, make sure month is not current month
19543             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19544             ? function(){
19545                 return new_date.getUTCMonth() == month;
19546             }
19547             // If going forward one month, make sure month is as expected
19548             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19549             : function(){
19550                 return new_date.getUTCMonth() != new_month;
19551             };
19552             new_month = month + dir;
19553             new_date.setUTCMonth(new_month);
19554             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19555             if (new_month < 0 || new_month > 11) {
19556                 new_month = (new_month + 12) % 12;
19557             }
19558         } else {
19559             // For magnitudes >1, move one month at a time...
19560             for (var i=0; i<mag; i++) {
19561                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19562                 new_date = this.moveMonth(new_date, dir);
19563             }
19564             // ...then reset the day, keeping it in the new month
19565             new_month = new_date.getUTCMonth();
19566             new_date.setUTCDate(day);
19567             test = function(){
19568                 return new_month != new_date.getUTCMonth();
19569             };
19570         }
19571         // Common date-resetting loop -- if date is beyond end of month, make it
19572         // end of month
19573         while (test()){
19574             new_date.setUTCDate(--day);
19575             new_date.setUTCMonth(new_month);
19576         }
19577         return new_date;
19578     },
19579
19580     moveYear: function(date, dir)
19581     {
19582         return this.moveMonth(date, dir*12);
19583     },
19584
19585     dateWithinRange: function(date)
19586     {
19587         return date >= this.startDate && date <= this.endDate;
19588     },
19589
19590     
19591     remove: function() 
19592     {
19593         this.picker().remove();
19594     },
19595     
19596     validateValue : function(value)
19597     {
19598         if(this.getVisibilityEl().hasClass('hidden')){
19599             return true;
19600         }
19601         
19602         if(value.length < 1)  {
19603             if(this.allowBlank){
19604                 return true;
19605             }
19606             return false;
19607         }
19608         
19609         if(value.length < this.minLength){
19610             return false;
19611         }
19612         if(value.length > this.maxLength){
19613             return false;
19614         }
19615         if(this.vtype){
19616             var vt = Roo.form.VTypes;
19617             if(!vt[this.vtype](value, this)){
19618                 return false;
19619             }
19620         }
19621         if(typeof this.validator == "function"){
19622             var msg = this.validator(value);
19623             if(msg !== true){
19624                 return false;
19625             }
19626         }
19627         
19628         if(this.regex && !this.regex.test(value)){
19629             return false;
19630         }
19631         
19632         if(typeof(this.parseDate(value)) == 'undefined'){
19633             return false;
19634         }
19635         
19636         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19637             return false;
19638         }      
19639         
19640         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19641             return false;
19642         } 
19643         
19644         
19645         return true;
19646     },
19647     
19648     reset : function()
19649     {
19650         this.date = this.viewDate = '';
19651         
19652         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19653     }
19654    
19655 });
19656
19657 Roo.apply(Roo.bootstrap.DateField,  {
19658     
19659     head : {
19660         tag: 'thead',
19661         cn: [
19662         {
19663             tag: 'tr',
19664             cn: [
19665             {
19666                 tag: 'th',
19667                 cls: 'prev',
19668                 html: '<i class="fa fa-arrow-left"/>'
19669             },
19670             {
19671                 tag: 'th',
19672                 cls: 'switch',
19673                 colspan: '5'
19674             },
19675             {
19676                 tag: 'th',
19677                 cls: 'next',
19678                 html: '<i class="fa fa-arrow-right"/>'
19679             }
19680
19681             ]
19682         }
19683         ]
19684     },
19685     
19686     content : {
19687         tag: 'tbody',
19688         cn: [
19689         {
19690             tag: 'tr',
19691             cn: [
19692             {
19693                 tag: 'td',
19694                 colspan: '7'
19695             }
19696             ]
19697         }
19698         ]
19699     },
19700     
19701     footer : {
19702         tag: 'tfoot',
19703         cn: [
19704         {
19705             tag: 'tr',
19706             cn: [
19707             {
19708                 tag: 'th',
19709                 colspan: '7',
19710                 cls: 'today'
19711             }
19712                     
19713             ]
19714         }
19715         ]
19716     },
19717     
19718     dates:{
19719         en: {
19720             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19721             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19722             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19723             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19724             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19725             today: "Today"
19726         }
19727     },
19728     
19729     modes: [
19730     {
19731         clsName: 'days',
19732         navFnc: 'Month',
19733         navStep: 1
19734     },
19735     {
19736         clsName: 'months',
19737         navFnc: 'FullYear',
19738         navStep: 1
19739     },
19740     {
19741         clsName: 'years',
19742         navFnc: 'FullYear',
19743         navStep: 10
19744     }]
19745 });
19746
19747 Roo.apply(Roo.bootstrap.DateField,  {
19748   
19749     template : {
19750         tag: 'div',
19751         cls: 'datepicker dropdown-menu roo-dynamic',
19752         cn: [
19753         {
19754             tag: 'div',
19755             cls: 'datepicker-days',
19756             cn: [
19757             {
19758                 tag: 'table',
19759                 cls: 'table-condensed',
19760                 cn:[
19761                 Roo.bootstrap.DateField.head,
19762                 {
19763                     tag: 'tbody'
19764                 },
19765                 Roo.bootstrap.DateField.footer
19766                 ]
19767             }
19768             ]
19769         },
19770         {
19771             tag: 'div',
19772             cls: 'datepicker-months',
19773             cn: [
19774             {
19775                 tag: 'table',
19776                 cls: 'table-condensed',
19777                 cn:[
19778                 Roo.bootstrap.DateField.head,
19779                 Roo.bootstrap.DateField.content,
19780                 Roo.bootstrap.DateField.footer
19781                 ]
19782             }
19783             ]
19784         },
19785         {
19786             tag: 'div',
19787             cls: 'datepicker-years',
19788             cn: [
19789             {
19790                 tag: 'table',
19791                 cls: 'table-condensed',
19792                 cn:[
19793                 Roo.bootstrap.DateField.head,
19794                 Roo.bootstrap.DateField.content,
19795                 Roo.bootstrap.DateField.footer
19796                 ]
19797             }
19798             ]
19799         }
19800         ]
19801     }
19802 });
19803
19804  
19805
19806  /*
19807  * - LGPL
19808  *
19809  * TimeField
19810  * 
19811  */
19812
19813 /**
19814  * @class Roo.bootstrap.TimeField
19815  * @extends Roo.bootstrap.Input
19816  * Bootstrap DateField class
19817  * 
19818  * 
19819  * @constructor
19820  * Create a new TimeField
19821  * @param {Object} config The config object
19822  */
19823
19824 Roo.bootstrap.TimeField = function(config){
19825     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19826     this.addEvents({
19827             /**
19828              * @event show
19829              * Fires when this field show.
19830              * @param {Roo.bootstrap.DateField} thisthis
19831              * @param {Mixed} date The date value
19832              */
19833             show : true,
19834             /**
19835              * @event show
19836              * Fires when this field hide.
19837              * @param {Roo.bootstrap.DateField} this
19838              * @param {Mixed} date The date value
19839              */
19840             hide : true,
19841             /**
19842              * @event select
19843              * Fires when select a date.
19844              * @param {Roo.bootstrap.DateField} this
19845              * @param {Mixed} date The date value
19846              */
19847             select : true
19848         });
19849 };
19850
19851 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19852     
19853     /**
19854      * @cfg {String} format
19855      * The default time format string which can be overriden for localization support.  The format must be
19856      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19857      */
19858     format : "H:i",
19859        
19860     onRender: function(ct, position)
19861     {
19862         
19863         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19864                 
19865         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19866         
19867         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19868         
19869         this.pop = this.picker().select('>.datepicker-time',true).first();
19870         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19871         
19872         this.picker().on('mousedown', this.onMousedown, this);
19873         this.picker().on('click', this.onClick, this);
19874         
19875         this.picker().addClass('datepicker-dropdown');
19876     
19877         this.fillTime();
19878         this.update();
19879             
19880         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19881         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19882         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19883         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19884         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19885         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19886
19887     },
19888     
19889     fireKey: function(e){
19890         if (!this.picker().isVisible()){
19891             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19892                 this.show();
19893             }
19894             return;
19895         }
19896
19897         e.preventDefault();
19898         
19899         switch(e.keyCode){
19900             case 27: // escape
19901                 this.hide();
19902                 break;
19903             case 37: // left
19904             case 39: // right
19905                 this.onTogglePeriod();
19906                 break;
19907             case 38: // up
19908                 this.onIncrementMinutes();
19909                 break;
19910             case 40: // down
19911                 this.onDecrementMinutes();
19912                 break;
19913             case 13: // enter
19914             case 9: // tab
19915                 this.setTime();
19916                 break;
19917         }
19918     },
19919     
19920     onClick: function(e) {
19921         e.stopPropagation();
19922         e.preventDefault();
19923     },
19924     
19925     picker : function()
19926     {
19927         return this.el.select('.datepicker', true).first();
19928     },
19929     
19930     fillTime: function()
19931     {    
19932         var time = this.pop.select('tbody', true).first();
19933         
19934         time.dom.innerHTML = '';
19935         
19936         time.createChild({
19937             tag: 'tr',
19938             cn: [
19939                 {
19940                     tag: 'td',
19941                     cn: [
19942                         {
19943                             tag: 'a',
19944                             href: '#',
19945                             cls: 'btn',
19946                             cn: [
19947                                 {
19948                                     tag: 'span',
19949                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19950                                 }
19951                             ]
19952                         } 
19953                     ]
19954                 },
19955                 {
19956                     tag: 'td',
19957                     cls: 'separator'
19958                 },
19959                 {
19960                     tag: 'td',
19961                     cn: [
19962                         {
19963                             tag: 'a',
19964                             href: '#',
19965                             cls: 'btn',
19966                             cn: [
19967                                 {
19968                                     tag: 'span',
19969                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19970                                 }
19971                             ]
19972                         }
19973                     ]
19974                 },
19975                 {
19976                     tag: 'td',
19977                     cls: 'separator'
19978                 }
19979             ]
19980         });
19981         
19982         time.createChild({
19983             tag: 'tr',
19984             cn: [
19985                 {
19986                     tag: 'td',
19987                     cn: [
19988                         {
19989                             tag: 'span',
19990                             cls: 'timepicker-hour',
19991                             html: '00'
19992                         }  
19993                     ]
19994                 },
19995                 {
19996                     tag: 'td',
19997                     cls: 'separator',
19998                     html: ':'
19999                 },
20000                 {
20001                     tag: 'td',
20002                     cn: [
20003                         {
20004                             tag: 'span',
20005                             cls: 'timepicker-minute',
20006                             html: '00'
20007                         }  
20008                     ]
20009                 },
20010                 {
20011                     tag: 'td',
20012                     cls: 'separator'
20013                 },
20014                 {
20015                     tag: 'td',
20016                     cn: [
20017                         {
20018                             tag: 'button',
20019                             type: 'button',
20020                             cls: 'btn btn-primary period',
20021                             html: 'AM'
20022                             
20023                         }
20024                     ]
20025                 }
20026             ]
20027         });
20028         
20029         time.createChild({
20030             tag: 'tr',
20031             cn: [
20032                 {
20033                     tag: 'td',
20034                     cn: [
20035                         {
20036                             tag: 'a',
20037                             href: '#',
20038                             cls: 'btn',
20039                             cn: [
20040                                 {
20041                                     tag: 'span',
20042                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20043                                 }
20044                             ]
20045                         }
20046                     ]
20047                 },
20048                 {
20049                     tag: 'td',
20050                     cls: 'separator'
20051                 },
20052                 {
20053                     tag: 'td',
20054                     cn: [
20055                         {
20056                             tag: 'a',
20057                             href: '#',
20058                             cls: 'btn',
20059                             cn: [
20060                                 {
20061                                     tag: 'span',
20062                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20063                                 }
20064                             ]
20065                         }
20066                     ]
20067                 },
20068                 {
20069                     tag: 'td',
20070                     cls: 'separator'
20071                 }
20072             ]
20073         });
20074         
20075     },
20076     
20077     update: function()
20078     {
20079         
20080         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20081         
20082         this.fill();
20083     },
20084     
20085     fill: function() 
20086     {
20087         var hours = this.time.getHours();
20088         var minutes = this.time.getMinutes();
20089         var period = 'AM';
20090         
20091         if(hours > 11){
20092             period = 'PM';
20093         }
20094         
20095         if(hours == 0){
20096             hours = 12;
20097         }
20098         
20099         
20100         if(hours > 12){
20101             hours = hours - 12;
20102         }
20103         
20104         if(hours < 10){
20105             hours = '0' + hours;
20106         }
20107         
20108         if(minutes < 10){
20109             minutes = '0' + minutes;
20110         }
20111         
20112         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20113         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20114         this.pop.select('button', true).first().dom.innerHTML = period;
20115         
20116     },
20117     
20118     place: function()
20119     {   
20120         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20121         
20122         var cls = ['bottom'];
20123         
20124         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20125             cls.pop();
20126             cls.push('top');
20127         }
20128         
20129         cls.push('right');
20130         
20131         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20132             cls.pop();
20133             cls.push('left');
20134         }
20135         
20136         this.picker().addClass(cls.join('-'));
20137         
20138         var _this = this;
20139         
20140         Roo.each(cls, function(c){
20141             if(c == 'bottom'){
20142                 _this.picker().setTop(_this.inputEl().getHeight());
20143                 return;
20144             }
20145             if(c == 'top'){
20146                 _this.picker().setTop(0 - _this.picker().getHeight());
20147                 return;
20148             }
20149             
20150             if(c == 'left'){
20151                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20152                 return;
20153             }
20154             if(c == 'right'){
20155                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20156                 return;
20157             }
20158         });
20159         
20160     },
20161   
20162     onFocus : function()
20163     {
20164         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20165         this.show();
20166     },
20167     
20168     onBlur : function()
20169     {
20170         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20171         this.hide();
20172     },
20173     
20174     show : function()
20175     {
20176         this.picker().show();
20177         this.pop.show();
20178         this.update();
20179         this.place();
20180         
20181         this.fireEvent('show', this, this.date);
20182     },
20183     
20184     hide : function()
20185     {
20186         this.picker().hide();
20187         this.pop.hide();
20188         
20189         this.fireEvent('hide', this, this.date);
20190     },
20191     
20192     setTime : function()
20193     {
20194         this.hide();
20195         this.setValue(this.time.format(this.format));
20196         
20197         this.fireEvent('select', this, this.date);
20198         
20199         
20200     },
20201     
20202     onMousedown: function(e){
20203         e.stopPropagation();
20204         e.preventDefault();
20205     },
20206     
20207     onIncrementHours: function()
20208     {
20209         Roo.log('onIncrementHours');
20210         this.time = this.time.add(Date.HOUR, 1);
20211         this.update();
20212         
20213     },
20214     
20215     onDecrementHours: function()
20216     {
20217         Roo.log('onDecrementHours');
20218         this.time = this.time.add(Date.HOUR, -1);
20219         this.update();
20220     },
20221     
20222     onIncrementMinutes: function()
20223     {
20224         Roo.log('onIncrementMinutes');
20225         this.time = this.time.add(Date.MINUTE, 1);
20226         this.update();
20227     },
20228     
20229     onDecrementMinutes: function()
20230     {
20231         Roo.log('onDecrementMinutes');
20232         this.time = this.time.add(Date.MINUTE, -1);
20233         this.update();
20234     },
20235     
20236     onTogglePeriod: function()
20237     {
20238         Roo.log('onTogglePeriod');
20239         this.time = this.time.add(Date.HOUR, 12);
20240         this.update();
20241     }
20242     
20243    
20244 });
20245
20246 Roo.apply(Roo.bootstrap.TimeField,  {
20247     
20248     content : {
20249         tag: 'tbody',
20250         cn: [
20251             {
20252                 tag: 'tr',
20253                 cn: [
20254                 {
20255                     tag: 'td',
20256                     colspan: '7'
20257                 }
20258                 ]
20259             }
20260         ]
20261     },
20262     
20263     footer : {
20264         tag: 'tfoot',
20265         cn: [
20266             {
20267                 tag: 'tr',
20268                 cn: [
20269                 {
20270                     tag: 'th',
20271                     colspan: '7',
20272                     cls: '',
20273                     cn: [
20274                         {
20275                             tag: 'button',
20276                             cls: 'btn btn-info ok',
20277                             html: 'OK'
20278                         }
20279                     ]
20280                 }
20281
20282                 ]
20283             }
20284         ]
20285     }
20286 });
20287
20288 Roo.apply(Roo.bootstrap.TimeField,  {
20289   
20290     template : {
20291         tag: 'div',
20292         cls: 'datepicker dropdown-menu',
20293         cn: [
20294             {
20295                 tag: 'div',
20296                 cls: 'datepicker-time',
20297                 cn: [
20298                 {
20299                     tag: 'table',
20300                     cls: 'table-condensed',
20301                     cn:[
20302                     Roo.bootstrap.TimeField.content,
20303                     Roo.bootstrap.TimeField.footer
20304                     ]
20305                 }
20306                 ]
20307             }
20308         ]
20309     }
20310 });
20311
20312  
20313
20314  /*
20315  * - LGPL
20316  *
20317  * MonthField
20318  * 
20319  */
20320
20321 /**
20322  * @class Roo.bootstrap.MonthField
20323  * @extends Roo.bootstrap.Input
20324  * Bootstrap MonthField class
20325  * 
20326  * @cfg {String} language default en
20327  * 
20328  * @constructor
20329  * Create a new MonthField
20330  * @param {Object} config The config object
20331  */
20332
20333 Roo.bootstrap.MonthField = function(config){
20334     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20335     
20336     this.addEvents({
20337         /**
20338          * @event show
20339          * Fires when this field show.
20340          * @param {Roo.bootstrap.MonthField} this
20341          * @param {Mixed} date The date value
20342          */
20343         show : true,
20344         /**
20345          * @event show
20346          * Fires when this field hide.
20347          * @param {Roo.bootstrap.MonthField} this
20348          * @param {Mixed} date The date value
20349          */
20350         hide : true,
20351         /**
20352          * @event select
20353          * Fires when select a date.
20354          * @param {Roo.bootstrap.MonthField} this
20355          * @param {String} oldvalue The old value
20356          * @param {String} newvalue The new value
20357          */
20358         select : true
20359     });
20360 };
20361
20362 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20363     
20364     onRender: function(ct, position)
20365     {
20366         
20367         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20368         
20369         this.language = this.language || 'en';
20370         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20371         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20372         
20373         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20374         this.isInline = false;
20375         this.isInput = true;
20376         this.component = this.el.select('.add-on', true).first() || false;
20377         this.component = (this.component && this.component.length === 0) ? false : this.component;
20378         this.hasInput = this.component && this.inputEL().length;
20379         
20380         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20381         
20382         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20383         
20384         this.picker().on('mousedown', this.onMousedown, this);
20385         this.picker().on('click', this.onClick, this);
20386         
20387         this.picker().addClass('datepicker-dropdown');
20388         
20389         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20390             v.setStyle('width', '189px');
20391         });
20392         
20393         this.fillMonths();
20394         
20395         this.update();
20396         
20397         if(this.isInline) {
20398             this.show();
20399         }
20400         
20401     },
20402     
20403     setValue: function(v, suppressEvent)
20404     {   
20405         var o = this.getValue();
20406         
20407         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20408         
20409         this.update();
20410
20411         if(suppressEvent !== true){
20412             this.fireEvent('select', this, o, v);
20413         }
20414         
20415     },
20416     
20417     getValue: function()
20418     {
20419         return this.value;
20420     },
20421     
20422     onClick: function(e) 
20423     {
20424         e.stopPropagation();
20425         e.preventDefault();
20426         
20427         var target = e.getTarget();
20428         
20429         if(target.nodeName.toLowerCase() === 'i'){
20430             target = Roo.get(target).dom.parentNode;
20431         }
20432         
20433         var nodeName = target.nodeName;
20434         var className = target.className;
20435         var html = target.innerHTML;
20436         
20437         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20438             return;
20439         }
20440         
20441         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20442         
20443         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20444         
20445         this.hide();
20446                         
20447     },
20448     
20449     picker : function()
20450     {
20451         return this.pickerEl;
20452     },
20453     
20454     fillMonths: function()
20455     {    
20456         var i = 0;
20457         var months = this.picker().select('>.datepicker-months td', true).first();
20458         
20459         months.dom.innerHTML = '';
20460         
20461         while (i < 12) {
20462             var month = {
20463                 tag: 'span',
20464                 cls: 'month',
20465                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20466             };
20467             
20468             months.createChild(month);
20469         }
20470         
20471     },
20472     
20473     update: function()
20474     {
20475         var _this = this;
20476         
20477         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20478             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20479         }
20480         
20481         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20482             e.removeClass('active');
20483             
20484             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20485                 e.addClass('active');
20486             }
20487         })
20488     },
20489     
20490     place: function()
20491     {
20492         if(this.isInline) {
20493             return;
20494         }
20495         
20496         this.picker().removeClass(['bottom', 'top']);
20497         
20498         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20499             /*
20500              * place to the top of element!
20501              *
20502              */
20503             
20504             this.picker().addClass('top');
20505             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20506             
20507             return;
20508         }
20509         
20510         this.picker().addClass('bottom');
20511         
20512         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20513     },
20514     
20515     onFocus : function()
20516     {
20517         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20518         this.show();
20519     },
20520     
20521     onBlur : function()
20522     {
20523         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20524         
20525         var d = this.inputEl().getValue();
20526         
20527         this.setValue(d);
20528                 
20529         this.hide();
20530     },
20531     
20532     show : function()
20533     {
20534         this.picker().show();
20535         this.picker().select('>.datepicker-months', true).first().show();
20536         this.update();
20537         this.place();
20538         
20539         this.fireEvent('show', this, this.date);
20540     },
20541     
20542     hide : function()
20543     {
20544         if(this.isInline) {
20545             return;
20546         }
20547         this.picker().hide();
20548         this.fireEvent('hide', this, this.date);
20549         
20550     },
20551     
20552     onMousedown: function(e)
20553     {
20554         e.stopPropagation();
20555         e.preventDefault();
20556     },
20557     
20558     keyup: function(e)
20559     {
20560         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20561         this.update();
20562     },
20563
20564     fireKey: function(e)
20565     {
20566         if (!this.picker().isVisible()){
20567             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20568                 this.show();
20569             }
20570             return;
20571         }
20572         
20573         var dir;
20574         
20575         switch(e.keyCode){
20576             case 27: // escape
20577                 this.hide();
20578                 e.preventDefault();
20579                 break;
20580             case 37: // left
20581             case 39: // right
20582                 dir = e.keyCode == 37 ? -1 : 1;
20583                 
20584                 this.vIndex = this.vIndex + dir;
20585                 
20586                 if(this.vIndex < 0){
20587                     this.vIndex = 0;
20588                 }
20589                 
20590                 if(this.vIndex > 11){
20591                     this.vIndex = 11;
20592                 }
20593                 
20594                 if(isNaN(this.vIndex)){
20595                     this.vIndex = 0;
20596                 }
20597                 
20598                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20599                 
20600                 break;
20601             case 38: // up
20602             case 40: // down
20603                 
20604                 dir = e.keyCode == 38 ? -1 : 1;
20605                 
20606                 this.vIndex = this.vIndex + dir * 4;
20607                 
20608                 if(this.vIndex < 0){
20609                     this.vIndex = 0;
20610                 }
20611                 
20612                 if(this.vIndex > 11){
20613                     this.vIndex = 11;
20614                 }
20615                 
20616                 if(isNaN(this.vIndex)){
20617                     this.vIndex = 0;
20618                 }
20619                 
20620                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20621                 break;
20622                 
20623             case 13: // enter
20624                 
20625                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20626                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20627                 }
20628                 
20629                 this.hide();
20630                 e.preventDefault();
20631                 break;
20632             case 9: // tab
20633                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20634                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20635                 }
20636                 this.hide();
20637                 break;
20638             case 16: // shift
20639             case 17: // ctrl
20640             case 18: // alt
20641                 break;
20642             default :
20643                 this.hide();
20644                 
20645         }
20646     },
20647     
20648     remove: function() 
20649     {
20650         this.picker().remove();
20651     }
20652    
20653 });
20654
20655 Roo.apply(Roo.bootstrap.MonthField,  {
20656     
20657     content : {
20658         tag: 'tbody',
20659         cn: [
20660         {
20661             tag: 'tr',
20662             cn: [
20663             {
20664                 tag: 'td',
20665                 colspan: '7'
20666             }
20667             ]
20668         }
20669         ]
20670     },
20671     
20672     dates:{
20673         en: {
20674             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20675             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20676         }
20677     }
20678 });
20679
20680 Roo.apply(Roo.bootstrap.MonthField,  {
20681   
20682     template : {
20683         tag: 'div',
20684         cls: 'datepicker dropdown-menu roo-dynamic',
20685         cn: [
20686             {
20687                 tag: 'div',
20688                 cls: 'datepicker-months',
20689                 cn: [
20690                 {
20691                     tag: 'table',
20692                     cls: 'table-condensed',
20693                     cn:[
20694                         Roo.bootstrap.DateField.content
20695                     ]
20696                 }
20697                 ]
20698             }
20699         ]
20700     }
20701 });
20702
20703  
20704
20705  
20706  /*
20707  * - LGPL
20708  *
20709  * CheckBox
20710  * 
20711  */
20712
20713 /**
20714  * @class Roo.bootstrap.CheckBox
20715  * @extends Roo.bootstrap.Input
20716  * Bootstrap CheckBox class
20717  * 
20718  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20719  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20720  * @cfg {String} boxLabel The text that appears beside the checkbox
20721  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20722  * @cfg {Boolean} checked initnal the element
20723  * @cfg {Boolean} inline inline the element (default false)
20724  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20725  * @cfg {String} tooltip label tooltip
20726  * 
20727  * @constructor
20728  * Create a new CheckBox
20729  * @param {Object} config The config object
20730  */
20731
20732 Roo.bootstrap.CheckBox = function(config){
20733     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20734    
20735     this.addEvents({
20736         /**
20737         * @event check
20738         * Fires when the element is checked or unchecked.
20739         * @param {Roo.bootstrap.CheckBox} this This input
20740         * @param {Boolean} checked The new checked value
20741         */
20742        check : true,
20743        /**
20744         * @event click
20745         * Fires when the element is click.
20746         * @param {Roo.bootstrap.CheckBox} this This input
20747         */
20748        click : true
20749     });
20750     
20751 };
20752
20753 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20754   
20755     inputType: 'checkbox',
20756     inputValue: 1,
20757     valueOff: 0,
20758     boxLabel: false,
20759     checked: false,
20760     weight : false,
20761     inline: false,
20762     tooltip : '',
20763     
20764     getAutoCreate : function()
20765     {
20766         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20767         
20768         var id = Roo.id();
20769         
20770         var cfg = {};
20771         
20772         cfg.cls = 'form-group ' + this.inputType; //input-group
20773         
20774         if(this.inline){
20775             cfg.cls += ' ' + this.inputType + '-inline';
20776         }
20777         
20778         var input =  {
20779             tag: 'input',
20780             id : id,
20781             type : this.inputType,
20782             value : this.inputValue,
20783             cls : 'roo-' + this.inputType, //'form-box',
20784             placeholder : this.placeholder || ''
20785             
20786         };
20787         
20788         if(this.inputType != 'radio'){
20789             var hidden =  {
20790                 tag: 'input',
20791                 type : 'hidden',
20792                 cls : 'roo-hidden-value',
20793                 value : this.checked ? this.inputValue : this.valueOff
20794             };
20795         }
20796         
20797             
20798         if (this.weight) { // Validity check?
20799             cfg.cls += " " + this.inputType + "-" + this.weight;
20800         }
20801         
20802         if (this.disabled) {
20803             input.disabled=true;
20804         }
20805         
20806         if(this.checked){
20807             input.checked = this.checked;
20808         }
20809         
20810         if (this.name) {
20811             
20812             input.name = this.name;
20813             
20814             if(this.inputType != 'radio'){
20815                 hidden.name = this.name;
20816                 input.name = '_hidden_' + this.name;
20817             }
20818         }
20819         
20820         if (this.size) {
20821             input.cls += ' input-' + this.size;
20822         }
20823         
20824         var settings=this;
20825         
20826         ['xs','sm','md','lg'].map(function(size){
20827             if (settings[size]) {
20828                 cfg.cls += ' col-' + size + '-' + settings[size];
20829             }
20830         });
20831         
20832         var inputblock = input;
20833          
20834         if (this.before || this.after) {
20835             
20836             inputblock = {
20837                 cls : 'input-group',
20838                 cn :  [] 
20839             };
20840             
20841             if (this.before) {
20842                 inputblock.cn.push({
20843                     tag :'span',
20844                     cls : 'input-group-addon',
20845                     html : this.before
20846                 });
20847             }
20848             
20849             inputblock.cn.push(input);
20850             
20851             if(this.inputType != 'radio'){
20852                 inputblock.cn.push(hidden);
20853             }
20854             
20855             if (this.after) {
20856                 inputblock.cn.push({
20857                     tag :'span',
20858                     cls : 'input-group-addon',
20859                     html : this.after
20860                 });
20861             }
20862             
20863         }
20864         
20865         if (align ==='left' && this.fieldLabel.length) {
20866 //                Roo.log("left and has label");
20867             cfg.cn = [
20868                 {
20869                     tag: 'label',
20870                     'for' :  id,
20871                     cls : 'control-label',
20872                     html : this.fieldLabel
20873                 },
20874                 {
20875                     cls : "", 
20876                     cn: [
20877                         inputblock
20878                     ]
20879                 }
20880             ];
20881             
20882             if(this.labelWidth > 12){
20883                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20884             }
20885             
20886             if(this.labelWidth < 13 && this.labelmd == 0){
20887                 this.labelmd = this.labelWidth;
20888             }
20889             
20890             if(this.labellg > 0){
20891                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20892                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20893             }
20894             
20895             if(this.labelmd > 0){
20896                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20897                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20898             }
20899             
20900             if(this.labelsm > 0){
20901                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20902                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20903             }
20904             
20905             if(this.labelxs > 0){
20906                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20907                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20908             }
20909             
20910         } else if ( this.fieldLabel.length) {
20911 //                Roo.log(" label");
20912                 cfg.cn = [
20913                    
20914                     {
20915                         tag: this.boxLabel ? 'span' : 'label',
20916                         'for': id,
20917                         cls: 'control-label box-input-label',
20918                         //cls : 'input-group-addon',
20919                         html : this.fieldLabel
20920                     },
20921                     
20922                     inputblock
20923                     
20924                 ];
20925
20926         } else {
20927             
20928 //                Roo.log(" no label && no align");
20929                 cfg.cn = [  inputblock ] ;
20930                 
20931                 
20932         }
20933         
20934         if(this.boxLabel){
20935              var boxLabelCfg = {
20936                 tag: 'label',
20937                 //'for': id, // box label is handled by onclick - so no for...
20938                 cls: 'box-label',
20939                 html: this.boxLabel
20940             };
20941             
20942             if(this.tooltip){
20943                 boxLabelCfg.tooltip = this.tooltip;
20944             }
20945              
20946             cfg.cn.push(boxLabelCfg);
20947         }
20948         
20949         if(this.inputType != 'radio'){
20950             cfg.cn.push(hidden);
20951         }
20952         
20953         return cfg;
20954         
20955     },
20956     
20957     /**
20958      * return the real input element.
20959      */
20960     inputEl: function ()
20961     {
20962         return this.el.select('input.roo-' + this.inputType,true).first();
20963     },
20964     hiddenEl: function ()
20965     {
20966         return this.el.select('input.roo-hidden-value',true).first();
20967     },
20968     
20969     labelEl: function()
20970     {
20971         return this.el.select('label.control-label',true).first();
20972     },
20973     /* depricated... */
20974     
20975     label: function()
20976     {
20977         return this.labelEl();
20978     },
20979     
20980     boxLabelEl: function()
20981     {
20982         return this.el.select('label.box-label',true).first();
20983     },
20984     
20985     initEvents : function()
20986     {
20987 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20988         
20989         this.inputEl().on('click', this.onClick,  this);
20990         
20991         if (this.boxLabel) { 
20992             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20993         }
20994         
20995         this.startValue = this.getValue();
20996         
20997         if(this.groupId){
20998             Roo.bootstrap.CheckBox.register(this);
20999         }
21000     },
21001     
21002     onClick : function(e)
21003     {   
21004         if(this.fireEvent('click', this, e) !== false){
21005             this.setChecked(!this.checked);
21006         }
21007         
21008     },
21009     
21010     setChecked : function(state,suppressEvent)
21011     {
21012         this.startValue = this.getValue();
21013
21014         if(this.inputType == 'radio'){
21015             
21016             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21017                 e.dom.checked = false;
21018             });
21019             
21020             this.inputEl().dom.checked = true;
21021             
21022             this.inputEl().dom.value = this.inputValue;
21023             
21024             if(suppressEvent !== true){
21025                 this.fireEvent('check', this, true);
21026             }
21027             
21028             this.validate();
21029             
21030             return;
21031         }
21032         
21033         this.checked = state;
21034         
21035         this.inputEl().dom.checked = state;
21036         
21037         
21038         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21039         
21040         if(suppressEvent !== true){
21041             this.fireEvent('check', this, state);
21042         }
21043         
21044         this.validate();
21045     },
21046     
21047     getValue : function()
21048     {
21049         if(this.inputType == 'radio'){
21050             return this.getGroupValue();
21051         }
21052         
21053         return this.hiddenEl().dom.value;
21054         
21055     },
21056     
21057     getGroupValue : function()
21058     {
21059         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21060             return '';
21061         }
21062         
21063         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21064     },
21065     
21066     setValue : function(v,suppressEvent)
21067     {
21068         if(this.inputType == 'radio'){
21069             this.setGroupValue(v, suppressEvent);
21070             return;
21071         }
21072         
21073         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21074         
21075         this.validate();
21076     },
21077     
21078     setGroupValue : function(v, suppressEvent)
21079     {
21080         this.startValue = this.getValue();
21081         
21082         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21083             e.dom.checked = false;
21084             
21085             if(e.dom.value == v){
21086                 e.dom.checked = true;
21087             }
21088         });
21089         
21090         if(suppressEvent !== true){
21091             this.fireEvent('check', this, true);
21092         }
21093
21094         this.validate();
21095         
21096         return;
21097     },
21098     
21099     validate : function()
21100     {
21101         if(this.getVisibilityEl().hasClass('hidden')){
21102             return true;
21103         }
21104         
21105         if(
21106                 this.disabled || 
21107                 (this.inputType == 'radio' && this.validateRadio()) ||
21108                 (this.inputType == 'checkbox' && this.validateCheckbox())
21109         ){
21110             this.markValid();
21111             return true;
21112         }
21113         
21114         this.markInvalid();
21115         return false;
21116     },
21117     
21118     validateRadio : function()
21119     {
21120         if(this.getVisibilityEl().hasClass('hidden')){
21121             return true;
21122         }
21123         
21124         if(this.allowBlank){
21125             return true;
21126         }
21127         
21128         var valid = false;
21129         
21130         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21131             if(!e.dom.checked){
21132                 return;
21133             }
21134             
21135             valid = true;
21136             
21137             return false;
21138         });
21139         
21140         return valid;
21141     },
21142     
21143     validateCheckbox : function()
21144     {
21145         if(!this.groupId){
21146             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21147             //return (this.getValue() == this.inputValue) ? true : false;
21148         }
21149         
21150         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21151         
21152         if(!group){
21153             return false;
21154         }
21155         
21156         var r = false;
21157         
21158         for(var i in group){
21159             if(group[i].el.isVisible(true)){
21160                 r = false;
21161                 break;
21162             }
21163             
21164             r = true;
21165         }
21166         
21167         for(var i in group){
21168             if(r){
21169                 break;
21170             }
21171             
21172             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21173         }
21174         
21175         return r;
21176     },
21177     
21178     /**
21179      * Mark this field as valid
21180      */
21181     markValid : function()
21182     {
21183         var _this = this;
21184         
21185         this.fireEvent('valid', this);
21186         
21187         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21188         
21189         if(this.groupId){
21190             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21191         }
21192         
21193         if(label){
21194             label.markValid();
21195         }
21196
21197         if(this.inputType == 'radio'){
21198             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21199                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21200                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21201             });
21202             
21203             return;
21204         }
21205
21206         if(!this.groupId){
21207             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21208             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21209             return;
21210         }
21211         
21212         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21213         
21214         if(!group){
21215             return;
21216         }
21217         
21218         for(var i in group){
21219             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21220             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21221         }
21222     },
21223     
21224      /**
21225      * Mark this field as invalid
21226      * @param {String} msg The validation message
21227      */
21228     markInvalid : function(msg)
21229     {
21230         if(this.allowBlank){
21231             return;
21232         }
21233         
21234         var _this = this;
21235         
21236         this.fireEvent('invalid', this, msg);
21237         
21238         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21239         
21240         if(this.groupId){
21241             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21242         }
21243         
21244         if(label){
21245             label.markInvalid();
21246         }
21247             
21248         if(this.inputType == 'radio'){
21249             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21250                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21251                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21252             });
21253             
21254             return;
21255         }
21256         
21257         if(!this.groupId){
21258             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21259             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21260             return;
21261         }
21262         
21263         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21264         
21265         if(!group){
21266             return;
21267         }
21268         
21269         for(var i in group){
21270             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21271             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21272         }
21273         
21274     },
21275     
21276     clearInvalid : function()
21277     {
21278         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21279         
21280         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21281         
21282         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21283         
21284         if (label && label.iconEl) {
21285             label.iconEl.removeClass(label.validClass);
21286             label.iconEl.removeClass(label.invalidClass);
21287         }
21288     },
21289     
21290     disable : function()
21291     {
21292         if(this.inputType != 'radio'){
21293             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21294             return;
21295         }
21296         
21297         var _this = this;
21298         
21299         if(this.rendered){
21300             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21301                 _this.getActionEl().addClass(this.disabledClass);
21302                 e.dom.disabled = true;
21303             });
21304         }
21305         
21306         this.disabled = true;
21307         this.fireEvent("disable", this);
21308         return this;
21309     },
21310
21311     enable : function()
21312     {
21313         if(this.inputType != 'radio'){
21314             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21315             return;
21316         }
21317         
21318         var _this = this;
21319         
21320         if(this.rendered){
21321             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21322                 _this.getActionEl().removeClass(this.disabledClass);
21323                 e.dom.disabled = false;
21324             });
21325         }
21326         
21327         this.disabled = false;
21328         this.fireEvent("enable", this);
21329         return this;
21330     },
21331     
21332     setBoxLabel : function(v)
21333     {
21334         this.boxLabel = v;
21335         
21336         if(this.rendered){
21337             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21338         }
21339     }
21340
21341 });
21342
21343 Roo.apply(Roo.bootstrap.CheckBox, {
21344     
21345     groups: {},
21346     
21347      /**
21348     * register a CheckBox Group
21349     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21350     */
21351     register : function(checkbox)
21352     {
21353         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21354             this.groups[checkbox.groupId] = {};
21355         }
21356         
21357         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21358             return;
21359         }
21360         
21361         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21362         
21363     },
21364     /**
21365     * fetch a CheckBox Group based on the group ID
21366     * @param {string} the group ID
21367     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21368     */
21369     get: function(groupId) {
21370         if (typeof(this.groups[groupId]) == 'undefined') {
21371             return false;
21372         }
21373         
21374         return this.groups[groupId] ;
21375     }
21376     
21377     
21378 });
21379 /*
21380  * - LGPL
21381  *
21382  * RadioItem
21383  * 
21384  */
21385
21386 /**
21387  * @class Roo.bootstrap.Radio
21388  * @extends Roo.bootstrap.Component
21389  * Bootstrap Radio class
21390  * @cfg {String} boxLabel - the label associated
21391  * @cfg {String} value - the value of radio
21392  * 
21393  * @constructor
21394  * Create a new Radio
21395  * @param {Object} config The config object
21396  */
21397 Roo.bootstrap.Radio = function(config){
21398     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21399     
21400 };
21401
21402 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21403     
21404     boxLabel : '',
21405     
21406     value : '',
21407     
21408     getAutoCreate : function()
21409     {
21410         var cfg = {
21411             tag : 'div',
21412             cls : 'form-group radio',
21413             cn : [
21414                 {
21415                     tag : 'label',
21416                     cls : 'box-label',
21417                     html : this.boxLabel
21418                 }
21419             ]
21420         };
21421         
21422         return cfg;
21423     },
21424     
21425     initEvents : function() 
21426     {
21427         this.parent().register(this);
21428         
21429         this.el.on('click', this.onClick, this);
21430         
21431     },
21432     
21433     onClick : function(e)
21434     {
21435         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21436             this.setChecked(true);
21437         }
21438     },
21439     
21440     setChecked : function(state, suppressEvent)
21441     {
21442         this.parent().setValue(this.value, suppressEvent);
21443         
21444     },
21445     
21446     setBoxLabel : function(v)
21447     {
21448         this.boxLabel = v;
21449         
21450         if(this.rendered){
21451             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21452         }
21453     }
21454     
21455 });
21456  
21457
21458  /*
21459  * - LGPL
21460  *
21461  * Input
21462  * 
21463  */
21464
21465 /**
21466  * @class Roo.bootstrap.SecurePass
21467  * @extends Roo.bootstrap.Input
21468  * Bootstrap SecurePass class
21469  *
21470  * 
21471  * @constructor
21472  * Create a new SecurePass
21473  * @param {Object} config The config object
21474  */
21475  
21476 Roo.bootstrap.SecurePass = function (config) {
21477     // these go here, so the translation tool can replace them..
21478     this.errors = {
21479         PwdEmpty: "Please type a password, and then retype it to confirm.",
21480         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21481         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21482         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21483         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21484         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21485         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21486         TooWeak: "Your password is Too Weak."
21487     },
21488     this.meterLabel = "Password strength:";
21489     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21490     this.meterClass = [
21491         "roo-password-meter-tooweak", 
21492         "roo-password-meter-weak", 
21493         "roo-password-meter-medium", 
21494         "roo-password-meter-strong", 
21495         "roo-password-meter-grey"
21496     ];
21497     
21498     this.errors = {};
21499     
21500     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21501 }
21502
21503 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21504     /**
21505      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21506      * {
21507      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21508      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21509      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21510      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21511      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21512      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21513      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21514      * })
21515      */
21516     // private
21517     
21518     meterWidth: 300,
21519     errorMsg :'',    
21520     errors: false,
21521     imageRoot: '/',
21522     /**
21523      * @cfg {String/Object} Label for the strength meter (defaults to
21524      * 'Password strength:')
21525      */
21526     // private
21527     meterLabel: '',
21528     /**
21529      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21530      * ['Weak', 'Medium', 'Strong'])
21531      */
21532     // private    
21533     pwdStrengths: false,    
21534     // private
21535     strength: 0,
21536     // private
21537     _lastPwd: null,
21538     // private
21539     kCapitalLetter: 0,
21540     kSmallLetter: 1,
21541     kDigit: 2,
21542     kPunctuation: 3,
21543     
21544     insecure: false,
21545     // private
21546     initEvents: function ()
21547     {
21548         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21549
21550         if (this.el.is('input[type=password]') && Roo.isSafari) {
21551             this.el.on('keydown', this.SafariOnKeyDown, this);
21552         }
21553
21554         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21555     },
21556     // private
21557     onRender: function (ct, position)
21558     {
21559         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21560         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21561         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21562
21563         this.trigger.createChild({
21564                    cn: [
21565                     {
21566                     //id: 'PwdMeter',
21567                     tag: 'div',
21568                     cls: 'roo-password-meter-grey col-xs-12',
21569                     style: {
21570                         //width: 0,
21571                         //width: this.meterWidth + 'px'                                                
21572                         }
21573                     },
21574                     {                            
21575                          cls: 'roo-password-meter-text'                          
21576                     }
21577                 ]            
21578         });
21579
21580          
21581         if (this.hideTrigger) {
21582             this.trigger.setDisplayed(false);
21583         }
21584         this.setSize(this.width || '', this.height || '');
21585     },
21586     // private
21587     onDestroy: function ()
21588     {
21589         if (this.trigger) {
21590             this.trigger.removeAllListeners();
21591             this.trigger.remove();
21592         }
21593         if (this.wrap) {
21594             this.wrap.remove();
21595         }
21596         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21597     },
21598     // private
21599     checkStrength: function ()
21600     {
21601         var pwd = this.inputEl().getValue();
21602         if (pwd == this._lastPwd) {
21603             return;
21604         }
21605
21606         var strength;
21607         if (this.ClientSideStrongPassword(pwd)) {
21608             strength = 3;
21609         } else if (this.ClientSideMediumPassword(pwd)) {
21610             strength = 2;
21611         } else if (this.ClientSideWeakPassword(pwd)) {
21612             strength = 1;
21613         } else {
21614             strength = 0;
21615         }
21616         
21617         Roo.log('strength1: ' + strength);
21618         
21619         //var pm = this.trigger.child('div/div/div').dom;
21620         var pm = this.trigger.child('div/div');
21621         pm.removeClass(this.meterClass);
21622         pm.addClass(this.meterClass[strength]);
21623                 
21624         
21625         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21626                 
21627         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21628         
21629         this._lastPwd = pwd;
21630     },
21631     reset: function ()
21632     {
21633         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21634         
21635         this._lastPwd = '';
21636         
21637         var pm = this.trigger.child('div/div');
21638         pm.removeClass(this.meterClass);
21639         pm.addClass('roo-password-meter-grey');        
21640         
21641         
21642         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21643         
21644         pt.innerHTML = '';
21645         this.inputEl().dom.type='password';
21646     },
21647     // private
21648     validateValue: function (value)
21649     {
21650         
21651         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21652             return false;
21653         }
21654         if (value.length == 0) {
21655             if (this.allowBlank) {
21656                 this.clearInvalid();
21657                 return true;
21658             }
21659
21660             this.markInvalid(this.errors.PwdEmpty);
21661             this.errorMsg = this.errors.PwdEmpty;
21662             return false;
21663         }
21664         
21665         if(this.insecure){
21666             return true;
21667         }
21668         
21669         if ('[\x21-\x7e]*'.match(value)) {
21670             this.markInvalid(this.errors.PwdBadChar);
21671             this.errorMsg = this.errors.PwdBadChar;
21672             return false;
21673         }
21674         if (value.length < 6) {
21675             this.markInvalid(this.errors.PwdShort);
21676             this.errorMsg = this.errors.PwdShort;
21677             return false;
21678         }
21679         if (value.length > 16) {
21680             this.markInvalid(this.errors.PwdLong);
21681             this.errorMsg = this.errors.PwdLong;
21682             return false;
21683         }
21684         var strength;
21685         if (this.ClientSideStrongPassword(value)) {
21686             strength = 3;
21687         } else if (this.ClientSideMediumPassword(value)) {
21688             strength = 2;
21689         } else if (this.ClientSideWeakPassword(value)) {
21690             strength = 1;
21691         } else {
21692             strength = 0;
21693         }
21694
21695         
21696         if (strength < 2) {
21697             //this.markInvalid(this.errors.TooWeak);
21698             this.errorMsg = this.errors.TooWeak;
21699             //return false;
21700         }
21701         
21702         
21703         console.log('strength2: ' + strength);
21704         
21705         //var pm = this.trigger.child('div/div/div').dom;
21706         
21707         var pm = this.trigger.child('div/div');
21708         pm.removeClass(this.meterClass);
21709         pm.addClass(this.meterClass[strength]);
21710                 
21711         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21712                 
21713         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21714         
21715         this.errorMsg = ''; 
21716         return true;
21717     },
21718     // private
21719     CharacterSetChecks: function (type)
21720     {
21721         this.type = type;
21722         this.fResult = false;
21723     },
21724     // private
21725     isctype: function (character, type)
21726     {
21727         switch (type) {  
21728             case this.kCapitalLetter:
21729                 if (character >= 'A' && character <= 'Z') {
21730                     return true;
21731                 }
21732                 break;
21733             
21734             case this.kSmallLetter:
21735                 if (character >= 'a' && character <= 'z') {
21736                     return true;
21737                 }
21738                 break;
21739             
21740             case this.kDigit:
21741                 if (character >= '0' && character <= '9') {
21742                     return true;
21743                 }
21744                 break;
21745             
21746             case this.kPunctuation:
21747                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21748                     return true;
21749                 }
21750                 break;
21751             
21752             default:
21753                 return false;
21754         }
21755
21756     },
21757     // private
21758     IsLongEnough: function (pwd, size)
21759     {
21760         return !(pwd == null || isNaN(size) || pwd.length < size);
21761     },
21762     // private
21763     SpansEnoughCharacterSets: function (word, nb)
21764     {
21765         if (!this.IsLongEnough(word, nb))
21766         {
21767             return false;
21768         }
21769
21770         var characterSetChecks = new Array(
21771             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21772             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21773         );
21774         
21775         for (var index = 0; index < word.length; ++index) {
21776             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21777                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21778                     characterSetChecks[nCharSet].fResult = true;
21779                     break;
21780                 }
21781             }
21782         }
21783
21784         var nCharSets = 0;
21785         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21786             if (characterSetChecks[nCharSet].fResult) {
21787                 ++nCharSets;
21788             }
21789         }
21790
21791         if (nCharSets < nb) {
21792             return false;
21793         }
21794         return true;
21795     },
21796     // private
21797     ClientSideStrongPassword: function (pwd)
21798     {
21799         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21800     },
21801     // private
21802     ClientSideMediumPassword: function (pwd)
21803     {
21804         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21805     },
21806     // private
21807     ClientSideWeakPassword: function (pwd)
21808     {
21809         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21810     }
21811           
21812 })//<script type="text/javascript">
21813
21814 /*
21815  * Based  Ext JS Library 1.1.1
21816  * Copyright(c) 2006-2007, Ext JS, LLC.
21817  * LGPL
21818  *
21819  */
21820  
21821 /**
21822  * @class Roo.HtmlEditorCore
21823  * @extends Roo.Component
21824  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21825  *
21826  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21827  */
21828
21829 Roo.HtmlEditorCore = function(config){
21830     
21831     
21832     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21833     
21834     
21835     this.addEvents({
21836         /**
21837          * @event initialize
21838          * Fires when the editor is fully initialized (including the iframe)
21839          * @param {Roo.HtmlEditorCore} this
21840          */
21841         initialize: true,
21842         /**
21843          * @event activate
21844          * Fires when the editor is first receives the focus. Any insertion must wait
21845          * until after this event.
21846          * @param {Roo.HtmlEditorCore} this
21847          */
21848         activate: true,
21849          /**
21850          * @event beforesync
21851          * Fires before the textarea is updated with content from the editor iframe. Return false
21852          * to cancel the sync.
21853          * @param {Roo.HtmlEditorCore} this
21854          * @param {String} html
21855          */
21856         beforesync: true,
21857          /**
21858          * @event beforepush
21859          * Fires before the iframe editor is updated with content from the textarea. Return false
21860          * to cancel the push.
21861          * @param {Roo.HtmlEditorCore} this
21862          * @param {String} html
21863          */
21864         beforepush: true,
21865          /**
21866          * @event sync
21867          * Fires when the textarea is updated with content from the editor iframe.
21868          * @param {Roo.HtmlEditorCore} this
21869          * @param {String} html
21870          */
21871         sync: true,
21872          /**
21873          * @event push
21874          * Fires when the iframe editor is updated with content from the textarea.
21875          * @param {Roo.HtmlEditorCore} this
21876          * @param {String} html
21877          */
21878         push: true,
21879         
21880         /**
21881          * @event editorevent
21882          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21883          * @param {Roo.HtmlEditorCore} this
21884          */
21885         editorevent: true
21886         
21887     });
21888     
21889     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21890     
21891     // defaults : white / black...
21892     this.applyBlacklists();
21893     
21894     
21895     
21896 };
21897
21898
21899 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21900
21901
21902      /**
21903      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21904      */
21905     
21906     owner : false,
21907     
21908      /**
21909      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21910      *                        Roo.resizable.
21911      */
21912     resizable : false,
21913      /**
21914      * @cfg {Number} height (in pixels)
21915      */   
21916     height: 300,
21917    /**
21918      * @cfg {Number} width (in pixels)
21919      */   
21920     width: 500,
21921     
21922     /**
21923      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21924      * 
21925      */
21926     stylesheets: false,
21927     
21928     // id of frame..
21929     frameId: false,
21930     
21931     // private properties
21932     validationEvent : false,
21933     deferHeight: true,
21934     initialized : false,
21935     activated : false,
21936     sourceEditMode : false,
21937     onFocus : Roo.emptyFn,
21938     iframePad:3,
21939     hideMode:'offsets',
21940     
21941     clearUp: true,
21942     
21943     // blacklist + whitelisted elements..
21944     black: false,
21945     white: false,
21946      
21947     bodyCls : '',
21948
21949     /**
21950      * Protected method that will not generally be called directly. It
21951      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21952      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21953      */
21954     getDocMarkup : function(){
21955         // body styles..
21956         var st = '';
21957         
21958         // inherit styels from page...?? 
21959         if (this.stylesheets === false) {
21960             
21961             Roo.get(document.head).select('style').each(function(node) {
21962                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21963             });
21964             
21965             Roo.get(document.head).select('link').each(function(node) { 
21966                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21967             });
21968             
21969         } else if (!this.stylesheets.length) {
21970                 // simple..
21971                 st = '<style type="text/css">' +
21972                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21973                    '</style>';
21974         } else { 
21975             st = '<style type="text/css">' +
21976                     this.stylesheets +
21977                 '</style>';
21978         }
21979         
21980         st +=  '<style type="text/css">' +
21981             'IMG { cursor: pointer } ' +
21982         '</style>';
21983
21984         var cls = 'roo-htmleditor-body';
21985         
21986         if(this.bodyCls.length){
21987             cls += ' ' + this.bodyCls;
21988         }
21989         
21990         return '<html><head>' + st  +
21991             //<style type="text/css">' +
21992             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21993             //'</style>' +
21994             ' </head><body class="' +  cls + '"></body></html>';
21995     },
21996
21997     // private
21998     onRender : function(ct, position)
21999     {
22000         var _t = this;
22001         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22002         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22003         
22004         
22005         this.el.dom.style.border = '0 none';
22006         this.el.dom.setAttribute('tabIndex', -1);
22007         this.el.addClass('x-hidden hide');
22008         
22009         
22010         
22011         if(Roo.isIE){ // fix IE 1px bogus margin
22012             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22013         }
22014        
22015         
22016         this.frameId = Roo.id();
22017         
22018          
22019         
22020         var iframe = this.owner.wrap.createChild({
22021             tag: 'iframe',
22022             cls: 'form-control', // bootstrap..
22023             id: this.frameId,
22024             name: this.frameId,
22025             frameBorder : 'no',
22026             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22027         }, this.el
22028         );
22029         
22030         
22031         this.iframe = iframe.dom;
22032
22033          this.assignDocWin();
22034         
22035         this.doc.designMode = 'on';
22036        
22037         this.doc.open();
22038         this.doc.write(this.getDocMarkup());
22039         this.doc.close();
22040
22041         
22042         var task = { // must defer to wait for browser to be ready
22043             run : function(){
22044                 //console.log("run task?" + this.doc.readyState);
22045                 this.assignDocWin();
22046                 if(this.doc.body || this.doc.readyState == 'complete'){
22047                     try {
22048                         this.doc.designMode="on";
22049                     } catch (e) {
22050                         return;
22051                     }
22052                     Roo.TaskMgr.stop(task);
22053                     this.initEditor.defer(10, this);
22054                 }
22055             },
22056             interval : 10,
22057             duration: 10000,
22058             scope: this
22059         };
22060         Roo.TaskMgr.start(task);
22061
22062     },
22063
22064     // private
22065     onResize : function(w, h)
22066     {
22067          Roo.log('resize: ' +w + ',' + h );
22068         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22069         if(!this.iframe){
22070             return;
22071         }
22072         if(typeof w == 'number'){
22073             
22074             this.iframe.style.width = w + 'px';
22075         }
22076         if(typeof h == 'number'){
22077             
22078             this.iframe.style.height = h + 'px';
22079             if(this.doc){
22080                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22081             }
22082         }
22083         
22084     },
22085
22086     /**
22087      * Toggles the editor between standard and source edit mode.
22088      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22089      */
22090     toggleSourceEdit : function(sourceEditMode){
22091         
22092         this.sourceEditMode = sourceEditMode === true;
22093         
22094         if(this.sourceEditMode){
22095  
22096             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22097             
22098         }else{
22099             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22100             //this.iframe.className = '';
22101             this.deferFocus();
22102         }
22103         //this.setSize(this.owner.wrap.getSize());
22104         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22105     },
22106
22107     
22108   
22109
22110     /**
22111      * Protected method that will not generally be called directly. If you need/want
22112      * custom HTML cleanup, this is the method you should override.
22113      * @param {String} html The HTML to be cleaned
22114      * return {String} The cleaned HTML
22115      */
22116     cleanHtml : function(html){
22117         html = String(html);
22118         if(html.length > 5){
22119             if(Roo.isSafari){ // strip safari nonsense
22120                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22121             }
22122         }
22123         if(html == '&nbsp;'){
22124             html = '';
22125         }
22126         return html;
22127     },
22128
22129     /**
22130      * HTML Editor -> Textarea
22131      * Protected method that will not generally be called directly. Syncs the contents
22132      * of the editor iframe with the textarea.
22133      */
22134     syncValue : function(){
22135         if(this.initialized){
22136             var bd = (this.doc.body || this.doc.documentElement);
22137             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22138             var html = bd.innerHTML;
22139             if(Roo.isSafari){
22140                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22141                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22142                 if(m && m[1]){
22143                     html = '<div style="'+m[0]+'">' + html + '</div>';
22144                 }
22145             }
22146             html = this.cleanHtml(html);
22147             // fix up the special chars.. normaly like back quotes in word...
22148             // however we do not want to do this with chinese..
22149             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22150                 var cc = b.charCodeAt();
22151                 if (
22152                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22153                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22154                     (cc >= 0xf900 && cc < 0xfb00 )
22155                 ) {
22156                         return b;
22157                 }
22158                 return "&#"+cc+";" 
22159             });
22160             if(this.owner.fireEvent('beforesync', this, html) !== false){
22161                 this.el.dom.value = html;
22162                 this.owner.fireEvent('sync', this, html);
22163             }
22164         }
22165     },
22166
22167     /**
22168      * Protected method that will not generally be called directly. Pushes the value of the textarea
22169      * into the iframe editor.
22170      */
22171     pushValue : function(){
22172         if(this.initialized){
22173             var v = this.el.dom.value.trim();
22174             
22175 //            if(v.length < 1){
22176 //                v = '&#160;';
22177 //            }
22178             
22179             if(this.owner.fireEvent('beforepush', this, v) !== false){
22180                 var d = (this.doc.body || this.doc.documentElement);
22181                 d.innerHTML = v;
22182                 this.cleanUpPaste();
22183                 this.el.dom.value = d.innerHTML;
22184                 this.owner.fireEvent('push', this, v);
22185             }
22186         }
22187     },
22188
22189     // private
22190     deferFocus : function(){
22191         this.focus.defer(10, this);
22192     },
22193
22194     // doc'ed in Field
22195     focus : function(){
22196         if(this.win && !this.sourceEditMode){
22197             this.win.focus();
22198         }else{
22199             this.el.focus();
22200         }
22201     },
22202     
22203     assignDocWin: function()
22204     {
22205         var iframe = this.iframe;
22206         
22207          if(Roo.isIE){
22208             this.doc = iframe.contentWindow.document;
22209             this.win = iframe.contentWindow;
22210         } else {
22211 //            if (!Roo.get(this.frameId)) {
22212 //                return;
22213 //            }
22214 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22215 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22216             
22217             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22218                 return;
22219             }
22220             
22221             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22222             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22223         }
22224     },
22225     
22226     // private
22227     initEditor : function(){
22228         //console.log("INIT EDITOR");
22229         this.assignDocWin();
22230         
22231         
22232         
22233         this.doc.designMode="on";
22234         this.doc.open();
22235         this.doc.write(this.getDocMarkup());
22236         this.doc.close();
22237         
22238         var dbody = (this.doc.body || this.doc.documentElement);
22239         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22240         // this copies styles from the containing element into thsi one..
22241         // not sure why we need all of this..
22242         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22243         
22244         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22245         //ss['background-attachment'] = 'fixed'; // w3c
22246         dbody.bgProperties = 'fixed'; // ie
22247         //Roo.DomHelper.applyStyles(dbody, ss);
22248         Roo.EventManager.on(this.doc, {
22249             //'mousedown': this.onEditorEvent,
22250             'mouseup': this.onEditorEvent,
22251             'dblclick': this.onEditorEvent,
22252             'click': this.onEditorEvent,
22253             'keyup': this.onEditorEvent,
22254             buffer:100,
22255             scope: this
22256         });
22257         if(Roo.isGecko){
22258             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22259         }
22260         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22261             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22262         }
22263         this.initialized = true;
22264
22265         this.owner.fireEvent('initialize', this);
22266         this.pushValue();
22267     },
22268
22269     // private
22270     onDestroy : function(){
22271         
22272         
22273         
22274         if(this.rendered){
22275             
22276             //for (var i =0; i < this.toolbars.length;i++) {
22277             //    // fixme - ask toolbars for heights?
22278             //    this.toolbars[i].onDestroy();
22279            // }
22280             
22281             //this.wrap.dom.innerHTML = '';
22282             //this.wrap.remove();
22283         }
22284     },
22285
22286     // private
22287     onFirstFocus : function(){
22288         
22289         this.assignDocWin();
22290         
22291         
22292         this.activated = true;
22293          
22294     
22295         if(Roo.isGecko){ // prevent silly gecko errors
22296             this.win.focus();
22297             var s = this.win.getSelection();
22298             if(!s.focusNode || s.focusNode.nodeType != 3){
22299                 var r = s.getRangeAt(0);
22300                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22301                 r.collapse(true);
22302                 this.deferFocus();
22303             }
22304             try{
22305                 this.execCmd('useCSS', true);
22306                 this.execCmd('styleWithCSS', false);
22307             }catch(e){}
22308         }
22309         this.owner.fireEvent('activate', this);
22310     },
22311
22312     // private
22313     adjustFont: function(btn){
22314         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22315         //if(Roo.isSafari){ // safari
22316         //    adjust *= 2;
22317        // }
22318         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22319         if(Roo.isSafari){ // safari
22320             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22321             v =  (v < 10) ? 10 : v;
22322             v =  (v > 48) ? 48 : v;
22323             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22324             
22325         }
22326         
22327         
22328         v = Math.max(1, v+adjust);
22329         
22330         this.execCmd('FontSize', v  );
22331     },
22332
22333     onEditorEvent : function(e)
22334     {
22335         this.owner.fireEvent('editorevent', this, e);
22336       //  this.updateToolbar();
22337         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22338     },
22339
22340     insertTag : function(tg)
22341     {
22342         // could be a bit smarter... -> wrap the current selected tRoo..
22343         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22344             
22345             range = this.createRange(this.getSelection());
22346             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22347             wrappingNode.appendChild(range.extractContents());
22348             range.insertNode(wrappingNode);
22349
22350             return;
22351             
22352             
22353             
22354         }
22355         this.execCmd("formatblock",   tg);
22356         
22357     },
22358     
22359     insertText : function(txt)
22360     {
22361         
22362         
22363         var range = this.createRange();
22364         range.deleteContents();
22365                //alert(Sender.getAttribute('label'));
22366                
22367         range.insertNode(this.doc.createTextNode(txt));
22368     } ,
22369     
22370      
22371
22372     /**
22373      * Executes a Midas editor command on the editor document and performs necessary focus and
22374      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22375      * @param {String} cmd The Midas command
22376      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22377      */
22378     relayCmd : function(cmd, value){
22379         this.win.focus();
22380         this.execCmd(cmd, value);
22381         this.owner.fireEvent('editorevent', this);
22382         //this.updateToolbar();
22383         this.owner.deferFocus();
22384     },
22385
22386     /**
22387      * Executes a Midas editor command directly on the editor document.
22388      * For visual commands, you should use {@link #relayCmd} instead.
22389      * <b>This should only be called after the editor is initialized.</b>
22390      * @param {String} cmd The Midas command
22391      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22392      */
22393     execCmd : function(cmd, value){
22394         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22395         this.syncValue();
22396     },
22397  
22398  
22399    
22400     /**
22401      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22402      * to insert tRoo.
22403      * @param {String} text | dom node.. 
22404      */
22405     insertAtCursor : function(text)
22406     {
22407         
22408         if(!this.activated){
22409             return;
22410         }
22411         /*
22412         if(Roo.isIE){
22413             this.win.focus();
22414             var r = this.doc.selection.createRange();
22415             if(r){
22416                 r.collapse(true);
22417                 r.pasteHTML(text);
22418                 this.syncValue();
22419                 this.deferFocus();
22420             
22421             }
22422             return;
22423         }
22424         */
22425         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22426             this.win.focus();
22427             
22428             
22429             // from jquery ui (MIT licenced)
22430             var range, node;
22431             var win = this.win;
22432             
22433             if (win.getSelection && win.getSelection().getRangeAt) {
22434                 range = win.getSelection().getRangeAt(0);
22435                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22436                 range.insertNode(node);
22437             } else if (win.document.selection && win.document.selection.createRange) {
22438                 // no firefox support
22439                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22440                 win.document.selection.createRange().pasteHTML(txt);
22441             } else {
22442                 // no firefox support
22443                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22444                 this.execCmd('InsertHTML', txt);
22445             } 
22446             
22447             this.syncValue();
22448             
22449             this.deferFocus();
22450         }
22451     },
22452  // private
22453     mozKeyPress : function(e){
22454         if(e.ctrlKey){
22455             var c = e.getCharCode(), cmd;
22456           
22457             if(c > 0){
22458                 c = String.fromCharCode(c).toLowerCase();
22459                 switch(c){
22460                     case 'b':
22461                         cmd = 'bold';
22462                         break;
22463                     case 'i':
22464                         cmd = 'italic';
22465                         break;
22466                     
22467                     case 'u':
22468                         cmd = 'underline';
22469                         break;
22470                     
22471                     case 'v':
22472                         this.cleanUpPaste.defer(100, this);
22473                         return;
22474                         
22475                 }
22476                 if(cmd){
22477                     this.win.focus();
22478                     this.execCmd(cmd);
22479                     this.deferFocus();
22480                     e.preventDefault();
22481                 }
22482                 
22483             }
22484         }
22485     },
22486
22487     // private
22488     fixKeys : function(){ // load time branching for fastest keydown performance
22489         if(Roo.isIE){
22490             return function(e){
22491                 var k = e.getKey(), r;
22492                 if(k == e.TAB){
22493                     e.stopEvent();
22494                     r = this.doc.selection.createRange();
22495                     if(r){
22496                         r.collapse(true);
22497                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22498                         this.deferFocus();
22499                     }
22500                     return;
22501                 }
22502                 
22503                 if(k == e.ENTER){
22504                     r = this.doc.selection.createRange();
22505                     if(r){
22506                         var target = r.parentElement();
22507                         if(!target || target.tagName.toLowerCase() != 'li'){
22508                             e.stopEvent();
22509                             r.pasteHTML('<br />');
22510                             r.collapse(false);
22511                             r.select();
22512                         }
22513                     }
22514                 }
22515                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22516                     this.cleanUpPaste.defer(100, this);
22517                     return;
22518                 }
22519                 
22520                 
22521             };
22522         }else if(Roo.isOpera){
22523             return function(e){
22524                 var k = e.getKey();
22525                 if(k == e.TAB){
22526                     e.stopEvent();
22527                     this.win.focus();
22528                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22529                     this.deferFocus();
22530                 }
22531                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22532                     this.cleanUpPaste.defer(100, this);
22533                     return;
22534                 }
22535                 
22536             };
22537         }else if(Roo.isSafari){
22538             return function(e){
22539                 var k = e.getKey();
22540                 
22541                 if(k == e.TAB){
22542                     e.stopEvent();
22543                     this.execCmd('InsertText','\t');
22544                     this.deferFocus();
22545                     return;
22546                 }
22547                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22548                     this.cleanUpPaste.defer(100, this);
22549                     return;
22550                 }
22551                 
22552              };
22553         }
22554     }(),
22555     
22556     getAllAncestors: function()
22557     {
22558         var p = this.getSelectedNode();
22559         var a = [];
22560         if (!p) {
22561             a.push(p); // push blank onto stack..
22562             p = this.getParentElement();
22563         }
22564         
22565         
22566         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22567             a.push(p);
22568             p = p.parentNode;
22569         }
22570         a.push(this.doc.body);
22571         return a;
22572     },
22573     lastSel : false,
22574     lastSelNode : false,
22575     
22576     
22577     getSelection : function() 
22578     {
22579         this.assignDocWin();
22580         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22581     },
22582     
22583     getSelectedNode: function() 
22584     {
22585         // this may only work on Gecko!!!
22586         
22587         // should we cache this!!!!
22588         
22589         
22590         
22591          
22592         var range = this.createRange(this.getSelection()).cloneRange();
22593         
22594         if (Roo.isIE) {
22595             var parent = range.parentElement();
22596             while (true) {
22597                 var testRange = range.duplicate();
22598                 testRange.moveToElementText(parent);
22599                 if (testRange.inRange(range)) {
22600                     break;
22601                 }
22602                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22603                     break;
22604                 }
22605                 parent = parent.parentElement;
22606             }
22607             return parent;
22608         }
22609         
22610         // is ancestor a text element.
22611         var ac =  range.commonAncestorContainer;
22612         if (ac.nodeType == 3) {
22613             ac = ac.parentNode;
22614         }
22615         
22616         var ar = ac.childNodes;
22617          
22618         var nodes = [];
22619         var other_nodes = [];
22620         var has_other_nodes = false;
22621         for (var i=0;i<ar.length;i++) {
22622             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22623                 continue;
22624             }
22625             // fullly contained node.
22626             
22627             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22628                 nodes.push(ar[i]);
22629                 continue;
22630             }
22631             
22632             // probably selected..
22633             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22634                 other_nodes.push(ar[i]);
22635                 continue;
22636             }
22637             // outer..
22638             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22639                 continue;
22640             }
22641             
22642             
22643             has_other_nodes = true;
22644         }
22645         if (!nodes.length && other_nodes.length) {
22646             nodes= other_nodes;
22647         }
22648         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22649             return false;
22650         }
22651         
22652         return nodes[0];
22653     },
22654     createRange: function(sel)
22655     {
22656         // this has strange effects when using with 
22657         // top toolbar - not sure if it's a great idea.
22658         //this.editor.contentWindow.focus();
22659         if (typeof sel != "undefined") {
22660             try {
22661                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22662             } catch(e) {
22663                 return this.doc.createRange();
22664             }
22665         } else {
22666             return this.doc.createRange();
22667         }
22668     },
22669     getParentElement: function()
22670     {
22671         
22672         this.assignDocWin();
22673         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22674         
22675         var range = this.createRange(sel);
22676          
22677         try {
22678             var p = range.commonAncestorContainer;
22679             while (p.nodeType == 3) { // text node
22680                 p = p.parentNode;
22681             }
22682             return p;
22683         } catch (e) {
22684             return null;
22685         }
22686     
22687     },
22688     /***
22689      *
22690      * Range intersection.. the hard stuff...
22691      *  '-1' = before
22692      *  '0' = hits..
22693      *  '1' = after.
22694      *         [ -- selected range --- ]
22695      *   [fail]                        [fail]
22696      *
22697      *    basically..
22698      *      if end is before start or  hits it. fail.
22699      *      if start is after end or hits it fail.
22700      *
22701      *   if either hits (but other is outside. - then it's not 
22702      *   
22703      *    
22704      **/
22705     
22706     
22707     // @see http://www.thismuchiknow.co.uk/?p=64.
22708     rangeIntersectsNode : function(range, node)
22709     {
22710         var nodeRange = node.ownerDocument.createRange();
22711         try {
22712             nodeRange.selectNode(node);
22713         } catch (e) {
22714             nodeRange.selectNodeContents(node);
22715         }
22716     
22717         var rangeStartRange = range.cloneRange();
22718         rangeStartRange.collapse(true);
22719     
22720         var rangeEndRange = range.cloneRange();
22721         rangeEndRange.collapse(false);
22722     
22723         var nodeStartRange = nodeRange.cloneRange();
22724         nodeStartRange.collapse(true);
22725     
22726         var nodeEndRange = nodeRange.cloneRange();
22727         nodeEndRange.collapse(false);
22728     
22729         return rangeStartRange.compareBoundaryPoints(
22730                  Range.START_TO_START, nodeEndRange) == -1 &&
22731                rangeEndRange.compareBoundaryPoints(
22732                  Range.START_TO_START, nodeStartRange) == 1;
22733         
22734          
22735     },
22736     rangeCompareNode : function(range, node)
22737     {
22738         var nodeRange = node.ownerDocument.createRange();
22739         try {
22740             nodeRange.selectNode(node);
22741         } catch (e) {
22742             nodeRange.selectNodeContents(node);
22743         }
22744         
22745         
22746         range.collapse(true);
22747     
22748         nodeRange.collapse(true);
22749      
22750         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22751         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22752          
22753         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22754         
22755         var nodeIsBefore   =  ss == 1;
22756         var nodeIsAfter    = ee == -1;
22757         
22758         if (nodeIsBefore && nodeIsAfter) {
22759             return 0; // outer
22760         }
22761         if (!nodeIsBefore && nodeIsAfter) {
22762             return 1; //right trailed.
22763         }
22764         
22765         if (nodeIsBefore && !nodeIsAfter) {
22766             return 2;  // left trailed.
22767         }
22768         // fully contined.
22769         return 3;
22770     },
22771
22772     // private? - in a new class?
22773     cleanUpPaste :  function()
22774     {
22775         // cleans up the whole document..
22776         Roo.log('cleanuppaste');
22777         
22778         this.cleanUpChildren(this.doc.body);
22779         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22780         if (clean != this.doc.body.innerHTML) {
22781             this.doc.body.innerHTML = clean;
22782         }
22783         
22784     },
22785     
22786     cleanWordChars : function(input) {// change the chars to hex code
22787         var he = Roo.HtmlEditorCore;
22788         
22789         var output = input;
22790         Roo.each(he.swapCodes, function(sw) { 
22791             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22792             
22793             output = output.replace(swapper, sw[1]);
22794         });
22795         
22796         return output;
22797     },
22798     
22799     
22800     cleanUpChildren : function (n)
22801     {
22802         if (!n.childNodes.length) {
22803             return;
22804         }
22805         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22806            this.cleanUpChild(n.childNodes[i]);
22807         }
22808     },
22809     
22810     
22811         
22812     
22813     cleanUpChild : function (node)
22814     {
22815         var ed = this;
22816         //console.log(node);
22817         if (node.nodeName == "#text") {
22818             // clean up silly Windows -- stuff?
22819             return; 
22820         }
22821         if (node.nodeName == "#comment") {
22822             node.parentNode.removeChild(node);
22823             // clean up silly Windows -- stuff?
22824             return; 
22825         }
22826         var lcname = node.tagName.toLowerCase();
22827         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22828         // whitelist of tags..
22829         
22830         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22831             // remove node.
22832             node.parentNode.removeChild(node);
22833             return;
22834             
22835         }
22836         
22837         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22838         
22839         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22840         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22841         
22842         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22843         //    remove_keep_children = true;
22844         //}
22845         
22846         if (remove_keep_children) {
22847             this.cleanUpChildren(node);
22848             // inserts everything just before this node...
22849             while (node.childNodes.length) {
22850                 var cn = node.childNodes[0];
22851                 node.removeChild(cn);
22852                 node.parentNode.insertBefore(cn, node);
22853             }
22854             node.parentNode.removeChild(node);
22855             return;
22856         }
22857         
22858         if (!node.attributes || !node.attributes.length) {
22859             this.cleanUpChildren(node);
22860             return;
22861         }
22862         
22863         function cleanAttr(n,v)
22864         {
22865             
22866             if (v.match(/^\./) || v.match(/^\//)) {
22867                 return;
22868             }
22869             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22870                 return;
22871             }
22872             if (v.match(/^#/)) {
22873                 return;
22874             }
22875 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22876             node.removeAttribute(n);
22877             
22878         }
22879         
22880         var cwhite = this.cwhite;
22881         var cblack = this.cblack;
22882             
22883         function cleanStyle(n,v)
22884         {
22885             if (v.match(/expression/)) { //XSS?? should we even bother..
22886                 node.removeAttribute(n);
22887                 return;
22888             }
22889             
22890             var parts = v.split(/;/);
22891             var clean = [];
22892             
22893             Roo.each(parts, function(p) {
22894                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22895                 if (!p.length) {
22896                     return true;
22897                 }
22898                 var l = p.split(':').shift().replace(/\s+/g,'');
22899                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22900                 
22901                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22902 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22903                     //node.removeAttribute(n);
22904                     return true;
22905                 }
22906                 //Roo.log()
22907                 // only allow 'c whitelisted system attributes'
22908                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22909 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22910                     //node.removeAttribute(n);
22911                     return true;
22912                 }
22913                 
22914                 
22915                  
22916                 
22917                 clean.push(p);
22918                 return true;
22919             });
22920             if (clean.length) { 
22921                 node.setAttribute(n, clean.join(';'));
22922             } else {
22923                 node.removeAttribute(n);
22924             }
22925             
22926         }
22927         
22928         
22929         for (var i = node.attributes.length-1; i > -1 ; i--) {
22930             var a = node.attributes[i];
22931             //console.log(a);
22932             
22933             if (a.name.toLowerCase().substr(0,2)=='on')  {
22934                 node.removeAttribute(a.name);
22935                 continue;
22936             }
22937             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22938                 node.removeAttribute(a.name);
22939                 continue;
22940             }
22941             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22942                 cleanAttr(a.name,a.value); // fixme..
22943                 continue;
22944             }
22945             if (a.name == 'style') {
22946                 cleanStyle(a.name,a.value);
22947                 continue;
22948             }
22949             /// clean up MS crap..
22950             // tecnically this should be a list of valid class'es..
22951             
22952             
22953             if (a.name == 'class') {
22954                 if (a.value.match(/^Mso/)) {
22955                     node.className = '';
22956                 }
22957                 
22958                 if (a.value.match(/^body$/)) {
22959                     node.className = '';
22960                 }
22961                 continue;
22962             }
22963             
22964             // style cleanup!?
22965             // class cleanup?
22966             
22967         }
22968         
22969         
22970         this.cleanUpChildren(node);
22971         
22972         
22973     },
22974     
22975     /**
22976      * Clean up MS wordisms...
22977      */
22978     cleanWord : function(node)
22979     {
22980         
22981         
22982         if (!node) {
22983             this.cleanWord(this.doc.body);
22984             return;
22985         }
22986         if (node.nodeName == "#text") {
22987             // clean up silly Windows -- stuff?
22988             return; 
22989         }
22990         if (node.nodeName == "#comment") {
22991             node.parentNode.removeChild(node);
22992             // clean up silly Windows -- stuff?
22993             return; 
22994         }
22995         
22996         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22997             node.parentNode.removeChild(node);
22998             return;
22999         }
23000         
23001         // remove - but keep children..
23002         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
23003             while (node.childNodes.length) {
23004                 var cn = node.childNodes[0];
23005                 node.removeChild(cn);
23006                 node.parentNode.insertBefore(cn, node);
23007             }
23008             node.parentNode.removeChild(node);
23009             this.iterateChildren(node, this.cleanWord);
23010             return;
23011         }
23012         // clean styles
23013         if (node.className.length) {
23014             
23015             var cn = node.className.split(/\W+/);
23016             var cna = [];
23017             Roo.each(cn, function(cls) {
23018                 if (cls.match(/Mso[a-zA-Z]+/)) {
23019                     return;
23020                 }
23021                 cna.push(cls);
23022             });
23023             node.className = cna.length ? cna.join(' ') : '';
23024             if (!cna.length) {
23025                 node.removeAttribute("class");
23026             }
23027         }
23028         
23029         if (node.hasAttribute("lang")) {
23030             node.removeAttribute("lang");
23031         }
23032         
23033         if (node.hasAttribute("style")) {
23034             
23035             var styles = node.getAttribute("style").split(";");
23036             var nstyle = [];
23037             Roo.each(styles, function(s) {
23038                 if (!s.match(/:/)) {
23039                     return;
23040                 }
23041                 var kv = s.split(":");
23042                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23043                     return;
23044                 }
23045                 // what ever is left... we allow.
23046                 nstyle.push(s);
23047             });
23048             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23049             if (!nstyle.length) {
23050                 node.removeAttribute('style');
23051             }
23052         }
23053         this.iterateChildren(node, this.cleanWord);
23054         
23055         
23056         
23057     },
23058     /**
23059      * iterateChildren of a Node, calling fn each time, using this as the scole..
23060      * @param {DomNode} node node to iterate children of.
23061      * @param {Function} fn method of this class to call on each item.
23062      */
23063     iterateChildren : function(node, fn)
23064     {
23065         if (!node.childNodes.length) {
23066                 return;
23067         }
23068         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23069            fn.call(this, node.childNodes[i])
23070         }
23071     },
23072     
23073     
23074     /**
23075      * cleanTableWidths.
23076      *
23077      * Quite often pasting from word etc.. results in tables with column and widths.
23078      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23079      *
23080      */
23081     cleanTableWidths : function(node)
23082     {
23083          
23084          
23085         if (!node) {
23086             this.cleanTableWidths(this.doc.body);
23087             return;
23088         }
23089         
23090         // ignore list...
23091         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23092             return; 
23093         }
23094         Roo.log(node.tagName);
23095         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23096             this.iterateChildren(node, this.cleanTableWidths);
23097             return;
23098         }
23099         if (node.hasAttribute('width')) {
23100             node.removeAttribute('width');
23101         }
23102         
23103          
23104         if (node.hasAttribute("style")) {
23105             // pretty basic...
23106             
23107             var styles = node.getAttribute("style").split(";");
23108             var nstyle = [];
23109             Roo.each(styles, function(s) {
23110                 if (!s.match(/:/)) {
23111                     return;
23112                 }
23113                 var kv = s.split(":");
23114                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23115                     return;
23116                 }
23117                 // what ever is left... we allow.
23118                 nstyle.push(s);
23119             });
23120             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23121             if (!nstyle.length) {
23122                 node.removeAttribute('style');
23123             }
23124         }
23125         
23126         this.iterateChildren(node, this.cleanTableWidths);
23127         
23128         
23129     },
23130     
23131     
23132     
23133     
23134     domToHTML : function(currentElement, depth, nopadtext) {
23135         
23136         depth = depth || 0;
23137         nopadtext = nopadtext || false;
23138     
23139         if (!currentElement) {
23140             return this.domToHTML(this.doc.body);
23141         }
23142         
23143         //Roo.log(currentElement);
23144         var j;
23145         var allText = false;
23146         var nodeName = currentElement.nodeName;
23147         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23148         
23149         if  (nodeName == '#text') {
23150             
23151             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23152         }
23153         
23154         
23155         var ret = '';
23156         if (nodeName != 'BODY') {
23157              
23158             var i = 0;
23159             // Prints the node tagName, such as <A>, <IMG>, etc
23160             if (tagName) {
23161                 var attr = [];
23162                 for(i = 0; i < currentElement.attributes.length;i++) {
23163                     // quoting?
23164                     var aname = currentElement.attributes.item(i).name;
23165                     if (!currentElement.attributes.item(i).value.length) {
23166                         continue;
23167                     }
23168                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23169                 }
23170                 
23171                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23172             } 
23173             else {
23174                 
23175                 // eack
23176             }
23177         } else {
23178             tagName = false;
23179         }
23180         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23181             return ret;
23182         }
23183         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23184             nopadtext = true;
23185         }
23186         
23187         
23188         // Traverse the tree
23189         i = 0;
23190         var currentElementChild = currentElement.childNodes.item(i);
23191         var allText = true;
23192         var innerHTML  = '';
23193         lastnode = '';
23194         while (currentElementChild) {
23195             // Formatting code (indent the tree so it looks nice on the screen)
23196             var nopad = nopadtext;
23197             if (lastnode == 'SPAN') {
23198                 nopad  = true;
23199             }
23200             // text
23201             if  (currentElementChild.nodeName == '#text') {
23202                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23203                 toadd = nopadtext ? toadd : toadd.trim();
23204                 if (!nopad && toadd.length > 80) {
23205                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23206                 }
23207                 innerHTML  += toadd;
23208                 
23209                 i++;
23210                 currentElementChild = currentElement.childNodes.item(i);
23211                 lastNode = '';
23212                 continue;
23213             }
23214             allText = false;
23215             
23216             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23217                 
23218             // Recursively traverse the tree structure of the child node
23219             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23220             lastnode = currentElementChild.nodeName;
23221             i++;
23222             currentElementChild=currentElement.childNodes.item(i);
23223         }
23224         
23225         ret += innerHTML;
23226         
23227         if (!allText) {
23228                 // The remaining code is mostly for formatting the tree
23229             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23230         }
23231         
23232         
23233         if (tagName) {
23234             ret+= "</"+tagName+">";
23235         }
23236         return ret;
23237         
23238     },
23239         
23240     applyBlacklists : function()
23241     {
23242         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23243         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23244         
23245         this.white = [];
23246         this.black = [];
23247         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23248             if (b.indexOf(tag) > -1) {
23249                 return;
23250             }
23251             this.white.push(tag);
23252             
23253         }, this);
23254         
23255         Roo.each(w, function(tag) {
23256             if (b.indexOf(tag) > -1) {
23257                 return;
23258             }
23259             if (this.white.indexOf(tag) > -1) {
23260                 return;
23261             }
23262             this.white.push(tag);
23263             
23264         }, this);
23265         
23266         
23267         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23268             if (w.indexOf(tag) > -1) {
23269                 return;
23270             }
23271             this.black.push(tag);
23272             
23273         }, this);
23274         
23275         Roo.each(b, function(tag) {
23276             if (w.indexOf(tag) > -1) {
23277                 return;
23278             }
23279             if (this.black.indexOf(tag) > -1) {
23280                 return;
23281             }
23282             this.black.push(tag);
23283             
23284         }, this);
23285         
23286         
23287         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23288         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23289         
23290         this.cwhite = [];
23291         this.cblack = [];
23292         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23293             if (b.indexOf(tag) > -1) {
23294                 return;
23295             }
23296             this.cwhite.push(tag);
23297             
23298         }, this);
23299         
23300         Roo.each(w, function(tag) {
23301             if (b.indexOf(tag) > -1) {
23302                 return;
23303             }
23304             if (this.cwhite.indexOf(tag) > -1) {
23305                 return;
23306             }
23307             this.cwhite.push(tag);
23308             
23309         }, this);
23310         
23311         
23312         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23313             if (w.indexOf(tag) > -1) {
23314                 return;
23315             }
23316             this.cblack.push(tag);
23317             
23318         }, this);
23319         
23320         Roo.each(b, function(tag) {
23321             if (w.indexOf(tag) > -1) {
23322                 return;
23323             }
23324             if (this.cblack.indexOf(tag) > -1) {
23325                 return;
23326             }
23327             this.cblack.push(tag);
23328             
23329         }, this);
23330     },
23331     
23332     setStylesheets : function(stylesheets)
23333     {
23334         if(typeof(stylesheets) == 'string'){
23335             Roo.get(this.iframe.contentDocument.head).createChild({
23336                 tag : 'link',
23337                 rel : 'stylesheet',
23338                 type : 'text/css',
23339                 href : stylesheets
23340             });
23341             
23342             return;
23343         }
23344         var _this = this;
23345      
23346         Roo.each(stylesheets, function(s) {
23347             if(!s.length){
23348                 return;
23349             }
23350             
23351             Roo.get(_this.iframe.contentDocument.head).createChild({
23352                 tag : 'link',
23353                 rel : 'stylesheet',
23354                 type : 'text/css',
23355                 href : s
23356             });
23357         });
23358
23359         
23360     },
23361     
23362     removeStylesheets : function()
23363     {
23364         var _this = this;
23365         
23366         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23367             s.remove();
23368         });
23369     },
23370     
23371     setStyle : function(style)
23372     {
23373         Roo.get(this.iframe.contentDocument.head).createChild({
23374             tag : 'style',
23375             type : 'text/css',
23376             html : style
23377         });
23378
23379         return;
23380     }
23381     
23382     // hide stuff that is not compatible
23383     /**
23384      * @event blur
23385      * @hide
23386      */
23387     /**
23388      * @event change
23389      * @hide
23390      */
23391     /**
23392      * @event focus
23393      * @hide
23394      */
23395     /**
23396      * @event specialkey
23397      * @hide
23398      */
23399     /**
23400      * @cfg {String} fieldClass @hide
23401      */
23402     /**
23403      * @cfg {String} focusClass @hide
23404      */
23405     /**
23406      * @cfg {String} autoCreate @hide
23407      */
23408     /**
23409      * @cfg {String} inputType @hide
23410      */
23411     /**
23412      * @cfg {String} invalidClass @hide
23413      */
23414     /**
23415      * @cfg {String} invalidText @hide
23416      */
23417     /**
23418      * @cfg {String} msgFx @hide
23419      */
23420     /**
23421      * @cfg {String} validateOnBlur @hide
23422      */
23423 });
23424
23425 Roo.HtmlEditorCore.white = [
23426         'area', 'br', 'img', 'input', 'hr', 'wbr',
23427         
23428        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23429        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23430        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23431        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23432        'table',   'ul',         'xmp', 
23433        
23434        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23435       'thead',   'tr', 
23436      
23437       'dir', 'menu', 'ol', 'ul', 'dl',
23438        
23439       'embed',  'object'
23440 ];
23441
23442
23443 Roo.HtmlEditorCore.black = [
23444     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23445         'applet', // 
23446         'base',   'basefont', 'bgsound', 'blink',  'body', 
23447         'frame',  'frameset', 'head',    'html',   'ilayer', 
23448         'iframe', 'layer',  'link',     'meta',    'object',   
23449         'script', 'style' ,'title',  'xml' // clean later..
23450 ];
23451 Roo.HtmlEditorCore.clean = [
23452     'script', 'style', 'title', 'xml'
23453 ];
23454 Roo.HtmlEditorCore.remove = [
23455     'font'
23456 ];
23457 // attributes..
23458
23459 Roo.HtmlEditorCore.ablack = [
23460     'on'
23461 ];
23462     
23463 Roo.HtmlEditorCore.aclean = [ 
23464     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23465 ];
23466
23467 // protocols..
23468 Roo.HtmlEditorCore.pwhite= [
23469         'http',  'https',  'mailto'
23470 ];
23471
23472 // white listed style attributes.
23473 Roo.HtmlEditorCore.cwhite= [
23474       //  'text-align', /// default is to allow most things..
23475       
23476          
23477 //        'font-size'//??
23478 ];
23479
23480 // black listed style attributes.
23481 Roo.HtmlEditorCore.cblack= [
23482       //  'font-size' -- this can be set by the project 
23483 ];
23484
23485
23486 Roo.HtmlEditorCore.swapCodes   =[ 
23487     [    8211, "--" ], 
23488     [    8212, "--" ], 
23489     [    8216,  "'" ],  
23490     [    8217, "'" ],  
23491     [    8220, '"' ],  
23492     [    8221, '"' ],  
23493     [    8226, "*" ],  
23494     [    8230, "..." ]
23495 ]; 
23496
23497     /*
23498  * - LGPL
23499  *
23500  * HtmlEditor
23501  * 
23502  */
23503
23504 /**
23505  * @class Roo.bootstrap.HtmlEditor
23506  * @extends Roo.bootstrap.TextArea
23507  * Bootstrap HtmlEditor class
23508
23509  * @constructor
23510  * Create a new HtmlEditor
23511  * @param {Object} config The config object
23512  */
23513
23514 Roo.bootstrap.HtmlEditor = function(config){
23515     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23516     if (!this.toolbars) {
23517         this.toolbars = [];
23518     }
23519     
23520     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23521     this.addEvents({
23522             /**
23523              * @event initialize
23524              * Fires when the editor is fully initialized (including the iframe)
23525              * @param {HtmlEditor} this
23526              */
23527             initialize: true,
23528             /**
23529              * @event activate
23530              * Fires when the editor is first receives the focus. Any insertion must wait
23531              * until after this event.
23532              * @param {HtmlEditor} this
23533              */
23534             activate: true,
23535              /**
23536              * @event beforesync
23537              * Fires before the textarea is updated with content from the editor iframe. Return false
23538              * to cancel the sync.
23539              * @param {HtmlEditor} this
23540              * @param {String} html
23541              */
23542             beforesync: true,
23543              /**
23544              * @event beforepush
23545              * Fires before the iframe editor is updated with content from the textarea. Return false
23546              * to cancel the push.
23547              * @param {HtmlEditor} this
23548              * @param {String} html
23549              */
23550             beforepush: true,
23551              /**
23552              * @event sync
23553              * Fires when the textarea is updated with content from the editor iframe.
23554              * @param {HtmlEditor} this
23555              * @param {String} html
23556              */
23557             sync: true,
23558              /**
23559              * @event push
23560              * Fires when the iframe editor is updated with content from the textarea.
23561              * @param {HtmlEditor} this
23562              * @param {String} html
23563              */
23564             push: true,
23565              /**
23566              * @event editmodechange
23567              * Fires when the editor switches edit modes
23568              * @param {HtmlEditor} this
23569              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23570              */
23571             editmodechange: true,
23572             /**
23573              * @event editorevent
23574              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23575              * @param {HtmlEditor} this
23576              */
23577             editorevent: true,
23578             /**
23579              * @event firstfocus
23580              * Fires when on first focus - needed by toolbars..
23581              * @param {HtmlEditor} this
23582              */
23583             firstfocus: true,
23584             /**
23585              * @event autosave
23586              * Auto save the htmlEditor value as a file into Events
23587              * @param {HtmlEditor} this
23588              */
23589             autosave: true,
23590             /**
23591              * @event savedpreview
23592              * preview the saved version of htmlEditor
23593              * @param {HtmlEditor} this
23594              */
23595             savedpreview: true
23596         });
23597 };
23598
23599
23600 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23601     
23602     
23603       /**
23604      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23605      */
23606     toolbars : false,
23607     
23608      /**
23609     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23610     */
23611     btns : [],
23612    
23613      /**
23614      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23615      *                        Roo.resizable.
23616      */
23617     resizable : false,
23618      /**
23619      * @cfg {Number} height (in pixels)
23620      */   
23621     height: 300,
23622    /**
23623      * @cfg {Number} width (in pixels)
23624      */   
23625     width: false,
23626     
23627     /**
23628      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23629      * 
23630      */
23631     stylesheets: false,
23632     
23633     // id of frame..
23634     frameId: false,
23635     
23636     // private properties
23637     validationEvent : false,
23638     deferHeight: true,
23639     initialized : false,
23640     activated : false,
23641     
23642     onFocus : Roo.emptyFn,
23643     iframePad:3,
23644     hideMode:'offsets',
23645     
23646     tbContainer : false,
23647     
23648     bodyCls : '',
23649     
23650     toolbarContainer :function() {
23651         return this.wrap.select('.x-html-editor-tb',true).first();
23652     },
23653
23654     /**
23655      * Protected method that will not generally be called directly. It
23656      * is called when the editor creates its toolbar. Override this method if you need to
23657      * add custom toolbar buttons.
23658      * @param {HtmlEditor} editor
23659      */
23660     createToolbar : function(){
23661         Roo.log('renewing');
23662         Roo.log("create toolbars");
23663         
23664         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23665         this.toolbars[0].render(this.toolbarContainer());
23666         
23667         return;
23668         
23669 //        if (!editor.toolbars || !editor.toolbars.length) {
23670 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23671 //        }
23672 //        
23673 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23674 //            editor.toolbars[i] = Roo.factory(
23675 //                    typeof(editor.toolbars[i]) == 'string' ?
23676 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23677 //                Roo.bootstrap.HtmlEditor);
23678 //            editor.toolbars[i].init(editor);
23679 //        }
23680     },
23681
23682      
23683     // private
23684     onRender : function(ct, position)
23685     {
23686        // Roo.log("Call onRender: " + this.xtype);
23687         var _t = this;
23688         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23689       
23690         this.wrap = this.inputEl().wrap({
23691             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23692         });
23693         
23694         this.editorcore.onRender(ct, position);
23695          
23696         if (this.resizable) {
23697             this.resizeEl = new Roo.Resizable(this.wrap, {
23698                 pinned : true,
23699                 wrap: true,
23700                 dynamic : true,
23701                 minHeight : this.height,
23702                 height: this.height,
23703                 handles : this.resizable,
23704                 width: this.width,
23705                 listeners : {
23706                     resize : function(r, w, h) {
23707                         _t.onResize(w,h); // -something
23708                     }
23709                 }
23710             });
23711             
23712         }
23713         this.createToolbar(this);
23714        
23715         
23716         if(!this.width && this.resizable){
23717             this.setSize(this.wrap.getSize());
23718         }
23719         if (this.resizeEl) {
23720             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23721             // should trigger onReize..
23722         }
23723         
23724     },
23725
23726     // private
23727     onResize : function(w, h)
23728     {
23729         Roo.log('resize: ' +w + ',' + h );
23730         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23731         var ew = false;
23732         var eh = false;
23733         
23734         if(this.inputEl() ){
23735             if(typeof w == 'number'){
23736                 var aw = w - this.wrap.getFrameWidth('lr');
23737                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23738                 ew = aw;
23739             }
23740             if(typeof h == 'number'){
23741                  var tbh = -11;  // fixme it needs to tool bar size!
23742                 for (var i =0; i < this.toolbars.length;i++) {
23743                     // fixme - ask toolbars for heights?
23744                     tbh += this.toolbars[i].el.getHeight();
23745                     //if (this.toolbars[i].footer) {
23746                     //    tbh += this.toolbars[i].footer.el.getHeight();
23747                     //}
23748                 }
23749               
23750                 
23751                 
23752                 
23753                 
23754                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23755                 ah -= 5; // knock a few pixes off for look..
23756                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23757                 var eh = ah;
23758             }
23759         }
23760         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23761         this.editorcore.onResize(ew,eh);
23762         
23763     },
23764
23765     /**
23766      * Toggles the editor between standard and source edit mode.
23767      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23768      */
23769     toggleSourceEdit : function(sourceEditMode)
23770     {
23771         this.editorcore.toggleSourceEdit(sourceEditMode);
23772         
23773         if(this.editorcore.sourceEditMode){
23774             Roo.log('editor - showing textarea');
23775             
23776 //            Roo.log('in');
23777 //            Roo.log(this.syncValue());
23778             this.syncValue();
23779             this.inputEl().removeClass(['hide', 'x-hidden']);
23780             this.inputEl().dom.removeAttribute('tabIndex');
23781             this.inputEl().focus();
23782         }else{
23783             Roo.log('editor - hiding textarea');
23784 //            Roo.log('out')
23785 //            Roo.log(this.pushValue()); 
23786             this.pushValue();
23787             
23788             this.inputEl().addClass(['hide', 'x-hidden']);
23789             this.inputEl().dom.setAttribute('tabIndex', -1);
23790             //this.deferFocus();
23791         }
23792          
23793         if(this.resizable){
23794             this.setSize(this.wrap.getSize());
23795         }
23796         
23797         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23798     },
23799  
23800     // private (for BoxComponent)
23801     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23802
23803     // private (for BoxComponent)
23804     getResizeEl : function(){
23805         return this.wrap;
23806     },
23807
23808     // private (for BoxComponent)
23809     getPositionEl : function(){
23810         return this.wrap;
23811     },
23812
23813     // private
23814     initEvents : function(){
23815         this.originalValue = this.getValue();
23816     },
23817
23818 //    /**
23819 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23820 //     * @method
23821 //     */
23822 //    markInvalid : Roo.emptyFn,
23823 //    /**
23824 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23825 //     * @method
23826 //     */
23827 //    clearInvalid : Roo.emptyFn,
23828
23829     setValue : function(v){
23830         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23831         this.editorcore.pushValue();
23832     },
23833
23834      
23835     // private
23836     deferFocus : function(){
23837         this.focus.defer(10, this);
23838     },
23839
23840     // doc'ed in Field
23841     focus : function(){
23842         this.editorcore.focus();
23843         
23844     },
23845       
23846
23847     // private
23848     onDestroy : function(){
23849         
23850         
23851         
23852         if(this.rendered){
23853             
23854             for (var i =0; i < this.toolbars.length;i++) {
23855                 // fixme - ask toolbars for heights?
23856                 this.toolbars[i].onDestroy();
23857             }
23858             
23859             this.wrap.dom.innerHTML = '';
23860             this.wrap.remove();
23861         }
23862     },
23863
23864     // private
23865     onFirstFocus : function(){
23866         //Roo.log("onFirstFocus");
23867         this.editorcore.onFirstFocus();
23868          for (var i =0; i < this.toolbars.length;i++) {
23869             this.toolbars[i].onFirstFocus();
23870         }
23871         
23872     },
23873     
23874     // private
23875     syncValue : function()
23876     {   
23877         this.editorcore.syncValue();
23878     },
23879     
23880     pushValue : function()
23881     {   
23882         this.editorcore.pushValue();
23883     }
23884      
23885     
23886     // hide stuff that is not compatible
23887     /**
23888      * @event blur
23889      * @hide
23890      */
23891     /**
23892      * @event change
23893      * @hide
23894      */
23895     /**
23896      * @event focus
23897      * @hide
23898      */
23899     /**
23900      * @event specialkey
23901      * @hide
23902      */
23903     /**
23904      * @cfg {String} fieldClass @hide
23905      */
23906     /**
23907      * @cfg {String} focusClass @hide
23908      */
23909     /**
23910      * @cfg {String} autoCreate @hide
23911      */
23912     /**
23913      * @cfg {String} inputType @hide
23914      */
23915     /**
23916      * @cfg {String} invalidClass @hide
23917      */
23918     /**
23919      * @cfg {String} invalidText @hide
23920      */
23921     /**
23922      * @cfg {String} msgFx @hide
23923      */
23924     /**
23925      * @cfg {String} validateOnBlur @hide
23926      */
23927 });
23928  
23929     
23930    
23931    
23932    
23933       
23934 Roo.namespace('Roo.bootstrap.htmleditor');
23935 /**
23936  * @class Roo.bootstrap.HtmlEditorToolbar1
23937  * Basic Toolbar
23938  * 
23939  * Usage:
23940  *
23941  new Roo.bootstrap.HtmlEditor({
23942     ....
23943     toolbars : [
23944         new Roo.bootstrap.HtmlEditorToolbar1({
23945             disable : { fonts: 1 , format: 1, ..., ... , ...],
23946             btns : [ .... ]
23947         })
23948     }
23949      
23950  * 
23951  * @cfg {Object} disable List of elements to disable..
23952  * @cfg {Array} btns List of additional buttons.
23953  * 
23954  * 
23955  * NEEDS Extra CSS? 
23956  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23957  */
23958  
23959 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23960 {
23961     
23962     Roo.apply(this, config);
23963     
23964     // default disabled, based on 'good practice'..
23965     this.disable = this.disable || {};
23966     Roo.applyIf(this.disable, {
23967         fontSize : true,
23968         colors : true,
23969         specialElements : true
23970     });
23971     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23972     
23973     this.editor = config.editor;
23974     this.editorcore = config.editor.editorcore;
23975     
23976     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23977     
23978     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23979     // dont call parent... till later.
23980 }
23981 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23982      
23983     bar : true,
23984     
23985     editor : false,
23986     editorcore : false,
23987     
23988     
23989     formats : [
23990         "p" ,  
23991         "h1","h2","h3","h4","h5","h6", 
23992         "pre", "code", 
23993         "abbr", "acronym", "address", "cite", "samp", "var",
23994         'div','span'
23995     ],
23996     
23997     onRender : function(ct, position)
23998     {
23999        // Roo.log("Call onRender: " + this.xtype);
24000         
24001        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24002        Roo.log(this.el);
24003        this.el.dom.style.marginBottom = '0';
24004        var _this = this;
24005        var editorcore = this.editorcore;
24006        var editor= this.editor;
24007        
24008        var children = [];
24009        var btn = function(id,cmd , toggle, handler, html){
24010        
24011             var  event = toggle ? 'toggle' : 'click';
24012        
24013             var a = {
24014                 size : 'sm',
24015                 xtype: 'Button',
24016                 xns: Roo.bootstrap,
24017                 //glyphicon : id,
24018                 fa: id,
24019                 cmd : id || cmd,
24020                 enableToggle:toggle !== false,
24021                 html : html || '',
24022                 pressed : toggle ? false : null,
24023                 listeners : {}
24024             };
24025             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24026                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24027             };
24028             children.push(a);
24029             return a;
24030        }
24031        
24032     //    var cb_box = function...
24033         
24034         var style = {
24035                 xtype: 'Button',
24036                 size : 'sm',
24037                 xns: Roo.bootstrap,
24038                 fa : 'font',
24039                 //html : 'submit'
24040                 menu : {
24041                     xtype: 'Menu',
24042                     xns: Roo.bootstrap,
24043                     items:  []
24044                 }
24045         };
24046         Roo.each(this.formats, function(f) {
24047             style.menu.items.push({
24048                 xtype :'MenuItem',
24049                 xns: Roo.bootstrap,
24050                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24051                 tagname : f,
24052                 listeners : {
24053                     click : function()
24054                     {
24055                         editorcore.insertTag(this.tagname);
24056                         editor.focus();
24057                     }
24058                 }
24059                 
24060             });
24061         });
24062         children.push(style);   
24063         
24064         btn('bold',false,true);
24065         btn('italic',false,true);
24066         btn('align-left', 'justifyleft',true);
24067         btn('align-center', 'justifycenter',true);
24068         btn('align-right' , 'justifyright',true);
24069         btn('link', false, false, function(btn) {
24070             //Roo.log("create link?");
24071             var url = prompt(this.createLinkText, this.defaultLinkValue);
24072             if(url && url != 'http:/'+'/'){
24073                 this.editorcore.relayCmd('createlink', url);
24074             }
24075         }),
24076         btn('list','insertunorderedlist',true);
24077         btn('pencil', false,true, function(btn){
24078                 Roo.log(this);
24079                 this.toggleSourceEdit(btn.pressed);
24080         });
24081         
24082         if (this.editor.btns.length > 0) {
24083             for (var i = 0; i<this.editor.btns.length; i++) {
24084                 children.push(this.editor.btns[i]);
24085             }
24086         }
24087         
24088         /*
24089         var cog = {
24090                 xtype: 'Button',
24091                 size : 'sm',
24092                 xns: Roo.bootstrap,
24093                 glyphicon : 'cog',
24094                 //html : 'submit'
24095                 menu : {
24096                     xtype: 'Menu',
24097                     xns: Roo.bootstrap,
24098                     items:  []
24099                 }
24100         };
24101         
24102         cog.menu.items.push({
24103             xtype :'MenuItem',
24104             xns: Roo.bootstrap,
24105             html : Clean styles,
24106             tagname : f,
24107             listeners : {
24108                 click : function()
24109                 {
24110                     editorcore.insertTag(this.tagname);
24111                     editor.focus();
24112                 }
24113             }
24114             
24115         });
24116        */
24117         
24118          
24119        this.xtype = 'NavSimplebar';
24120         
24121         for(var i=0;i< children.length;i++) {
24122             
24123             this.buttons.add(this.addxtypeChild(children[i]));
24124             
24125         }
24126         
24127         editor.on('editorevent', this.updateToolbar, this);
24128     },
24129     onBtnClick : function(id)
24130     {
24131        this.editorcore.relayCmd(id);
24132        this.editorcore.focus();
24133     },
24134     
24135     /**
24136      * Protected method that will not generally be called directly. It triggers
24137      * a toolbar update by reading the markup state of the current selection in the editor.
24138      */
24139     updateToolbar: function(){
24140
24141         if(!this.editorcore.activated){
24142             this.editor.onFirstFocus(); // is this neeed?
24143             return;
24144         }
24145
24146         var btns = this.buttons; 
24147         var doc = this.editorcore.doc;
24148         btns.get('bold').setActive(doc.queryCommandState('bold'));
24149         btns.get('italic').setActive(doc.queryCommandState('italic'));
24150         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24151         
24152         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24153         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24154         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24155         
24156         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24157         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24158          /*
24159         
24160         var ans = this.editorcore.getAllAncestors();
24161         if (this.formatCombo) {
24162             
24163             
24164             var store = this.formatCombo.store;
24165             this.formatCombo.setValue("");
24166             for (var i =0; i < ans.length;i++) {
24167                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24168                     // select it..
24169                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24170                     break;
24171                 }
24172             }
24173         }
24174         
24175         
24176         
24177         // hides menus... - so this cant be on a menu...
24178         Roo.bootstrap.MenuMgr.hideAll();
24179         */
24180         Roo.bootstrap.MenuMgr.hideAll();
24181         //this.editorsyncValue();
24182     },
24183     onFirstFocus: function() {
24184         this.buttons.each(function(item){
24185            item.enable();
24186         });
24187     },
24188     toggleSourceEdit : function(sourceEditMode){
24189         
24190           
24191         if(sourceEditMode){
24192             Roo.log("disabling buttons");
24193            this.buttons.each( function(item){
24194                 if(item.cmd != 'pencil'){
24195                     item.disable();
24196                 }
24197             });
24198           
24199         }else{
24200             Roo.log("enabling buttons");
24201             if(this.editorcore.initialized){
24202                 this.buttons.each( function(item){
24203                     item.enable();
24204                 });
24205             }
24206             
24207         }
24208         Roo.log("calling toggole on editor");
24209         // tell the editor that it's been pressed..
24210         this.editor.toggleSourceEdit(sourceEditMode);
24211        
24212     }
24213 });
24214
24215
24216
24217
24218
24219 /**
24220  * @class Roo.bootstrap.Table.AbstractSelectionModel
24221  * @extends Roo.util.Observable
24222  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24223  * implemented by descendant classes.  This class should not be directly instantiated.
24224  * @constructor
24225  */
24226 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24227     this.locked = false;
24228     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24229 };
24230
24231
24232 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24233     /** @ignore Called by the grid automatically. Do not call directly. */
24234     init : function(grid){
24235         this.grid = grid;
24236         this.initEvents();
24237     },
24238
24239     /**
24240      * Locks the selections.
24241      */
24242     lock : function(){
24243         this.locked = true;
24244     },
24245
24246     /**
24247      * Unlocks the selections.
24248      */
24249     unlock : function(){
24250         this.locked = false;
24251     },
24252
24253     /**
24254      * Returns true if the selections are locked.
24255      * @return {Boolean}
24256      */
24257     isLocked : function(){
24258         return this.locked;
24259     }
24260 });
24261 /**
24262  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24263  * @class Roo.bootstrap.Table.RowSelectionModel
24264  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24265  * It supports multiple selections and keyboard selection/navigation. 
24266  * @constructor
24267  * @param {Object} config
24268  */
24269
24270 Roo.bootstrap.Table.RowSelectionModel = function(config){
24271     Roo.apply(this, config);
24272     this.selections = new Roo.util.MixedCollection(false, function(o){
24273         return o.id;
24274     });
24275
24276     this.last = false;
24277     this.lastActive = false;
24278
24279     this.addEvents({
24280         /**
24281              * @event selectionchange
24282              * Fires when the selection changes
24283              * @param {SelectionModel} this
24284              */
24285             "selectionchange" : true,
24286         /**
24287              * @event afterselectionchange
24288              * Fires after the selection changes (eg. by key press or clicking)
24289              * @param {SelectionModel} this
24290              */
24291             "afterselectionchange" : true,
24292         /**
24293              * @event beforerowselect
24294              * Fires when a row is selected being selected, return false to cancel.
24295              * @param {SelectionModel} this
24296              * @param {Number} rowIndex The selected index
24297              * @param {Boolean} keepExisting False if other selections will be cleared
24298              */
24299             "beforerowselect" : true,
24300         /**
24301              * @event rowselect
24302              * Fires when a row is selected.
24303              * @param {SelectionModel} this
24304              * @param {Number} rowIndex The selected index
24305              * @param {Roo.data.Record} r The record
24306              */
24307             "rowselect" : true,
24308         /**
24309              * @event rowdeselect
24310              * Fires when a row is deselected.
24311              * @param {SelectionModel} this
24312              * @param {Number} rowIndex The selected index
24313              */
24314         "rowdeselect" : true
24315     });
24316     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24317     this.locked = false;
24318  };
24319
24320 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24321     /**
24322      * @cfg {Boolean} singleSelect
24323      * True to allow selection of only one row at a time (defaults to false)
24324      */
24325     singleSelect : false,
24326
24327     // private
24328     initEvents : function()
24329     {
24330
24331         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24332         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24333         //}else{ // allow click to work like normal
24334          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24335         //}
24336         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24337         this.grid.on("rowclick", this.handleMouseDown, this);
24338         
24339         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24340             "up" : function(e){
24341                 if(!e.shiftKey){
24342                     this.selectPrevious(e.shiftKey);
24343                 }else if(this.last !== false && this.lastActive !== false){
24344                     var last = this.last;
24345                     this.selectRange(this.last,  this.lastActive-1);
24346                     this.grid.getView().focusRow(this.lastActive);
24347                     if(last !== false){
24348                         this.last = last;
24349                     }
24350                 }else{
24351                     this.selectFirstRow();
24352                 }
24353                 this.fireEvent("afterselectionchange", this);
24354             },
24355             "down" : function(e){
24356                 if(!e.shiftKey){
24357                     this.selectNext(e.shiftKey);
24358                 }else if(this.last !== false && this.lastActive !== false){
24359                     var last = this.last;
24360                     this.selectRange(this.last,  this.lastActive+1);
24361                     this.grid.getView().focusRow(this.lastActive);
24362                     if(last !== false){
24363                         this.last = last;
24364                     }
24365                 }else{
24366                     this.selectFirstRow();
24367                 }
24368                 this.fireEvent("afterselectionchange", this);
24369             },
24370             scope: this
24371         });
24372         this.grid.store.on('load', function(){
24373             this.selections.clear();
24374         },this);
24375         /*
24376         var view = this.grid.view;
24377         view.on("refresh", this.onRefresh, this);
24378         view.on("rowupdated", this.onRowUpdated, this);
24379         view.on("rowremoved", this.onRemove, this);
24380         */
24381     },
24382
24383     // private
24384     onRefresh : function()
24385     {
24386         var ds = this.grid.store, i, v = this.grid.view;
24387         var s = this.selections;
24388         s.each(function(r){
24389             if((i = ds.indexOfId(r.id)) != -1){
24390                 v.onRowSelect(i);
24391             }else{
24392                 s.remove(r);
24393             }
24394         });
24395     },
24396
24397     // private
24398     onRemove : function(v, index, r){
24399         this.selections.remove(r);
24400     },
24401
24402     // private
24403     onRowUpdated : function(v, index, r){
24404         if(this.isSelected(r)){
24405             v.onRowSelect(index);
24406         }
24407     },
24408
24409     /**
24410      * Select records.
24411      * @param {Array} records The records to select
24412      * @param {Boolean} keepExisting (optional) True to keep existing selections
24413      */
24414     selectRecords : function(records, keepExisting)
24415     {
24416         if(!keepExisting){
24417             this.clearSelections();
24418         }
24419             var ds = this.grid.store;
24420         for(var i = 0, len = records.length; i < len; i++){
24421             this.selectRow(ds.indexOf(records[i]), true);
24422         }
24423     },
24424
24425     /**
24426      * Gets the number of selected rows.
24427      * @return {Number}
24428      */
24429     getCount : function(){
24430         return this.selections.length;
24431     },
24432
24433     /**
24434      * Selects the first row in the grid.
24435      */
24436     selectFirstRow : function(){
24437         this.selectRow(0);
24438     },
24439
24440     /**
24441      * Select the last row.
24442      * @param {Boolean} keepExisting (optional) True to keep existing selections
24443      */
24444     selectLastRow : function(keepExisting){
24445         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24446         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24447     },
24448
24449     /**
24450      * Selects the row immediately following the last selected row.
24451      * @param {Boolean} keepExisting (optional) True to keep existing selections
24452      */
24453     selectNext : function(keepExisting)
24454     {
24455             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24456             this.selectRow(this.last+1, keepExisting);
24457             this.grid.getView().focusRow(this.last);
24458         }
24459     },
24460
24461     /**
24462      * Selects the row that precedes the last selected row.
24463      * @param {Boolean} keepExisting (optional) True to keep existing selections
24464      */
24465     selectPrevious : function(keepExisting){
24466         if(this.last){
24467             this.selectRow(this.last-1, keepExisting);
24468             this.grid.getView().focusRow(this.last);
24469         }
24470     },
24471
24472     /**
24473      * Returns the selected records
24474      * @return {Array} Array of selected records
24475      */
24476     getSelections : function(){
24477         return [].concat(this.selections.items);
24478     },
24479
24480     /**
24481      * Returns the first selected record.
24482      * @return {Record}
24483      */
24484     getSelected : function(){
24485         return this.selections.itemAt(0);
24486     },
24487
24488
24489     /**
24490      * Clears all selections.
24491      */
24492     clearSelections : function(fast)
24493     {
24494         if(this.locked) {
24495             return;
24496         }
24497         if(fast !== true){
24498                 var ds = this.grid.store;
24499             var s = this.selections;
24500             s.each(function(r){
24501                 this.deselectRow(ds.indexOfId(r.id));
24502             }, this);
24503             s.clear();
24504         }else{
24505             this.selections.clear();
24506         }
24507         this.last = false;
24508     },
24509
24510
24511     /**
24512      * Selects all rows.
24513      */
24514     selectAll : function(){
24515         if(this.locked) {
24516             return;
24517         }
24518         this.selections.clear();
24519         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24520             this.selectRow(i, true);
24521         }
24522     },
24523
24524     /**
24525      * Returns True if there is a selection.
24526      * @return {Boolean}
24527      */
24528     hasSelection : function(){
24529         return this.selections.length > 0;
24530     },
24531
24532     /**
24533      * Returns True if the specified row is selected.
24534      * @param {Number/Record} record The record or index of the record to check
24535      * @return {Boolean}
24536      */
24537     isSelected : function(index){
24538             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24539         return (r && this.selections.key(r.id) ? true : false);
24540     },
24541
24542     /**
24543      * Returns True if the specified record id is selected.
24544      * @param {String} id The id of record to check
24545      * @return {Boolean}
24546      */
24547     isIdSelected : function(id){
24548         return (this.selections.key(id) ? true : false);
24549     },
24550
24551
24552     // private
24553     handleMouseDBClick : function(e, t){
24554         
24555     },
24556     // private
24557     handleMouseDown : function(e, t)
24558     {
24559             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24560         if(this.isLocked() || rowIndex < 0 ){
24561             return;
24562         };
24563         if(e.shiftKey && this.last !== false){
24564             var last = this.last;
24565             this.selectRange(last, rowIndex, e.ctrlKey);
24566             this.last = last; // reset the last
24567             t.focus();
24568     
24569         }else{
24570             var isSelected = this.isSelected(rowIndex);
24571             //Roo.log("select row:" + rowIndex);
24572             if(isSelected){
24573                 this.deselectRow(rowIndex);
24574             } else {
24575                         this.selectRow(rowIndex, true);
24576             }
24577     
24578             /*
24579                 if(e.button !== 0 && isSelected){
24580                 alert('rowIndex 2: ' + rowIndex);
24581                     view.focusRow(rowIndex);
24582                 }else if(e.ctrlKey && isSelected){
24583                     this.deselectRow(rowIndex);
24584                 }else if(!isSelected){
24585                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24586                     view.focusRow(rowIndex);
24587                 }
24588             */
24589         }
24590         this.fireEvent("afterselectionchange", this);
24591     },
24592     // private
24593     handleDragableRowClick :  function(grid, rowIndex, e) 
24594     {
24595         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24596             this.selectRow(rowIndex, false);
24597             grid.view.focusRow(rowIndex);
24598              this.fireEvent("afterselectionchange", this);
24599         }
24600     },
24601     
24602     /**
24603      * Selects multiple rows.
24604      * @param {Array} rows Array of the indexes of the row to select
24605      * @param {Boolean} keepExisting (optional) True to keep existing selections
24606      */
24607     selectRows : function(rows, keepExisting){
24608         if(!keepExisting){
24609             this.clearSelections();
24610         }
24611         for(var i = 0, len = rows.length; i < len; i++){
24612             this.selectRow(rows[i], true);
24613         }
24614     },
24615
24616     /**
24617      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24618      * @param {Number} startRow The index of the first row in the range
24619      * @param {Number} endRow The index of the last row in the range
24620      * @param {Boolean} keepExisting (optional) True to retain existing selections
24621      */
24622     selectRange : function(startRow, endRow, keepExisting){
24623         if(this.locked) {
24624             return;
24625         }
24626         if(!keepExisting){
24627             this.clearSelections();
24628         }
24629         if(startRow <= endRow){
24630             for(var i = startRow; i <= endRow; i++){
24631                 this.selectRow(i, true);
24632             }
24633         }else{
24634             for(var i = startRow; i >= endRow; i--){
24635                 this.selectRow(i, true);
24636             }
24637         }
24638     },
24639
24640     /**
24641      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24642      * @param {Number} startRow The index of the first row in the range
24643      * @param {Number} endRow The index of the last row in the range
24644      */
24645     deselectRange : function(startRow, endRow, preventViewNotify){
24646         if(this.locked) {
24647             return;
24648         }
24649         for(var i = startRow; i <= endRow; i++){
24650             this.deselectRow(i, preventViewNotify);
24651         }
24652     },
24653
24654     /**
24655      * Selects a row.
24656      * @param {Number} row The index of the row to select
24657      * @param {Boolean} keepExisting (optional) True to keep existing selections
24658      */
24659     selectRow : function(index, keepExisting, preventViewNotify)
24660     {
24661             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24662             return;
24663         }
24664         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24665             if(!keepExisting || this.singleSelect){
24666                 this.clearSelections();
24667             }
24668             
24669             var r = this.grid.store.getAt(index);
24670             //console.log('selectRow - record id :' + r.id);
24671             
24672             this.selections.add(r);
24673             this.last = this.lastActive = index;
24674             if(!preventViewNotify){
24675                 var proxy = new Roo.Element(
24676                                 this.grid.getRowDom(index)
24677                 );
24678                 proxy.addClass('bg-info info');
24679             }
24680             this.fireEvent("rowselect", this, index, r);
24681             this.fireEvent("selectionchange", this);
24682         }
24683     },
24684
24685     /**
24686      * Deselects a row.
24687      * @param {Number} row The index of the row to deselect
24688      */
24689     deselectRow : function(index, preventViewNotify)
24690     {
24691         if(this.locked) {
24692             return;
24693         }
24694         if(this.last == index){
24695             this.last = false;
24696         }
24697         if(this.lastActive == index){
24698             this.lastActive = false;
24699         }
24700         
24701         var r = this.grid.store.getAt(index);
24702         if (!r) {
24703             return;
24704         }
24705         
24706         this.selections.remove(r);
24707         //.console.log('deselectRow - record id :' + r.id);
24708         if(!preventViewNotify){
24709         
24710             var proxy = new Roo.Element(
24711                 this.grid.getRowDom(index)
24712             );
24713             proxy.removeClass('bg-info info');
24714         }
24715         this.fireEvent("rowdeselect", this, index);
24716         this.fireEvent("selectionchange", this);
24717     },
24718
24719     // private
24720     restoreLast : function(){
24721         if(this._last){
24722             this.last = this._last;
24723         }
24724     },
24725
24726     // private
24727     acceptsNav : function(row, col, cm){
24728         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24729     },
24730
24731     // private
24732     onEditorKey : function(field, e){
24733         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24734         if(k == e.TAB){
24735             e.stopEvent();
24736             ed.completeEdit();
24737             if(e.shiftKey){
24738                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24739             }else{
24740                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24741             }
24742         }else if(k == e.ENTER && !e.ctrlKey){
24743             e.stopEvent();
24744             ed.completeEdit();
24745             if(e.shiftKey){
24746                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24747             }else{
24748                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24749             }
24750         }else if(k == e.ESC){
24751             ed.cancelEdit();
24752         }
24753         if(newCell){
24754             g.startEditing(newCell[0], newCell[1]);
24755         }
24756     }
24757 });
24758 /*
24759  * Based on:
24760  * Ext JS Library 1.1.1
24761  * Copyright(c) 2006-2007, Ext JS, LLC.
24762  *
24763  * Originally Released Under LGPL - original licence link has changed is not relivant.
24764  *
24765  * Fork - LGPL
24766  * <script type="text/javascript">
24767  */
24768  
24769 /**
24770  * @class Roo.bootstrap.PagingToolbar
24771  * @extends Roo.bootstrap.NavSimplebar
24772  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24773  * @constructor
24774  * Create a new PagingToolbar
24775  * @param {Object} config The config object
24776  * @param {Roo.data.Store} store
24777  */
24778 Roo.bootstrap.PagingToolbar = function(config)
24779 {
24780     // old args format still supported... - xtype is prefered..
24781         // created from xtype...
24782     
24783     this.ds = config.dataSource;
24784     
24785     if (config.store && !this.ds) {
24786         this.store= Roo.factory(config.store, Roo.data);
24787         this.ds = this.store;
24788         this.ds.xmodule = this.xmodule || false;
24789     }
24790     
24791     this.toolbarItems = [];
24792     if (config.items) {
24793         this.toolbarItems = config.items;
24794     }
24795     
24796     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24797     
24798     this.cursor = 0;
24799     
24800     if (this.ds) { 
24801         this.bind(this.ds);
24802     }
24803     
24804     if (Roo.bootstrap.version == 4) {
24805         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24806     } else {
24807         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24808     }
24809     
24810 };
24811
24812 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24813     /**
24814      * @cfg {Roo.data.Store} dataSource
24815      * The underlying data store providing the paged data
24816      */
24817     /**
24818      * @cfg {String/HTMLElement/Element} container
24819      * container The id or element that will contain the toolbar
24820      */
24821     /**
24822      * @cfg {Boolean} displayInfo
24823      * True to display the displayMsg (defaults to false)
24824      */
24825     /**
24826      * @cfg {Number} pageSize
24827      * The number of records to display per page (defaults to 20)
24828      */
24829     pageSize: 20,
24830     /**
24831      * @cfg {String} displayMsg
24832      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24833      */
24834     displayMsg : 'Displaying {0} - {1} of {2}',
24835     /**
24836      * @cfg {String} emptyMsg
24837      * The message to display when no records are found (defaults to "No data to display")
24838      */
24839     emptyMsg : 'No data to display',
24840     /**
24841      * Customizable piece of the default paging text (defaults to "Page")
24842      * @type String
24843      */
24844     beforePageText : "Page",
24845     /**
24846      * Customizable piece of the default paging text (defaults to "of %0")
24847      * @type String
24848      */
24849     afterPageText : "of {0}",
24850     /**
24851      * Customizable piece of the default paging text (defaults to "First Page")
24852      * @type String
24853      */
24854     firstText : "First Page",
24855     /**
24856      * Customizable piece of the default paging text (defaults to "Previous Page")
24857      * @type String
24858      */
24859     prevText : "Previous Page",
24860     /**
24861      * Customizable piece of the default paging text (defaults to "Next Page")
24862      * @type String
24863      */
24864     nextText : "Next Page",
24865     /**
24866      * Customizable piece of the default paging text (defaults to "Last Page")
24867      * @type String
24868      */
24869     lastText : "Last Page",
24870     /**
24871      * Customizable piece of the default paging text (defaults to "Refresh")
24872      * @type String
24873      */
24874     refreshText : "Refresh",
24875
24876     buttons : false,
24877     // private
24878     onRender : function(ct, position) 
24879     {
24880         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24881         this.navgroup.parentId = this.id;
24882         this.navgroup.onRender(this.el, null);
24883         // add the buttons to the navgroup
24884         
24885         if(this.displayInfo){
24886             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24887             this.displayEl = this.el.select('.x-paging-info', true).first();
24888 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24889 //            this.displayEl = navel.el.select('span',true).first();
24890         }
24891         
24892         var _this = this;
24893         
24894         if(this.buttons){
24895             Roo.each(_this.buttons, function(e){ // this might need to use render????
24896                Roo.factory(e).render(_this.el);
24897             });
24898         }
24899             
24900         Roo.each(_this.toolbarItems, function(e) {
24901             _this.navgroup.addItem(e);
24902         });
24903         
24904         
24905         this.first = this.navgroup.addItem({
24906             tooltip: this.firstText,
24907             cls: "prev btn-outline-secondary",
24908             html : ' <i class="fa fa-step-backward"></i>',
24909             disabled: true,
24910             preventDefault: true,
24911             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24912         });
24913         
24914         this.prev =  this.navgroup.addItem({
24915             tooltip: this.prevText,
24916             cls: "prev btn-outline-secondary",
24917             html : ' <i class="fa fa-backward"></i>',
24918             disabled: true,
24919             preventDefault: true,
24920             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24921         });
24922     //this.addSeparator();
24923         
24924         
24925         var field = this.navgroup.addItem( {
24926             tagtype : 'span',
24927             cls : 'x-paging-position  btn-outline-secondary',
24928              disabled: true,
24929             html : this.beforePageText  +
24930                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24931                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24932          } ); //?? escaped?
24933         
24934         this.field = field.el.select('input', true).first();
24935         this.field.on("keydown", this.onPagingKeydown, this);
24936         this.field.on("focus", function(){this.dom.select();});
24937     
24938     
24939         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24940         //this.field.setHeight(18);
24941         //this.addSeparator();
24942         this.next = this.navgroup.addItem({
24943             tooltip: this.nextText,
24944             cls: "next btn-outline-secondary",
24945             html : ' <i class="fa fa-forward"></i>',
24946             disabled: true,
24947             preventDefault: true,
24948             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24949         });
24950         this.last = this.navgroup.addItem({
24951             tooltip: this.lastText,
24952             html : ' <i class="fa fa-step-forward"></i>',
24953             cls: "next btn-outline-secondary",
24954             disabled: true,
24955             preventDefault: true,
24956             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24957         });
24958     //this.addSeparator();
24959         this.loading = this.navgroup.addItem({
24960             tooltip: this.refreshText,
24961             cls: "btn-outline-secondary",
24962             html : ' <i class="fa fa-refresh"></i>',
24963             preventDefault: true,
24964             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24965         });
24966         
24967     },
24968
24969     // private
24970     updateInfo : function(){
24971         if(this.displayEl){
24972             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24973             var msg = count == 0 ?
24974                 this.emptyMsg :
24975                 String.format(
24976                     this.displayMsg,
24977                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24978                 );
24979             this.displayEl.update(msg);
24980         }
24981     },
24982
24983     // private
24984     onLoad : function(ds, r, o)
24985     {
24986         this.cursor = o.params.start ? o.params.start : 0;
24987         
24988         var d = this.getPageData(),
24989             ap = d.activePage,
24990             ps = d.pages;
24991         
24992         
24993         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24994         this.field.dom.value = ap;
24995         this.first.setDisabled(ap == 1);
24996         this.prev.setDisabled(ap == 1);
24997         this.next.setDisabled(ap == ps);
24998         this.last.setDisabled(ap == ps);
24999         this.loading.enable();
25000         this.updateInfo();
25001     },
25002
25003     // private
25004     getPageData : function(){
25005         var total = this.ds.getTotalCount();
25006         return {
25007             total : total,
25008             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25009             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25010         };
25011     },
25012
25013     // private
25014     onLoadError : function(){
25015         this.loading.enable();
25016     },
25017
25018     // private
25019     onPagingKeydown : function(e){
25020         var k = e.getKey();
25021         var d = this.getPageData();
25022         if(k == e.RETURN){
25023             var v = this.field.dom.value, pageNum;
25024             if(!v || isNaN(pageNum = parseInt(v, 10))){
25025                 this.field.dom.value = d.activePage;
25026                 return;
25027             }
25028             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25029             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25030             e.stopEvent();
25031         }
25032         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))
25033         {
25034           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25035           this.field.dom.value = pageNum;
25036           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25037           e.stopEvent();
25038         }
25039         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25040         {
25041           var v = this.field.dom.value, pageNum; 
25042           var increment = (e.shiftKey) ? 10 : 1;
25043           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25044                 increment *= -1;
25045           }
25046           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25047             this.field.dom.value = d.activePage;
25048             return;
25049           }
25050           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25051           {
25052             this.field.dom.value = parseInt(v, 10) + increment;
25053             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25054             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25055           }
25056           e.stopEvent();
25057         }
25058     },
25059
25060     // private
25061     beforeLoad : function(){
25062         if(this.loading){
25063             this.loading.disable();
25064         }
25065     },
25066
25067     // private
25068     onClick : function(which){
25069         
25070         var ds = this.ds;
25071         if (!ds) {
25072             return;
25073         }
25074         
25075         switch(which){
25076             case "first":
25077                 ds.load({params:{start: 0, limit: this.pageSize}});
25078             break;
25079             case "prev":
25080                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25081             break;
25082             case "next":
25083                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25084             break;
25085             case "last":
25086                 var total = ds.getTotalCount();
25087                 var extra = total % this.pageSize;
25088                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25089                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25090             break;
25091             case "refresh":
25092                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25093             break;
25094         }
25095     },
25096
25097     /**
25098      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25099      * @param {Roo.data.Store} store The data store to unbind
25100      */
25101     unbind : function(ds){
25102         ds.un("beforeload", this.beforeLoad, this);
25103         ds.un("load", this.onLoad, this);
25104         ds.un("loadexception", this.onLoadError, this);
25105         ds.un("remove", this.updateInfo, this);
25106         ds.un("add", this.updateInfo, this);
25107         this.ds = undefined;
25108     },
25109
25110     /**
25111      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25112      * @param {Roo.data.Store} store The data store to bind
25113      */
25114     bind : function(ds){
25115         ds.on("beforeload", this.beforeLoad, this);
25116         ds.on("load", this.onLoad, this);
25117         ds.on("loadexception", this.onLoadError, this);
25118         ds.on("remove", this.updateInfo, this);
25119         ds.on("add", this.updateInfo, this);
25120         this.ds = ds;
25121     }
25122 });/*
25123  * - LGPL
25124  *
25125  * element
25126  * 
25127  */
25128
25129 /**
25130  * @class Roo.bootstrap.MessageBar
25131  * @extends Roo.bootstrap.Component
25132  * Bootstrap MessageBar class
25133  * @cfg {String} html contents of the MessageBar
25134  * @cfg {String} weight (info | success | warning | danger) default info
25135  * @cfg {String} beforeClass insert the bar before the given class
25136  * @cfg {Boolean} closable (true | false) default false
25137  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25138  * 
25139  * @constructor
25140  * Create a new Element
25141  * @param {Object} config The config object
25142  */
25143
25144 Roo.bootstrap.MessageBar = function(config){
25145     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25146 };
25147
25148 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25149     
25150     html: '',
25151     weight: 'info',
25152     closable: false,
25153     fixed: false,
25154     beforeClass: 'bootstrap-sticky-wrap',
25155     
25156     getAutoCreate : function(){
25157         
25158         var cfg = {
25159             tag: 'div',
25160             cls: 'alert alert-dismissable alert-' + this.weight,
25161             cn: [
25162                 {
25163                     tag: 'span',
25164                     cls: 'message',
25165                     html: this.html || ''
25166                 }
25167             ]
25168         };
25169         
25170         if(this.fixed){
25171             cfg.cls += ' alert-messages-fixed';
25172         }
25173         
25174         if(this.closable){
25175             cfg.cn.push({
25176                 tag: 'button',
25177                 cls: 'close',
25178                 html: 'x'
25179             });
25180         }
25181         
25182         return cfg;
25183     },
25184     
25185     onRender : function(ct, position)
25186     {
25187         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25188         
25189         if(!this.el){
25190             var cfg = Roo.apply({},  this.getAutoCreate());
25191             cfg.id = Roo.id();
25192             
25193             if (this.cls) {
25194                 cfg.cls += ' ' + this.cls;
25195             }
25196             if (this.style) {
25197                 cfg.style = this.style;
25198             }
25199             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25200             
25201             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25202         }
25203         
25204         this.el.select('>button.close').on('click', this.hide, this);
25205         
25206     },
25207     
25208     show : function()
25209     {
25210         if (!this.rendered) {
25211             this.render();
25212         }
25213         
25214         this.el.show();
25215         
25216         this.fireEvent('show', this);
25217         
25218     },
25219     
25220     hide : function()
25221     {
25222         if (!this.rendered) {
25223             this.render();
25224         }
25225         
25226         this.el.hide();
25227         
25228         this.fireEvent('hide', this);
25229     },
25230     
25231     update : function()
25232     {
25233 //        var e = this.el.dom.firstChild;
25234 //        
25235 //        if(this.closable){
25236 //            e = e.nextSibling;
25237 //        }
25238 //        
25239 //        e.data = this.html || '';
25240
25241         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25242     }
25243    
25244 });
25245
25246  
25247
25248      /*
25249  * - LGPL
25250  *
25251  * Graph
25252  * 
25253  */
25254
25255
25256 /**
25257  * @class Roo.bootstrap.Graph
25258  * @extends Roo.bootstrap.Component
25259  * Bootstrap Graph class
25260 > Prameters
25261  -sm {number} sm 4
25262  -md {number} md 5
25263  @cfg {String} graphtype  bar | vbar | pie
25264  @cfg {number} g_x coodinator | centre x (pie)
25265  @cfg {number} g_y coodinator | centre y (pie)
25266  @cfg {number} g_r radius (pie)
25267  @cfg {number} g_height height of the chart (respected by all elements in the set)
25268  @cfg {number} g_width width of the chart (respected by all elements in the set)
25269  @cfg {Object} title The title of the chart
25270     
25271  -{Array}  values
25272  -opts (object) options for the chart 
25273      o {
25274      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25275      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25276      o vgutter (number)
25277      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.
25278      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25279      o to
25280      o stretch (boolean)
25281      o }
25282  -opts (object) options for the pie
25283      o{
25284      o cut
25285      o startAngle (number)
25286      o endAngle (number)
25287      } 
25288  *
25289  * @constructor
25290  * Create a new Input
25291  * @param {Object} config The config object
25292  */
25293
25294 Roo.bootstrap.Graph = function(config){
25295     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25296     
25297     this.addEvents({
25298         // img events
25299         /**
25300          * @event click
25301          * The img click event for the img.
25302          * @param {Roo.EventObject} e
25303          */
25304         "click" : true
25305     });
25306 };
25307
25308 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25309     
25310     sm: 4,
25311     md: 5,
25312     graphtype: 'bar',
25313     g_height: 250,
25314     g_width: 400,
25315     g_x: 50,
25316     g_y: 50,
25317     g_r: 30,
25318     opts:{
25319         //g_colors: this.colors,
25320         g_type: 'soft',
25321         g_gutter: '20%'
25322
25323     },
25324     title : false,
25325
25326     getAutoCreate : function(){
25327         
25328         var cfg = {
25329             tag: 'div',
25330             html : null
25331         };
25332         
25333         
25334         return  cfg;
25335     },
25336
25337     onRender : function(ct,position){
25338         
25339         
25340         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25341         
25342         if (typeof(Raphael) == 'undefined') {
25343             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25344             return;
25345         }
25346         
25347         this.raphael = Raphael(this.el.dom);
25348         
25349                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25350                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25351                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25352                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25353                 /*
25354                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25355                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25356                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25357                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25358                 
25359                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25360                 r.barchart(330, 10, 300, 220, data1);
25361                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25362                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25363                 */
25364                 
25365                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25366                 // r.barchart(30, 30, 560, 250,  xdata, {
25367                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25368                 //     axis : "0 0 1 1",
25369                 //     axisxlabels :  xdata
25370                 //     //yvalues : cols,
25371                    
25372                 // });
25373 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25374 //        
25375 //        this.load(null,xdata,{
25376 //                axis : "0 0 1 1",
25377 //                axisxlabels :  xdata
25378 //                });
25379
25380     },
25381
25382     load : function(graphtype,xdata,opts)
25383     {
25384         this.raphael.clear();
25385         if(!graphtype) {
25386             graphtype = this.graphtype;
25387         }
25388         if(!opts){
25389             opts = this.opts;
25390         }
25391         var r = this.raphael,
25392             fin = function () {
25393                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25394             },
25395             fout = function () {
25396                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25397             },
25398             pfin = function() {
25399                 this.sector.stop();
25400                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25401
25402                 if (this.label) {
25403                     this.label[0].stop();
25404                     this.label[0].attr({ r: 7.5 });
25405                     this.label[1].attr({ "font-weight": 800 });
25406                 }
25407             },
25408             pfout = function() {
25409                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25410
25411                 if (this.label) {
25412                     this.label[0].animate({ r: 5 }, 500, "bounce");
25413                     this.label[1].attr({ "font-weight": 400 });
25414                 }
25415             };
25416
25417         switch(graphtype){
25418             case 'bar':
25419                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25420                 break;
25421             case 'hbar':
25422                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25423                 break;
25424             case 'pie':
25425 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25426 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25427 //            
25428                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25429                 
25430                 break;
25431
25432         }
25433         
25434         if(this.title){
25435             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25436         }
25437         
25438     },
25439     
25440     setTitle: function(o)
25441     {
25442         this.title = o;
25443     },
25444     
25445     initEvents: function() {
25446         
25447         if(!this.href){
25448             this.el.on('click', this.onClick, this);
25449         }
25450     },
25451     
25452     onClick : function(e)
25453     {
25454         Roo.log('img onclick');
25455         this.fireEvent('click', this, e);
25456     }
25457    
25458 });
25459
25460  
25461 /*
25462  * - LGPL
25463  *
25464  * numberBox
25465  * 
25466  */
25467 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25468
25469 /**
25470  * @class Roo.bootstrap.dash.NumberBox
25471  * @extends Roo.bootstrap.Component
25472  * Bootstrap NumberBox class
25473  * @cfg {String} headline Box headline
25474  * @cfg {String} content Box content
25475  * @cfg {String} icon Box icon
25476  * @cfg {String} footer Footer text
25477  * @cfg {String} fhref Footer href
25478  * 
25479  * @constructor
25480  * Create a new NumberBox
25481  * @param {Object} config The config object
25482  */
25483
25484
25485 Roo.bootstrap.dash.NumberBox = function(config){
25486     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25487     
25488 };
25489
25490 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25491     
25492     headline : '',
25493     content : '',
25494     icon : '',
25495     footer : '',
25496     fhref : '',
25497     ficon : '',
25498     
25499     getAutoCreate : function(){
25500         
25501         var cfg = {
25502             tag : 'div',
25503             cls : 'small-box ',
25504             cn : [
25505                 {
25506                     tag : 'div',
25507                     cls : 'inner',
25508                     cn :[
25509                         {
25510                             tag : 'h3',
25511                             cls : 'roo-headline',
25512                             html : this.headline
25513                         },
25514                         {
25515                             tag : 'p',
25516                             cls : 'roo-content',
25517                             html : this.content
25518                         }
25519                     ]
25520                 }
25521             ]
25522         };
25523         
25524         if(this.icon){
25525             cfg.cn.push({
25526                 tag : 'div',
25527                 cls : 'icon',
25528                 cn :[
25529                     {
25530                         tag : 'i',
25531                         cls : 'ion ' + this.icon
25532                     }
25533                 ]
25534             });
25535         }
25536         
25537         if(this.footer){
25538             var footer = {
25539                 tag : 'a',
25540                 cls : 'small-box-footer',
25541                 href : this.fhref || '#',
25542                 html : this.footer
25543             };
25544             
25545             cfg.cn.push(footer);
25546             
25547         }
25548         
25549         return  cfg;
25550     },
25551
25552     onRender : function(ct,position){
25553         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25554
25555
25556        
25557                 
25558     },
25559
25560     setHeadline: function (value)
25561     {
25562         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25563     },
25564     
25565     setFooter: function (value, href)
25566     {
25567         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25568         
25569         if(href){
25570             this.el.select('a.small-box-footer',true).first().attr('href', href);
25571         }
25572         
25573     },
25574
25575     setContent: function (value)
25576     {
25577         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25578     },
25579
25580     initEvents: function() 
25581     {   
25582         
25583     }
25584     
25585 });
25586
25587  
25588 /*
25589  * - LGPL
25590  *
25591  * TabBox
25592  * 
25593  */
25594 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25595
25596 /**
25597  * @class Roo.bootstrap.dash.TabBox
25598  * @extends Roo.bootstrap.Component
25599  * Bootstrap TabBox class
25600  * @cfg {String} title Title of the TabBox
25601  * @cfg {String} icon Icon of the TabBox
25602  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25603  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25604  * 
25605  * @constructor
25606  * Create a new TabBox
25607  * @param {Object} config The config object
25608  */
25609
25610
25611 Roo.bootstrap.dash.TabBox = function(config){
25612     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25613     this.addEvents({
25614         // raw events
25615         /**
25616          * @event addpane
25617          * When a pane is added
25618          * @param {Roo.bootstrap.dash.TabPane} pane
25619          */
25620         "addpane" : true,
25621         /**
25622          * @event activatepane
25623          * When a pane is activated
25624          * @param {Roo.bootstrap.dash.TabPane} pane
25625          */
25626         "activatepane" : true
25627         
25628          
25629     });
25630     
25631     this.panes = [];
25632 };
25633
25634 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25635
25636     title : '',
25637     icon : false,
25638     showtabs : true,
25639     tabScrollable : false,
25640     
25641     getChildContainer : function()
25642     {
25643         return this.el.select('.tab-content', true).first();
25644     },
25645     
25646     getAutoCreate : function(){
25647         
25648         var header = {
25649             tag: 'li',
25650             cls: 'pull-left header',
25651             html: this.title,
25652             cn : []
25653         };
25654         
25655         if(this.icon){
25656             header.cn.push({
25657                 tag: 'i',
25658                 cls: 'fa ' + this.icon
25659             });
25660         }
25661         
25662         var h = {
25663             tag: 'ul',
25664             cls: 'nav nav-tabs pull-right',
25665             cn: [
25666                 header
25667             ]
25668         };
25669         
25670         if(this.tabScrollable){
25671             h = {
25672                 tag: 'div',
25673                 cls: 'tab-header',
25674                 cn: [
25675                     {
25676                         tag: 'ul',
25677                         cls: 'nav nav-tabs pull-right',
25678                         cn: [
25679                             header
25680                         ]
25681                     }
25682                 ]
25683             };
25684         }
25685         
25686         var cfg = {
25687             tag: 'div',
25688             cls: 'nav-tabs-custom',
25689             cn: [
25690                 h,
25691                 {
25692                     tag: 'div',
25693                     cls: 'tab-content no-padding',
25694                     cn: []
25695                 }
25696             ]
25697         };
25698
25699         return  cfg;
25700     },
25701     initEvents : function()
25702     {
25703         //Roo.log('add add pane handler');
25704         this.on('addpane', this.onAddPane, this);
25705     },
25706      /**
25707      * Updates the box title
25708      * @param {String} html to set the title to.
25709      */
25710     setTitle : function(value)
25711     {
25712         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25713     },
25714     onAddPane : function(pane)
25715     {
25716         this.panes.push(pane);
25717         //Roo.log('addpane');
25718         //Roo.log(pane);
25719         // tabs are rendere left to right..
25720         if(!this.showtabs){
25721             return;
25722         }
25723         
25724         var ctr = this.el.select('.nav-tabs', true).first();
25725          
25726          
25727         var existing = ctr.select('.nav-tab',true);
25728         var qty = existing.getCount();;
25729         
25730         
25731         var tab = ctr.createChild({
25732             tag : 'li',
25733             cls : 'nav-tab' + (qty ? '' : ' active'),
25734             cn : [
25735                 {
25736                     tag : 'a',
25737                     href:'#',
25738                     html : pane.title
25739                 }
25740             ]
25741         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25742         pane.tab = tab;
25743         
25744         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25745         if (!qty) {
25746             pane.el.addClass('active');
25747         }
25748         
25749                 
25750     },
25751     onTabClick : function(ev,un,ob,pane)
25752     {
25753         //Roo.log('tab - prev default');
25754         ev.preventDefault();
25755         
25756         
25757         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25758         pane.tab.addClass('active');
25759         //Roo.log(pane.title);
25760         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25761         // technically we should have a deactivate event.. but maybe add later.
25762         // and it should not de-activate the selected tab...
25763         this.fireEvent('activatepane', pane);
25764         pane.el.addClass('active');
25765         pane.fireEvent('activate');
25766         
25767         
25768     },
25769     
25770     getActivePane : function()
25771     {
25772         var r = false;
25773         Roo.each(this.panes, function(p) {
25774             if(p.el.hasClass('active')){
25775                 r = p;
25776                 return false;
25777             }
25778             
25779             return;
25780         });
25781         
25782         return r;
25783     }
25784     
25785     
25786 });
25787
25788  
25789 /*
25790  * - LGPL
25791  *
25792  * Tab pane
25793  * 
25794  */
25795 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25796 /**
25797  * @class Roo.bootstrap.TabPane
25798  * @extends Roo.bootstrap.Component
25799  * Bootstrap TabPane class
25800  * @cfg {Boolean} active (false | true) Default false
25801  * @cfg {String} title title of panel
25802
25803  * 
25804  * @constructor
25805  * Create a new TabPane
25806  * @param {Object} config The config object
25807  */
25808
25809 Roo.bootstrap.dash.TabPane = function(config){
25810     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25811     
25812     this.addEvents({
25813         // raw events
25814         /**
25815          * @event activate
25816          * When a pane is activated
25817          * @param {Roo.bootstrap.dash.TabPane} pane
25818          */
25819         "activate" : true
25820          
25821     });
25822 };
25823
25824 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25825     
25826     active : false,
25827     title : '',
25828     
25829     // the tabBox that this is attached to.
25830     tab : false,
25831      
25832     getAutoCreate : function() 
25833     {
25834         var cfg = {
25835             tag: 'div',
25836             cls: 'tab-pane'
25837         };
25838         
25839         if(this.active){
25840             cfg.cls += ' active';
25841         }
25842         
25843         return cfg;
25844     },
25845     initEvents  : function()
25846     {
25847         //Roo.log('trigger add pane handler');
25848         this.parent().fireEvent('addpane', this)
25849     },
25850     
25851      /**
25852      * Updates the tab title 
25853      * @param {String} html to set the title to.
25854      */
25855     setTitle: function(str)
25856     {
25857         if (!this.tab) {
25858             return;
25859         }
25860         this.title = str;
25861         this.tab.select('a', true).first().dom.innerHTML = str;
25862         
25863     }
25864     
25865     
25866     
25867 });
25868
25869  
25870
25871
25872  /*
25873  * - LGPL
25874  *
25875  * menu
25876  * 
25877  */
25878 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25879
25880 /**
25881  * @class Roo.bootstrap.menu.Menu
25882  * @extends Roo.bootstrap.Component
25883  * Bootstrap Menu class - container for Menu
25884  * @cfg {String} html Text of the menu
25885  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25886  * @cfg {String} icon Font awesome icon
25887  * @cfg {String} pos Menu align to (top | bottom) default bottom
25888  * 
25889  * 
25890  * @constructor
25891  * Create a new Menu
25892  * @param {Object} config The config object
25893  */
25894
25895
25896 Roo.bootstrap.menu.Menu = function(config){
25897     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25898     
25899     this.addEvents({
25900         /**
25901          * @event beforeshow
25902          * Fires before this menu is displayed
25903          * @param {Roo.bootstrap.menu.Menu} this
25904          */
25905         beforeshow : true,
25906         /**
25907          * @event beforehide
25908          * Fires before this menu is hidden
25909          * @param {Roo.bootstrap.menu.Menu} this
25910          */
25911         beforehide : true,
25912         /**
25913          * @event show
25914          * Fires after this menu is displayed
25915          * @param {Roo.bootstrap.menu.Menu} this
25916          */
25917         show : true,
25918         /**
25919          * @event hide
25920          * Fires after this menu is hidden
25921          * @param {Roo.bootstrap.menu.Menu} this
25922          */
25923         hide : true,
25924         /**
25925          * @event click
25926          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25927          * @param {Roo.bootstrap.menu.Menu} this
25928          * @param {Roo.EventObject} e
25929          */
25930         click : true
25931     });
25932     
25933 };
25934
25935 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25936     
25937     submenu : false,
25938     html : '',
25939     weight : 'default',
25940     icon : false,
25941     pos : 'bottom',
25942     
25943     
25944     getChildContainer : function() {
25945         if(this.isSubMenu){
25946             return this.el;
25947         }
25948         
25949         return this.el.select('ul.dropdown-menu', true).first();  
25950     },
25951     
25952     getAutoCreate : function()
25953     {
25954         var text = [
25955             {
25956                 tag : 'span',
25957                 cls : 'roo-menu-text',
25958                 html : this.html
25959             }
25960         ];
25961         
25962         if(this.icon){
25963             text.unshift({
25964                 tag : 'i',
25965                 cls : 'fa ' + this.icon
25966             })
25967         }
25968         
25969         
25970         var cfg = {
25971             tag : 'div',
25972             cls : 'btn-group',
25973             cn : [
25974                 {
25975                     tag : 'button',
25976                     cls : 'dropdown-button btn btn-' + this.weight,
25977                     cn : text
25978                 },
25979                 {
25980                     tag : 'button',
25981                     cls : 'dropdown-toggle btn btn-' + this.weight,
25982                     cn : [
25983                         {
25984                             tag : 'span',
25985                             cls : 'caret'
25986                         }
25987                     ]
25988                 },
25989                 {
25990                     tag : 'ul',
25991                     cls : 'dropdown-menu'
25992                 }
25993             ]
25994             
25995         };
25996         
25997         if(this.pos == 'top'){
25998             cfg.cls += ' dropup';
25999         }
26000         
26001         if(this.isSubMenu){
26002             cfg = {
26003                 tag : 'ul',
26004                 cls : 'dropdown-menu'
26005             }
26006         }
26007         
26008         return cfg;
26009     },
26010     
26011     onRender : function(ct, position)
26012     {
26013         this.isSubMenu = ct.hasClass('dropdown-submenu');
26014         
26015         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26016     },
26017     
26018     initEvents : function() 
26019     {
26020         if(this.isSubMenu){
26021             return;
26022         }
26023         
26024         this.hidden = true;
26025         
26026         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26027         this.triggerEl.on('click', this.onTriggerPress, this);
26028         
26029         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26030         this.buttonEl.on('click', this.onClick, this);
26031         
26032     },
26033     
26034     list : function()
26035     {
26036         if(this.isSubMenu){
26037             return this.el;
26038         }
26039         
26040         return this.el.select('ul.dropdown-menu', true).first();
26041     },
26042     
26043     onClick : function(e)
26044     {
26045         this.fireEvent("click", this, e);
26046     },
26047     
26048     onTriggerPress  : function(e)
26049     {   
26050         if (this.isVisible()) {
26051             this.hide();
26052         } else {
26053             this.show();
26054         }
26055     },
26056     
26057     isVisible : function(){
26058         return !this.hidden;
26059     },
26060     
26061     show : function()
26062     {
26063         this.fireEvent("beforeshow", this);
26064         
26065         this.hidden = false;
26066         this.el.addClass('open');
26067         
26068         Roo.get(document).on("mouseup", this.onMouseUp, this);
26069         
26070         this.fireEvent("show", this);
26071         
26072         
26073     },
26074     
26075     hide : function()
26076     {
26077         this.fireEvent("beforehide", this);
26078         
26079         this.hidden = true;
26080         this.el.removeClass('open');
26081         
26082         Roo.get(document).un("mouseup", this.onMouseUp);
26083         
26084         this.fireEvent("hide", this);
26085     },
26086     
26087     onMouseUp : function()
26088     {
26089         this.hide();
26090     }
26091     
26092 });
26093
26094  
26095  /*
26096  * - LGPL
26097  *
26098  * menu item
26099  * 
26100  */
26101 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26102
26103 /**
26104  * @class Roo.bootstrap.menu.Item
26105  * @extends Roo.bootstrap.Component
26106  * Bootstrap MenuItem class
26107  * @cfg {Boolean} submenu (true | false) default false
26108  * @cfg {String} html text of the item
26109  * @cfg {String} href the link
26110  * @cfg {Boolean} disable (true | false) default false
26111  * @cfg {Boolean} preventDefault (true | false) default true
26112  * @cfg {String} icon Font awesome icon
26113  * @cfg {String} pos Submenu align to (left | right) default right 
26114  * 
26115  * 
26116  * @constructor
26117  * Create a new Item
26118  * @param {Object} config The config object
26119  */
26120
26121
26122 Roo.bootstrap.menu.Item = function(config){
26123     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26124     this.addEvents({
26125         /**
26126          * @event mouseover
26127          * Fires when the mouse is hovering over this menu
26128          * @param {Roo.bootstrap.menu.Item} this
26129          * @param {Roo.EventObject} e
26130          */
26131         mouseover : true,
26132         /**
26133          * @event mouseout
26134          * Fires when the mouse exits this menu
26135          * @param {Roo.bootstrap.menu.Item} this
26136          * @param {Roo.EventObject} e
26137          */
26138         mouseout : true,
26139         // raw events
26140         /**
26141          * @event click
26142          * The raw click event for the entire grid.
26143          * @param {Roo.EventObject} e
26144          */
26145         click : true
26146     });
26147 };
26148
26149 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26150     
26151     submenu : false,
26152     href : '',
26153     html : '',
26154     preventDefault: true,
26155     disable : false,
26156     icon : false,
26157     pos : 'right',
26158     
26159     getAutoCreate : function()
26160     {
26161         var text = [
26162             {
26163                 tag : 'span',
26164                 cls : 'roo-menu-item-text',
26165                 html : this.html
26166             }
26167         ];
26168         
26169         if(this.icon){
26170             text.unshift({
26171                 tag : 'i',
26172                 cls : 'fa ' + this.icon
26173             })
26174         }
26175         
26176         var cfg = {
26177             tag : 'li',
26178             cn : [
26179                 {
26180                     tag : 'a',
26181                     href : this.href || '#',
26182                     cn : text
26183                 }
26184             ]
26185         };
26186         
26187         if(this.disable){
26188             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26189         }
26190         
26191         if(this.submenu){
26192             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26193             
26194             if(this.pos == 'left'){
26195                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26196             }
26197         }
26198         
26199         return cfg;
26200     },
26201     
26202     initEvents : function() 
26203     {
26204         this.el.on('mouseover', this.onMouseOver, this);
26205         this.el.on('mouseout', this.onMouseOut, this);
26206         
26207         this.el.select('a', true).first().on('click', this.onClick, this);
26208         
26209     },
26210     
26211     onClick : function(e)
26212     {
26213         if(this.preventDefault){
26214             e.preventDefault();
26215         }
26216         
26217         this.fireEvent("click", this, e);
26218     },
26219     
26220     onMouseOver : function(e)
26221     {
26222         if(this.submenu && this.pos == 'left'){
26223             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26224         }
26225         
26226         this.fireEvent("mouseover", this, e);
26227     },
26228     
26229     onMouseOut : function(e)
26230     {
26231         this.fireEvent("mouseout", this, e);
26232     }
26233 });
26234
26235  
26236
26237  /*
26238  * - LGPL
26239  *
26240  * menu separator
26241  * 
26242  */
26243 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26244
26245 /**
26246  * @class Roo.bootstrap.menu.Separator
26247  * @extends Roo.bootstrap.Component
26248  * Bootstrap Separator class
26249  * 
26250  * @constructor
26251  * Create a new Separator
26252  * @param {Object} config The config object
26253  */
26254
26255
26256 Roo.bootstrap.menu.Separator = function(config){
26257     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26258 };
26259
26260 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26261     
26262     getAutoCreate : function(){
26263         var cfg = {
26264             tag : 'li',
26265             cls: 'divider'
26266         };
26267         
26268         return cfg;
26269     }
26270    
26271 });
26272
26273  
26274
26275  /*
26276  * - LGPL
26277  *
26278  * Tooltip
26279  * 
26280  */
26281
26282 /**
26283  * @class Roo.bootstrap.Tooltip
26284  * Bootstrap Tooltip class
26285  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26286  * to determine which dom element triggers the tooltip.
26287  * 
26288  * It needs to add support for additional attributes like tooltip-position
26289  * 
26290  * @constructor
26291  * Create a new Toolti
26292  * @param {Object} config The config object
26293  */
26294
26295 Roo.bootstrap.Tooltip = function(config){
26296     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26297     
26298     this.alignment = Roo.bootstrap.Tooltip.alignment;
26299     
26300     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26301         this.alignment = config.alignment;
26302     }
26303     
26304 };
26305
26306 Roo.apply(Roo.bootstrap.Tooltip, {
26307     /**
26308      * @function init initialize tooltip monitoring.
26309      * @static
26310      */
26311     currentEl : false,
26312     currentTip : false,
26313     currentRegion : false,
26314     
26315     //  init : delay?
26316     
26317     init : function()
26318     {
26319         Roo.get(document).on('mouseover', this.enter ,this);
26320         Roo.get(document).on('mouseout', this.leave, this);
26321          
26322         
26323         this.currentTip = new Roo.bootstrap.Tooltip();
26324     },
26325     
26326     enter : function(ev)
26327     {
26328         var dom = ev.getTarget();
26329         
26330         //Roo.log(['enter',dom]);
26331         var el = Roo.fly(dom);
26332         if (this.currentEl) {
26333             //Roo.log(dom);
26334             //Roo.log(this.currentEl);
26335             //Roo.log(this.currentEl.contains(dom));
26336             if (this.currentEl == el) {
26337                 return;
26338             }
26339             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26340                 return;
26341             }
26342
26343         }
26344         
26345         if (this.currentTip.el) {
26346             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26347         }    
26348         //Roo.log(ev);
26349         
26350         if(!el || el.dom == document){
26351             return;
26352         }
26353         
26354         var bindEl = el;
26355         
26356         // you can not look for children, as if el is the body.. then everythign is the child..
26357         if (!el.attr('tooltip')) { //
26358             if (!el.select("[tooltip]").elements.length) {
26359                 return;
26360             }
26361             // is the mouse over this child...?
26362             bindEl = el.select("[tooltip]").first();
26363             var xy = ev.getXY();
26364             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26365                 //Roo.log("not in region.");
26366                 return;
26367             }
26368             //Roo.log("child element over..");
26369             
26370         }
26371         this.currentEl = bindEl;
26372         this.currentTip.bind(bindEl);
26373         this.currentRegion = Roo.lib.Region.getRegion(dom);
26374         this.currentTip.enter();
26375         
26376     },
26377     leave : function(ev)
26378     {
26379         var dom = ev.getTarget();
26380         //Roo.log(['leave',dom]);
26381         if (!this.currentEl) {
26382             return;
26383         }
26384         
26385         
26386         if (dom != this.currentEl.dom) {
26387             return;
26388         }
26389         var xy = ev.getXY();
26390         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26391             return;
26392         }
26393         // only activate leave if mouse cursor is outside... bounding box..
26394         
26395         
26396         
26397         
26398         if (this.currentTip) {
26399             this.currentTip.leave();
26400         }
26401         //Roo.log('clear currentEl');
26402         this.currentEl = false;
26403         
26404         
26405     },
26406     alignment : {
26407         'left' : ['r-l', [-2,0], 'right'],
26408         'right' : ['l-r', [2,0], 'left'],
26409         'bottom' : ['t-b', [0,2], 'top'],
26410         'top' : [ 'b-t', [0,-2], 'bottom']
26411     }
26412     
26413 });
26414
26415
26416 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26417     
26418     
26419     bindEl : false,
26420     
26421     delay : null, // can be { show : 300 , hide: 500}
26422     
26423     timeout : null,
26424     
26425     hoverState : null, //???
26426     
26427     placement : 'bottom', 
26428     
26429     alignment : false,
26430     
26431     getAutoCreate : function(){
26432     
26433         var cfg = {
26434            cls : 'tooltip',
26435            role : 'tooltip',
26436            cn : [
26437                 {
26438                     cls : 'tooltip-arrow'
26439                 },
26440                 {
26441                     cls : 'tooltip-inner'
26442                 }
26443            ]
26444         };
26445         
26446         return cfg;
26447     },
26448     bind : function(el)
26449     {
26450         this.bindEl = el;
26451     },
26452       
26453     
26454     enter : function () {
26455        
26456         if (this.timeout != null) {
26457             clearTimeout(this.timeout);
26458         }
26459         
26460         this.hoverState = 'in';
26461          //Roo.log("enter - show");
26462         if (!this.delay || !this.delay.show) {
26463             this.show();
26464             return;
26465         }
26466         var _t = this;
26467         this.timeout = setTimeout(function () {
26468             if (_t.hoverState == 'in') {
26469                 _t.show();
26470             }
26471         }, this.delay.show);
26472     },
26473     leave : function()
26474     {
26475         clearTimeout(this.timeout);
26476     
26477         this.hoverState = 'out';
26478          if (!this.delay || !this.delay.hide) {
26479             this.hide();
26480             return;
26481         }
26482        
26483         var _t = this;
26484         this.timeout = setTimeout(function () {
26485             //Roo.log("leave - timeout");
26486             
26487             if (_t.hoverState == 'out') {
26488                 _t.hide();
26489                 Roo.bootstrap.Tooltip.currentEl = false;
26490             }
26491         }, delay);
26492     },
26493     
26494     show : function (msg)
26495     {
26496         if (!this.el) {
26497             this.render(document.body);
26498         }
26499         // set content.
26500         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26501         
26502         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26503         
26504         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26505         
26506         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26507         
26508         var placement = typeof this.placement == 'function' ?
26509             this.placement.call(this, this.el, on_el) :
26510             this.placement;
26511             
26512         var autoToken = /\s?auto?\s?/i;
26513         var autoPlace = autoToken.test(placement);
26514         if (autoPlace) {
26515             placement = placement.replace(autoToken, '') || 'top';
26516         }
26517         
26518         //this.el.detach()
26519         //this.el.setXY([0,0]);
26520         this.el.show();
26521         //this.el.dom.style.display='block';
26522         
26523         //this.el.appendTo(on_el);
26524         
26525         var p = this.getPosition();
26526         var box = this.el.getBox();
26527         
26528         if (autoPlace) {
26529             // fixme..
26530         }
26531         
26532         var align = this.alignment[placement];
26533         
26534         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26535         
26536         if(placement == 'top' || placement == 'bottom'){
26537             if(xy[0] < 0){
26538                 placement = 'right';
26539             }
26540             
26541             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26542                 placement = 'left';
26543             }
26544             
26545             var scroll = Roo.select('body', true).first().getScroll();
26546             
26547             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26548                 placement = 'top';
26549             }
26550             
26551             align = this.alignment[placement];
26552         }
26553         
26554         this.el.alignTo(this.bindEl, align[0],align[1]);
26555         //var arrow = this.el.select('.arrow',true).first();
26556         //arrow.set(align[2], 
26557         
26558         this.el.addClass(placement);
26559         
26560         this.el.addClass('in fade');
26561         
26562         this.hoverState = null;
26563         
26564         if (this.el.hasClass('fade')) {
26565             // fade it?
26566         }
26567         
26568     },
26569     hide : function()
26570     {
26571          
26572         if (!this.el) {
26573             return;
26574         }
26575         //this.el.setXY([0,0]);
26576         this.el.removeClass('in');
26577         //this.el.hide();
26578         
26579     }
26580     
26581 });
26582  
26583
26584  /*
26585  * - LGPL
26586  *
26587  * Location Picker
26588  * 
26589  */
26590
26591 /**
26592  * @class Roo.bootstrap.LocationPicker
26593  * @extends Roo.bootstrap.Component
26594  * Bootstrap LocationPicker class
26595  * @cfg {Number} latitude Position when init default 0
26596  * @cfg {Number} longitude Position when init default 0
26597  * @cfg {Number} zoom default 15
26598  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26599  * @cfg {Boolean} mapTypeControl default false
26600  * @cfg {Boolean} disableDoubleClickZoom default false
26601  * @cfg {Boolean} scrollwheel default true
26602  * @cfg {Boolean} streetViewControl default false
26603  * @cfg {Number} radius default 0
26604  * @cfg {String} locationName
26605  * @cfg {Boolean} draggable default true
26606  * @cfg {Boolean} enableAutocomplete default false
26607  * @cfg {Boolean} enableReverseGeocode default true
26608  * @cfg {String} markerTitle
26609  * 
26610  * @constructor
26611  * Create a new LocationPicker
26612  * @param {Object} config The config object
26613  */
26614
26615
26616 Roo.bootstrap.LocationPicker = function(config){
26617     
26618     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26619     
26620     this.addEvents({
26621         /**
26622          * @event initial
26623          * Fires when the picker initialized.
26624          * @param {Roo.bootstrap.LocationPicker} this
26625          * @param {Google Location} location
26626          */
26627         initial : true,
26628         /**
26629          * @event positionchanged
26630          * Fires when the picker position changed.
26631          * @param {Roo.bootstrap.LocationPicker} this
26632          * @param {Google Location} location
26633          */
26634         positionchanged : true,
26635         /**
26636          * @event resize
26637          * Fires when the map resize.
26638          * @param {Roo.bootstrap.LocationPicker} this
26639          */
26640         resize : true,
26641         /**
26642          * @event show
26643          * Fires when the map show.
26644          * @param {Roo.bootstrap.LocationPicker} this
26645          */
26646         show : true,
26647         /**
26648          * @event hide
26649          * Fires when the map hide.
26650          * @param {Roo.bootstrap.LocationPicker} this
26651          */
26652         hide : true,
26653         /**
26654          * @event mapClick
26655          * Fires when click the map.
26656          * @param {Roo.bootstrap.LocationPicker} this
26657          * @param {Map event} e
26658          */
26659         mapClick : true,
26660         /**
26661          * @event mapRightClick
26662          * Fires when right click the map.
26663          * @param {Roo.bootstrap.LocationPicker} this
26664          * @param {Map event} e
26665          */
26666         mapRightClick : true,
26667         /**
26668          * @event markerClick
26669          * Fires when click the marker.
26670          * @param {Roo.bootstrap.LocationPicker} this
26671          * @param {Map event} e
26672          */
26673         markerClick : true,
26674         /**
26675          * @event markerRightClick
26676          * Fires when right click the marker.
26677          * @param {Roo.bootstrap.LocationPicker} this
26678          * @param {Map event} e
26679          */
26680         markerRightClick : true,
26681         /**
26682          * @event OverlayViewDraw
26683          * Fires when OverlayView Draw
26684          * @param {Roo.bootstrap.LocationPicker} this
26685          */
26686         OverlayViewDraw : true,
26687         /**
26688          * @event OverlayViewOnAdd
26689          * Fires when OverlayView Draw
26690          * @param {Roo.bootstrap.LocationPicker} this
26691          */
26692         OverlayViewOnAdd : true,
26693         /**
26694          * @event OverlayViewOnRemove
26695          * Fires when OverlayView Draw
26696          * @param {Roo.bootstrap.LocationPicker} this
26697          */
26698         OverlayViewOnRemove : true,
26699         /**
26700          * @event OverlayViewShow
26701          * Fires when OverlayView Draw
26702          * @param {Roo.bootstrap.LocationPicker} this
26703          * @param {Pixel} cpx
26704          */
26705         OverlayViewShow : true,
26706         /**
26707          * @event OverlayViewHide
26708          * Fires when OverlayView Draw
26709          * @param {Roo.bootstrap.LocationPicker} this
26710          */
26711         OverlayViewHide : true,
26712         /**
26713          * @event loadexception
26714          * Fires when load google lib failed.
26715          * @param {Roo.bootstrap.LocationPicker} this
26716          */
26717         loadexception : true
26718     });
26719         
26720 };
26721
26722 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26723     
26724     gMapContext: false,
26725     
26726     latitude: 0,
26727     longitude: 0,
26728     zoom: 15,
26729     mapTypeId: false,
26730     mapTypeControl: false,
26731     disableDoubleClickZoom: false,
26732     scrollwheel: true,
26733     streetViewControl: false,
26734     radius: 0,
26735     locationName: '',
26736     draggable: true,
26737     enableAutocomplete: false,
26738     enableReverseGeocode: true,
26739     markerTitle: '',
26740     
26741     getAutoCreate: function()
26742     {
26743
26744         var cfg = {
26745             tag: 'div',
26746             cls: 'roo-location-picker'
26747         };
26748         
26749         return cfg
26750     },
26751     
26752     initEvents: function(ct, position)
26753     {       
26754         if(!this.el.getWidth() || this.isApplied()){
26755             return;
26756         }
26757         
26758         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26759         
26760         this.initial();
26761     },
26762     
26763     initial: function()
26764     {
26765         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26766             this.fireEvent('loadexception', this);
26767             return;
26768         }
26769         
26770         if(!this.mapTypeId){
26771             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26772         }
26773         
26774         this.gMapContext = this.GMapContext();
26775         
26776         this.initOverlayView();
26777         
26778         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26779         
26780         var _this = this;
26781                 
26782         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26783             _this.setPosition(_this.gMapContext.marker.position);
26784         });
26785         
26786         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26787             _this.fireEvent('mapClick', this, event);
26788             
26789         });
26790
26791         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26792             _this.fireEvent('mapRightClick', this, event);
26793             
26794         });
26795         
26796         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26797             _this.fireEvent('markerClick', this, event);
26798             
26799         });
26800
26801         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26802             _this.fireEvent('markerRightClick', this, event);
26803             
26804         });
26805         
26806         this.setPosition(this.gMapContext.location);
26807         
26808         this.fireEvent('initial', this, this.gMapContext.location);
26809     },
26810     
26811     initOverlayView: function()
26812     {
26813         var _this = this;
26814         
26815         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26816             
26817             draw: function()
26818             {
26819                 _this.fireEvent('OverlayViewDraw', _this);
26820             },
26821             
26822             onAdd: function()
26823             {
26824                 _this.fireEvent('OverlayViewOnAdd', _this);
26825             },
26826             
26827             onRemove: function()
26828             {
26829                 _this.fireEvent('OverlayViewOnRemove', _this);
26830             },
26831             
26832             show: function(cpx)
26833             {
26834                 _this.fireEvent('OverlayViewShow', _this, cpx);
26835             },
26836             
26837             hide: function()
26838             {
26839                 _this.fireEvent('OverlayViewHide', _this);
26840             }
26841             
26842         });
26843     },
26844     
26845     fromLatLngToContainerPixel: function(event)
26846     {
26847         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26848     },
26849     
26850     isApplied: function() 
26851     {
26852         return this.getGmapContext() == false ? false : true;
26853     },
26854     
26855     getGmapContext: function() 
26856     {
26857         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26858     },
26859     
26860     GMapContext: function() 
26861     {
26862         var position = new google.maps.LatLng(this.latitude, this.longitude);
26863         
26864         var _map = new google.maps.Map(this.el.dom, {
26865             center: position,
26866             zoom: this.zoom,
26867             mapTypeId: this.mapTypeId,
26868             mapTypeControl: this.mapTypeControl,
26869             disableDoubleClickZoom: this.disableDoubleClickZoom,
26870             scrollwheel: this.scrollwheel,
26871             streetViewControl: this.streetViewControl,
26872             locationName: this.locationName,
26873             draggable: this.draggable,
26874             enableAutocomplete: this.enableAutocomplete,
26875             enableReverseGeocode: this.enableReverseGeocode
26876         });
26877         
26878         var _marker = new google.maps.Marker({
26879             position: position,
26880             map: _map,
26881             title: this.markerTitle,
26882             draggable: this.draggable
26883         });
26884         
26885         return {
26886             map: _map,
26887             marker: _marker,
26888             circle: null,
26889             location: position,
26890             radius: this.radius,
26891             locationName: this.locationName,
26892             addressComponents: {
26893                 formatted_address: null,
26894                 addressLine1: null,
26895                 addressLine2: null,
26896                 streetName: null,
26897                 streetNumber: null,
26898                 city: null,
26899                 district: null,
26900                 state: null,
26901                 stateOrProvince: null
26902             },
26903             settings: this,
26904             domContainer: this.el.dom,
26905             geodecoder: new google.maps.Geocoder()
26906         };
26907     },
26908     
26909     drawCircle: function(center, radius, options) 
26910     {
26911         if (this.gMapContext.circle != null) {
26912             this.gMapContext.circle.setMap(null);
26913         }
26914         if (radius > 0) {
26915             radius *= 1;
26916             options = Roo.apply({}, options, {
26917                 strokeColor: "#0000FF",
26918                 strokeOpacity: .35,
26919                 strokeWeight: 2,
26920                 fillColor: "#0000FF",
26921                 fillOpacity: .2
26922             });
26923             
26924             options.map = this.gMapContext.map;
26925             options.radius = radius;
26926             options.center = center;
26927             this.gMapContext.circle = new google.maps.Circle(options);
26928             return this.gMapContext.circle;
26929         }
26930         
26931         return null;
26932     },
26933     
26934     setPosition: function(location) 
26935     {
26936         this.gMapContext.location = location;
26937         this.gMapContext.marker.setPosition(location);
26938         this.gMapContext.map.panTo(location);
26939         this.drawCircle(location, this.gMapContext.radius, {});
26940         
26941         var _this = this;
26942         
26943         if (this.gMapContext.settings.enableReverseGeocode) {
26944             this.gMapContext.geodecoder.geocode({
26945                 latLng: this.gMapContext.location
26946             }, function(results, status) {
26947                 
26948                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26949                     _this.gMapContext.locationName = results[0].formatted_address;
26950                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26951                     
26952                     _this.fireEvent('positionchanged', this, location);
26953                 }
26954             });
26955             
26956             return;
26957         }
26958         
26959         this.fireEvent('positionchanged', this, location);
26960     },
26961     
26962     resize: function()
26963     {
26964         google.maps.event.trigger(this.gMapContext.map, "resize");
26965         
26966         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26967         
26968         this.fireEvent('resize', this);
26969     },
26970     
26971     setPositionByLatLng: function(latitude, longitude)
26972     {
26973         this.setPosition(new google.maps.LatLng(latitude, longitude));
26974     },
26975     
26976     getCurrentPosition: function() 
26977     {
26978         return {
26979             latitude: this.gMapContext.location.lat(),
26980             longitude: this.gMapContext.location.lng()
26981         };
26982     },
26983     
26984     getAddressName: function() 
26985     {
26986         return this.gMapContext.locationName;
26987     },
26988     
26989     getAddressComponents: function() 
26990     {
26991         return this.gMapContext.addressComponents;
26992     },
26993     
26994     address_component_from_google_geocode: function(address_components) 
26995     {
26996         var result = {};
26997         
26998         for (var i = 0; i < address_components.length; i++) {
26999             var component = address_components[i];
27000             if (component.types.indexOf("postal_code") >= 0) {
27001                 result.postalCode = component.short_name;
27002             } else if (component.types.indexOf("street_number") >= 0) {
27003                 result.streetNumber = component.short_name;
27004             } else if (component.types.indexOf("route") >= 0) {
27005                 result.streetName = component.short_name;
27006             } else if (component.types.indexOf("neighborhood") >= 0) {
27007                 result.city = component.short_name;
27008             } else if (component.types.indexOf("locality") >= 0) {
27009                 result.city = component.short_name;
27010             } else if (component.types.indexOf("sublocality") >= 0) {
27011                 result.district = component.short_name;
27012             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27013                 result.stateOrProvince = component.short_name;
27014             } else if (component.types.indexOf("country") >= 0) {
27015                 result.country = component.short_name;
27016             }
27017         }
27018         
27019         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27020         result.addressLine2 = "";
27021         return result;
27022     },
27023     
27024     setZoomLevel: function(zoom)
27025     {
27026         this.gMapContext.map.setZoom(zoom);
27027     },
27028     
27029     show: function()
27030     {
27031         if(!this.el){
27032             return;
27033         }
27034         
27035         this.el.show();
27036         
27037         this.resize();
27038         
27039         this.fireEvent('show', this);
27040     },
27041     
27042     hide: function()
27043     {
27044         if(!this.el){
27045             return;
27046         }
27047         
27048         this.el.hide();
27049         
27050         this.fireEvent('hide', this);
27051     }
27052     
27053 });
27054
27055 Roo.apply(Roo.bootstrap.LocationPicker, {
27056     
27057     OverlayView : function(map, options)
27058     {
27059         options = options || {};
27060         
27061         this.setMap(map);
27062     }
27063     
27064     
27065 });/*
27066  * - LGPL
27067  *
27068  * Alert
27069  * 
27070  */
27071
27072 /**
27073  * @class Roo.bootstrap.Alert
27074  * @extends Roo.bootstrap.Component
27075  * Bootstrap Alert class
27076  * @cfg {String} title The title of alert
27077  * @cfg {String} html The content of alert
27078  * @cfg {String} weight (  success | info | warning | danger )
27079  * @cfg {String} faicon font-awesomeicon
27080  * 
27081  * @constructor
27082  * Create a new alert
27083  * @param {Object} config The config object
27084  */
27085
27086
27087 Roo.bootstrap.Alert = function(config){
27088     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27089     
27090 };
27091
27092 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27093     
27094     title: '',
27095     html: '',
27096     weight: false,
27097     faicon: false,
27098     
27099     getAutoCreate : function()
27100     {
27101         
27102         var cfg = {
27103             tag : 'div',
27104             cls : 'alert',
27105             cn : [
27106                 {
27107                     tag : 'i',
27108                     cls : 'roo-alert-icon'
27109                     
27110                 },
27111                 {
27112                     tag : 'b',
27113                     cls : 'roo-alert-title',
27114                     html : this.title
27115                 },
27116                 {
27117                     tag : 'span',
27118                     cls : 'roo-alert-text',
27119                     html : this.html
27120                 }
27121             ]
27122         };
27123         
27124         if(this.faicon){
27125             cfg.cn[0].cls += ' fa ' + this.faicon;
27126         }
27127         
27128         if(this.weight){
27129             cfg.cls += ' alert-' + this.weight;
27130         }
27131         
27132         return cfg;
27133     },
27134     
27135     initEvents: function() 
27136     {
27137         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27138     },
27139     
27140     setTitle : function(str)
27141     {
27142         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27143     },
27144     
27145     setText : function(str)
27146     {
27147         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27148     },
27149     
27150     setWeight : function(weight)
27151     {
27152         if(this.weight){
27153             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27154         }
27155         
27156         this.weight = weight;
27157         
27158         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27159     },
27160     
27161     setIcon : function(icon)
27162     {
27163         if(this.faicon){
27164             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27165         }
27166         
27167         this.faicon = icon;
27168         
27169         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27170     },
27171     
27172     hide: function() 
27173     {
27174         this.el.hide();   
27175     },
27176     
27177     show: function() 
27178     {  
27179         this.el.show();   
27180     }
27181     
27182 });
27183
27184  
27185 /*
27186 * Licence: LGPL
27187 */
27188
27189 /**
27190  * @class Roo.bootstrap.UploadCropbox
27191  * @extends Roo.bootstrap.Component
27192  * Bootstrap UploadCropbox class
27193  * @cfg {String} emptyText show when image has been loaded
27194  * @cfg {String} rotateNotify show when image too small to rotate
27195  * @cfg {Number} errorTimeout default 3000
27196  * @cfg {Number} minWidth default 300
27197  * @cfg {Number} minHeight default 300
27198  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27199  * @cfg {Boolean} isDocument (true|false) default false
27200  * @cfg {String} url action url
27201  * @cfg {String} paramName default 'imageUpload'
27202  * @cfg {String} method default POST
27203  * @cfg {Boolean} loadMask (true|false) default true
27204  * @cfg {Boolean} loadingText default 'Loading...'
27205  * 
27206  * @constructor
27207  * Create a new UploadCropbox
27208  * @param {Object} config The config object
27209  */
27210
27211 Roo.bootstrap.UploadCropbox = function(config){
27212     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27213     
27214     this.addEvents({
27215         /**
27216          * @event beforeselectfile
27217          * Fire before select file
27218          * @param {Roo.bootstrap.UploadCropbox} this
27219          */
27220         "beforeselectfile" : true,
27221         /**
27222          * @event initial
27223          * Fire after initEvent
27224          * @param {Roo.bootstrap.UploadCropbox} this
27225          */
27226         "initial" : true,
27227         /**
27228          * @event crop
27229          * Fire after initEvent
27230          * @param {Roo.bootstrap.UploadCropbox} this
27231          * @param {String} data
27232          */
27233         "crop" : true,
27234         /**
27235          * @event prepare
27236          * Fire when preparing the file data
27237          * @param {Roo.bootstrap.UploadCropbox} this
27238          * @param {Object} file
27239          */
27240         "prepare" : true,
27241         /**
27242          * @event exception
27243          * Fire when get exception
27244          * @param {Roo.bootstrap.UploadCropbox} this
27245          * @param {XMLHttpRequest} xhr
27246          */
27247         "exception" : true,
27248         /**
27249          * @event beforeloadcanvas
27250          * Fire before load the canvas
27251          * @param {Roo.bootstrap.UploadCropbox} this
27252          * @param {String} src
27253          */
27254         "beforeloadcanvas" : true,
27255         /**
27256          * @event trash
27257          * Fire when trash image
27258          * @param {Roo.bootstrap.UploadCropbox} this
27259          */
27260         "trash" : true,
27261         /**
27262          * @event download
27263          * Fire when download the image
27264          * @param {Roo.bootstrap.UploadCropbox} this
27265          */
27266         "download" : true,
27267         /**
27268          * @event footerbuttonclick
27269          * Fire when footerbuttonclick
27270          * @param {Roo.bootstrap.UploadCropbox} this
27271          * @param {String} type
27272          */
27273         "footerbuttonclick" : true,
27274         /**
27275          * @event resize
27276          * Fire when resize
27277          * @param {Roo.bootstrap.UploadCropbox} this
27278          */
27279         "resize" : true,
27280         /**
27281          * @event rotate
27282          * Fire when rotate the image
27283          * @param {Roo.bootstrap.UploadCropbox} this
27284          * @param {String} pos
27285          */
27286         "rotate" : true,
27287         /**
27288          * @event inspect
27289          * Fire when inspect the file
27290          * @param {Roo.bootstrap.UploadCropbox} this
27291          * @param {Object} file
27292          */
27293         "inspect" : true,
27294         /**
27295          * @event upload
27296          * Fire when xhr upload the file
27297          * @param {Roo.bootstrap.UploadCropbox} this
27298          * @param {Object} data
27299          */
27300         "upload" : true,
27301         /**
27302          * @event arrange
27303          * Fire when arrange the file data
27304          * @param {Roo.bootstrap.UploadCropbox} this
27305          * @param {Object} formData
27306          */
27307         "arrange" : true
27308     });
27309     
27310     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27311 };
27312
27313 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27314     
27315     emptyText : 'Click to upload image',
27316     rotateNotify : 'Image is too small to rotate',
27317     errorTimeout : 3000,
27318     scale : 0,
27319     baseScale : 1,
27320     rotate : 0,
27321     dragable : false,
27322     pinching : false,
27323     mouseX : 0,
27324     mouseY : 0,
27325     cropData : false,
27326     minWidth : 300,
27327     minHeight : 300,
27328     file : false,
27329     exif : {},
27330     baseRotate : 1,
27331     cropType : 'image/jpeg',
27332     buttons : false,
27333     canvasLoaded : false,
27334     isDocument : false,
27335     method : 'POST',
27336     paramName : 'imageUpload',
27337     loadMask : true,
27338     loadingText : 'Loading...',
27339     maskEl : false,
27340     
27341     getAutoCreate : function()
27342     {
27343         var cfg = {
27344             tag : 'div',
27345             cls : 'roo-upload-cropbox',
27346             cn : [
27347                 {
27348                     tag : 'input',
27349                     cls : 'roo-upload-cropbox-selector',
27350                     type : 'file'
27351                 },
27352                 {
27353                     tag : 'div',
27354                     cls : 'roo-upload-cropbox-body',
27355                     style : 'cursor:pointer',
27356                     cn : [
27357                         {
27358                             tag : 'div',
27359                             cls : 'roo-upload-cropbox-preview'
27360                         },
27361                         {
27362                             tag : 'div',
27363                             cls : 'roo-upload-cropbox-thumb'
27364                         },
27365                         {
27366                             tag : 'div',
27367                             cls : 'roo-upload-cropbox-empty-notify',
27368                             html : this.emptyText
27369                         },
27370                         {
27371                             tag : 'div',
27372                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27373                             html : this.rotateNotify
27374                         }
27375                     ]
27376                 },
27377                 {
27378                     tag : 'div',
27379                     cls : 'roo-upload-cropbox-footer',
27380                     cn : {
27381                         tag : 'div',
27382                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27383                         cn : []
27384                     }
27385                 }
27386             ]
27387         };
27388         
27389         return cfg;
27390     },
27391     
27392     onRender : function(ct, position)
27393     {
27394         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27395         
27396         if (this.buttons.length) {
27397             
27398             Roo.each(this.buttons, function(bb) {
27399                 
27400                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27401                 
27402                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27403                 
27404             }, this);
27405         }
27406         
27407         if(this.loadMask){
27408             this.maskEl = this.el;
27409         }
27410     },
27411     
27412     initEvents : function()
27413     {
27414         this.urlAPI = (window.createObjectURL && window) || 
27415                                 (window.URL && URL.revokeObjectURL && URL) || 
27416                                 (window.webkitURL && webkitURL);
27417                         
27418         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27419         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27420         
27421         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27422         this.selectorEl.hide();
27423         
27424         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27425         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27426         
27427         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27428         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27429         this.thumbEl.hide();
27430         
27431         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27432         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27433         
27434         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27435         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27436         this.errorEl.hide();
27437         
27438         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27439         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27440         this.footerEl.hide();
27441         
27442         this.setThumbBoxSize();
27443         
27444         this.bind();
27445         
27446         this.resize();
27447         
27448         this.fireEvent('initial', this);
27449     },
27450
27451     bind : function()
27452     {
27453         var _this = this;
27454         
27455         window.addEventListener("resize", function() { _this.resize(); } );
27456         
27457         this.bodyEl.on('click', this.beforeSelectFile, this);
27458         
27459         if(Roo.isTouch){
27460             this.bodyEl.on('touchstart', this.onTouchStart, this);
27461             this.bodyEl.on('touchmove', this.onTouchMove, this);
27462             this.bodyEl.on('touchend', this.onTouchEnd, this);
27463         }
27464         
27465         if(!Roo.isTouch){
27466             this.bodyEl.on('mousedown', this.onMouseDown, this);
27467             this.bodyEl.on('mousemove', this.onMouseMove, this);
27468             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27469             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27470             Roo.get(document).on('mouseup', this.onMouseUp, this);
27471         }
27472         
27473         this.selectorEl.on('change', this.onFileSelected, this);
27474     },
27475     
27476     reset : function()
27477     {    
27478         this.scale = 0;
27479         this.baseScale = 1;
27480         this.rotate = 0;
27481         this.baseRotate = 1;
27482         this.dragable = false;
27483         this.pinching = false;
27484         this.mouseX = 0;
27485         this.mouseY = 0;
27486         this.cropData = false;
27487         this.notifyEl.dom.innerHTML = this.emptyText;
27488         
27489         this.selectorEl.dom.value = '';
27490         
27491     },
27492     
27493     resize : function()
27494     {
27495         if(this.fireEvent('resize', this) != false){
27496             this.setThumbBoxPosition();
27497             this.setCanvasPosition();
27498         }
27499     },
27500     
27501     onFooterButtonClick : function(e, el, o, type)
27502     {
27503         switch (type) {
27504             case 'rotate-left' :
27505                 this.onRotateLeft(e);
27506                 break;
27507             case 'rotate-right' :
27508                 this.onRotateRight(e);
27509                 break;
27510             case 'picture' :
27511                 this.beforeSelectFile(e);
27512                 break;
27513             case 'trash' :
27514                 this.trash(e);
27515                 break;
27516             case 'crop' :
27517                 this.crop(e);
27518                 break;
27519             case 'download' :
27520                 this.download(e);
27521                 break;
27522             default :
27523                 break;
27524         }
27525         
27526         this.fireEvent('footerbuttonclick', this, type);
27527     },
27528     
27529     beforeSelectFile : function(e)
27530     {
27531         e.preventDefault();
27532         
27533         if(this.fireEvent('beforeselectfile', this) != false){
27534             this.selectorEl.dom.click();
27535         }
27536     },
27537     
27538     onFileSelected : function(e)
27539     {
27540         e.preventDefault();
27541         
27542         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27543             return;
27544         }
27545         
27546         var file = this.selectorEl.dom.files[0];
27547         
27548         if(this.fireEvent('inspect', this, file) != false){
27549             this.prepare(file);
27550         }
27551         
27552     },
27553     
27554     trash : function(e)
27555     {
27556         this.fireEvent('trash', this);
27557     },
27558     
27559     download : function(e)
27560     {
27561         this.fireEvent('download', this);
27562     },
27563     
27564     loadCanvas : function(src)
27565     {   
27566         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27567             
27568             this.reset();
27569             
27570             this.imageEl = document.createElement('img');
27571             
27572             var _this = this;
27573             
27574             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27575             
27576             this.imageEl.src = src;
27577         }
27578     },
27579     
27580     onLoadCanvas : function()
27581     {   
27582         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27583         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27584         
27585         this.bodyEl.un('click', this.beforeSelectFile, this);
27586         
27587         this.notifyEl.hide();
27588         this.thumbEl.show();
27589         this.footerEl.show();
27590         
27591         this.baseRotateLevel();
27592         
27593         if(this.isDocument){
27594             this.setThumbBoxSize();
27595         }
27596         
27597         this.setThumbBoxPosition();
27598         
27599         this.baseScaleLevel();
27600         
27601         this.draw();
27602         
27603         this.resize();
27604         
27605         this.canvasLoaded = true;
27606         
27607         if(this.loadMask){
27608             this.maskEl.unmask();
27609         }
27610         
27611     },
27612     
27613     setCanvasPosition : function()
27614     {   
27615         if(!this.canvasEl){
27616             return;
27617         }
27618         
27619         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27620         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27621         
27622         this.previewEl.setLeft(pw);
27623         this.previewEl.setTop(ph);
27624         
27625     },
27626     
27627     onMouseDown : function(e)
27628     {   
27629         e.stopEvent();
27630         
27631         this.dragable = true;
27632         this.pinching = false;
27633         
27634         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27635             this.dragable = false;
27636             return;
27637         }
27638         
27639         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27640         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27641         
27642     },
27643     
27644     onMouseMove : function(e)
27645     {   
27646         e.stopEvent();
27647         
27648         if(!this.canvasLoaded){
27649             return;
27650         }
27651         
27652         if (!this.dragable){
27653             return;
27654         }
27655         
27656         var minX = Math.ceil(this.thumbEl.getLeft(true));
27657         var minY = Math.ceil(this.thumbEl.getTop(true));
27658         
27659         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27660         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27661         
27662         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27663         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27664         
27665         x = x - this.mouseX;
27666         y = y - this.mouseY;
27667         
27668         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27669         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27670         
27671         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27672         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27673         
27674         this.previewEl.setLeft(bgX);
27675         this.previewEl.setTop(bgY);
27676         
27677         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27678         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27679     },
27680     
27681     onMouseUp : function(e)
27682     {   
27683         e.stopEvent();
27684         
27685         this.dragable = false;
27686     },
27687     
27688     onMouseWheel : function(e)
27689     {   
27690         e.stopEvent();
27691         
27692         this.startScale = this.scale;
27693         
27694         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27695         
27696         if(!this.zoomable()){
27697             this.scale = this.startScale;
27698             return;
27699         }
27700         
27701         this.draw();
27702         
27703         return;
27704     },
27705     
27706     zoomable : function()
27707     {
27708         var minScale = this.thumbEl.getWidth() / this.minWidth;
27709         
27710         if(this.minWidth < this.minHeight){
27711             minScale = this.thumbEl.getHeight() / this.minHeight;
27712         }
27713         
27714         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27715         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27716         
27717         if(
27718                 this.isDocument &&
27719                 (this.rotate == 0 || this.rotate == 180) && 
27720                 (
27721                     width > this.imageEl.OriginWidth || 
27722                     height > this.imageEl.OriginHeight ||
27723                     (width < this.minWidth && height < this.minHeight)
27724                 )
27725         ){
27726             return false;
27727         }
27728         
27729         if(
27730                 this.isDocument &&
27731                 (this.rotate == 90 || this.rotate == 270) && 
27732                 (
27733                     width > this.imageEl.OriginWidth || 
27734                     height > this.imageEl.OriginHeight ||
27735                     (width < this.minHeight && height < this.minWidth)
27736                 )
27737         ){
27738             return false;
27739         }
27740         
27741         if(
27742                 !this.isDocument &&
27743                 (this.rotate == 0 || this.rotate == 180) && 
27744                 (
27745                     width < this.minWidth || 
27746                     width > this.imageEl.OriginWidth || 
27747                     height < this.minHeight || 
27748                     height > this.imageEl.OriginHeight
27749                 )
27750         ){
27751             return false;
27752         }
27753         
27754         if(
27755                 !this.isDocument &&
27756                 (this.rotate == 90 || this.rotate == 270) && 
27757                 (
27758                     width < this.minHeight || 
27759                     width > this.imageEl.OriginWidth || 
27760                     height < this.minWidth || 
27761                     height > this.imageEl.OriginHeight
27762                 )
27763         ){
27764             return false;
27765         }
27766         
27767         return true;
27768         
27769     },
27770     
27771     onRotateLeft : function(e)
27772     {   
27773         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27774             
27775             var minScale = this.thumbEl.getWidth() / this.minWidth;
27776             
27777             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27778             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27779             
27780             this.startScale = this.scale;
27781             
27782             while (this.getScaleLevel() < minScale){
27783             
27784                 this.scale = this.scale + 1;
27785                 
27786                 if(!this.zoomable()){
27787                     break;
27788                 }
27789                 
27790                 if(
27791                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27792                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27793                 ){
27794                     continue;
27795                 }
27796                 
27797                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27798
27799                 this.draw();
27800                 
27801                 return;
27802             }
27803             
27804             this.scale = this.startScale;
27805             
27806             this.onRotateFail();
27807             
27808             return false;
27809         }
27810         
27811         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27812
27813         if(this.isDocument){
27814             this.setThumbBoxSize();
27815             this.setThumbBoxPosition();
27816             this.setCanvasPosition();
27817         }
27818         
27819         this.draw();
27820         
27821         this.fireEvent('rotate', this, 'left');
27822         
27823     },
27824     
27825     onRotateRight : function(e)
27826     {
27827         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27828             
27829             var minScale = this.thumbEl.getWidth() / this.minWidth;
27830         
27831             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27832             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27833             
27834             this.startScale = this.scale;
27835             
27836             while (this.getScaleLevel() < minScale){
27837             
27838                 this.scale = this.scale + 1;
27839                 
27840                 if(!this.zoomable()){
27841                     break;
27842                 }
27843                 
27844                 if(
27845                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27846                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27847                 ){
27848                     continue;
27849                 }
27850                 
27851                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27852
27853                 this.draw();
27854                 
27855                 return;
27856             }
27857             
27858             this.scale = this.startScale;
27859             
27860             this.onRotateFail();
27861             
27862             return false;
27863         }
27864         
27865         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27866
27867         if(this.isDocument){
27868             this.setThumbBoxSize();
27869             this.setThumbBoxPosition();
27870             this.setCanvasPosition();
27871         }
27872         
27873         this.draw();
27874         
27875         this.fireEvent('rotate', this, 'right');
27876     },
27877     
27878     onRotateFail : function()
27879     {
27880         this.errorEl.show(true);
27881         
27882         var _this = this;
27883         
27884         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27885     },
27886     
27887     draw : function()
27888     {
27889         this.previewEl.dom.innerHTML = '';
27890         
27891         var canvasEl = document.createElement("canvas");
27892         
27893         var contextEl = canvasEl.getContext("2d");
27894         
27895         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27896         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27897         var center = this.imageEl.OriginWidth / 2;
27898         
27899         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27900             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27901             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27902             center = this.imageEl.OriginHeight / 2;
27903         }
27904         
27905         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27906         
27907         contextEl.translate(center, center);
27908         contextEl.rotate(this.rotate * Math.PI / 180);
27909
27910         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27911         
27912         this.canvasEl = document.createElement("canvas");
27913         
27914         this.contextEl = this.canvasEl.getContext("2d");
27915         
27916         switch (this.rotate) {
27917             case 0 :
27918                 
27919                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27920                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27921                 
27922                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27923                 
27924                 break;
27925             case 90 : 
27926                 
27927                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27928                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27929                 
27930                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27931                     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);
27932                     break;
27933                 }
27934                 
27935                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27936                 
27937                 break;
27938             case 180 :
27939                 
27940                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27941                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27942                 
27943                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27944                     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);
27945                     break;
27946                 }
27947                 
27948                 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);
27949                 
27950                 break;
27951             case 270 :
27952                 
27953                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27954                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27955         
27956                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27957                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27958                     break;
27959                 }
27960                 
27961                 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);
27962                 
27963                 break;
27964             default : 
27965                 break;
27966         }
27967         
27968         this.previewEl.appendChild(this.canvasEl);
27969         
27970         this.setCanvasPosition();
27971     },
27972     
27973     crop : function()
27974     {
27975         if(!this.canvasLoaded){
27976             return;
27977         }
27978         
27979         var imageCanvas = document.createElement("canvas");
27980         
27981         var imageContext = imageCanvas.getContext("2d");
27982         
27983         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27984         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27985         
27986         var center = imageCanvas.width / 2;
27987         
27988         imageContext.translate(center, center);
27989         
27990         imageContext.rotate(this.rotate * Math.PI / 180);
27991         
27992         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27993         
27994         var canvas = document.createElement("canvas");
27995         
27996         var context = canvas.getContext("2d");
27997                 
27998         canvas.width = this.minWidth;
27999         canvas.height = this.minHeight;
28000
28001         switch (this.rotate) {
28002             case 0 :
28003                 
28004                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28005                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28006                 
28007                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28008                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28009                 
28010                 var targetWidth = this.minWidth - 2 * x;
28011                 var targetHeight = this.minHeight - 2 * y;
28012                 
28013                 var scale = 1;
28014                 
28015                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28016                     scale = targetWidth / width;
28017                 }
28018                 
28019                 if(x > 0 && y == 0){
28020                     scale = targetHeight / height;
28021                 }
28022                 
28023                 if(x > 0 && y > 0){
28024                     scale = targetWidth / width;
28025                     
28026                     if(width < height){
28027                         scale = targetHeight / height;
28028                     }
28029                 }
28030                 
28031                 context.scale(scale, scale);
28032                 
28033                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28034                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28035
28036                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28037                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28038
28039                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28040                 
28041                 break;
28042             case 90 : 
28043                 
28044                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28045                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28046                 
28047                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28048                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28049                 
28050                 var targetWidth = this.minWidth - 2 * x;
28051                 var targetHeight = this.minHeight - 2 * y;
28052                 
28053                 var scale = 1;
28054                 
28055                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28056                     scale = targetWidth / width;
28057                 }
28058                 
28059                 if(x > 0 && y == 0){
28060                     scale = targetHeight / height;
28061                 }
28062                 
28063                 if(x > 0 && y > 0){
28064                     scale = targetWidth / width;
28065                     
28066                     if(width < height){
28067                         scale = targetHeight / height;
28068                     }
28069                 }
28070                 
28071                 context.scale(scale, scale);
28072                 
28073                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28074                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28075
28076                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28077                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28078                 
28079                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28080                 
28081                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28082                 
28083                 break;
28084             case 180 :
28085                 
28086                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28087                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28088                 
28089                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28090                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28091                 
28092                 var targetWidth = this.minWidth - 2 * x;
28093                 var targetHeight = this.minHeight - 2 * y;
28094                 
28095                 var scale = 1;
28096                 
28097                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28098                     scale = targetWidth / width;
28099                 }
28100                 
28101                 if(x > 0 && y == 0){
28102                     scale = targetHeight / height;
28103                 }
28104                 
28105                 if(x > 0 && y > 0){
28106                     scale = targetWidth / width;
28107                     
28108                     if(width < height){
28109                         scale = targetHeight / height;
28110                     }
28111                 }
28112                 
28113                 context.scale(scale, scale);
28114                 
28115                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28116                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28117
28118                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28119                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28120
28121                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28122                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28123                 
28124                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28125                 
28126                 break;
28127             case 270 :
28128                 
28129                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28130                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28131                 
28132                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28133                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28134                 
28135                 var targetWidth = this.minWidth - 2 * x;
28136                 var targetHeight = this.minHeight - 2 * y;
28137                 
28138                 var scale = 1;
28139                 
28140                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28141                     scale = targetWidth / width;
28142                 }
28143                 
28144                 if(x > 0 && y == 0){
28145                     scale = targetHeight / height;
28146                 }
28147                 
28148                 if(x > 0 && y > 0){
28149                     scale = targetWidth / width;
28150                     
28151                     if(width < height){
28152                         scale = targetHeight / height;
28153                     }
28154                 }
28155                 
28156                 context.scale(scale, scale);
28157                 
28158                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28159                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28160
28161                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28162                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28163                 
28164                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28165                 
28166                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28167                 
28168                 break;
28169             default : 
28170                 break;
28171         }
28172         
28173         this.cropData = canvas.toDataURL(this.cropType);
28174         
28175         if(this.fireEvent('crop', this, this.cropData) !== false){
28176             this.process(this.file, this.cropData);
28177         }
28178         
28179         return;
28180         
28181     },
28182     
28183     setThumbBoxSize : function()
28184     {
28185         var width, height;
28186         
28187         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28188             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28189             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28190             
28191             this.minWidth = width;
28192             this.minHeight = height;
28193             
28194             if(this.rotate == 90 || this.rotate == 270){
28195                 this.minWidth = height;
28196                 this.minHeight = width;
28197             }
28198         }
28199         
28200         height = 300;
28201         width = Math.ceil(this.minWidth * height / this.minHeight);
28202         
28203         if(this.minWidth > this.minHeight){
28204             width = 300;
28205             height = Math.ceil(this.minHeight * width / this.minWidth);
28206         }
28207         
28208         this.thumbEl.setStyle({
28209             width : width + 'px',
28210             height : height + 'px'
28211         });
28212
28213         return;
28214             
28215     },
28216     
28217     setThumbBoxPosition : function()
28218     {
28219         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28220         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28221         
28222         this.thumbEl.setLeft(x);
28223         this.thumbEl.setTop(y);
28224         
28225     },
28226     
28227     baseRotateLevel : function()
28228     {
28229         this.baseRotate = 1;
28230         
28231         if(
28232                 typeof(this.exif) != 'undefined' &&
28233                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28234                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28235         ){
28236             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28237         }
28238         
28239         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28240         
28241     },
28242     
28243     baseScaleLevel : function()
28244     {
28245         var width, height;
28246         
28247         if(this.isDocument){
28248             
28249             if(this.baseRotate == 6 || this.baseRotate == 8){
28250             
28251                 height = this.thumbEl.getHeight();
28252                 this.baseScale = height / this.imageEl.OriginWidth;
28253
28254                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28255                     width = this.thumbEl.getWidth();
28256                     this.baseScale = width / this.imageEl.OriginHeight;
28257                 }
28258
28259                 return;
28260             }
28261
28262             height = this.thumbEl.getHeight();
28263             this.baseScale = height / this.imageEl.OriginHeight;
28264
28265             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28266                 width = this.thumbEl.getWidth();
28267                 this.baseScale = width / this.imageEl.OriginWidth;
28268             }
28269
28270             return;
28271         }
28272         
28273         if(this.baseRotate == 6 || this.baseRotate == 8){
28274             
28275             width = this.thumbEl.getHeight();
28276             this.baseScale = width / this.imageEl.OriginHeight;
28277             
28278             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28279                 height = this.thumbEl.getWidth();
28280                 this.baseScale = height / this.imageEl.OriginHeight;
28281             }
28282             
28283             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28284                 height = this.thumbEl.getWidth();
28285                 this.baseScale = height / this.imageEl.OriginHeight;
28286                 
28287                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28288                     width = this.thumbEl.getHeight();
28289                     this.baseScale = width / this.imageEl.OriginWidth;
28290                 }
28291             }
28292             
28293             return;
28294         }
28295         
28296         width = this.thumbEl.getWidth();
28297         this.baseScale = width / this.imageEl.OriginWidth;
28298         
28299         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28300             height = this.thumbEl.getHeight();
28301             this.baseScale = height / this.imageEl.OriginHeight;
28302         }
28303         
28304         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28305             
28306             height = this.thumbEl.getHeight();
28307             this.baseScale = height / this.imageEl.OriginHeight;
28308             
28309             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28310                 width = this.thumbEl.getWidth();
28311                 this.baseScale = width / this.imageEl.OriginWidth;
28312             }
28313             
28314         }
28315         
28316         return;
28317     },
28318     
28319     getScaleLevel : function()
28320     {
28321         return this.baseScale * Math.pow(1.1, this.scale);
28322     },
28323     
28324     onTouchStart : function(e)
28325     {
28326         if(!this.canvasLoaded){
28327             this.beforeSelectFile(e);
28328             return;
28329         }
28330         
28331         var touches = e.browserEvent.touches;
28332         
28333         if(!touches){
28334             return;
28335         }
28336         
28337         if(touches.length == 1){
28338             this.onMouseDown(e);
28339             return;
28340         }
28341         
28342         if(touches.length != 2){
28343             return;
28344         }
28345         
28346         var coords = [];
28347         
28348         for(var i = 0, finger; finger = touches[i]; i++){
28349             coords.push(finger.pageX, finger.pageY);
28350         }
28351         
28352         var x = Math.pow(coords[0] - coords[2], 2);
28353         var y = Math.pow(coords[1] - coords[3], 2);
28354         
28355         this.startDistance = Math.sqrt(x + y);
28356         
28357         this.startScale = this.scale;
28358         
28359         this.pinching = true;
28360         this.dragable = false;
28361         
28362     },
28363     
28364     onTouchMove : function(e)
28365     {
28366         if(!this.pinching && !this.dragable){
28367             return;
28368         }
28369         
28370         var touches = e.browserEvent.touches;
28371         
28372         if(!touches){
28373             return;
28374         }
28375         
28376         if(this.dragable){
28377             this.onMouseMove(e);
28378             return;
28379         }
28380         
28381         var coords = [];
28382         
28383         for(var i = 0, finger; finger = touches[i]; i++){
28384             coords.push(finger.pageX, finger.pageY);
28385         }
28386         
28387         var x = Math.pow(coords[0] - coords[2], 2);
28388         var y = Math.pow(coords[1] - coords[3], 2);
28389         
28390         this.endDistance = Math.sqrt(x + y);
28391         
28392         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28393         
28394         if(!this.zoomable()){
28395             this.scale = this.startScale;
28396             return;
28397         }
28398         
28399         this.draw();
28400         
28401     },
28402     
28403     onTouchEnd : function(e)
28404     {
28405         this.pinching = false;
28406         this.dragable = false;
28407         
28408     },
28409     
28410     process : function(file, crop)
28411     {
28412         if(this.loadMask){
28413             this.maskEl.mask(this.loadingText);
28414         }
28415         
28416         this.xhr = new XMLHttpRequest();
28417         
28418         file.xhr = this.xhr;
28419
28420         this.xhr.open(this.method, this.url, true);
28421         
28422         var headers = {
28423             "Accept": "application/json",
28424             "Cache-Control": "no-cache",
28425             "X-Requested-With": "XMLHttpRequest"
28426         };
28427         
28428         for (var headerName in headers) {
28429             var headerValue = headers[headerName];
28430             if (headerValue) {
28431                 this.xhr.setRequestHeader(headerName, headerValue);
28432             }
28433         }
28434         
28435         var _this = this;
28436         
28437         this.xhr.onload = function()
28438         {
28439             _this.xhrOnLoad(_this.xhr);
28440         }
28441         
28442         this.xhr.onerror = function()
28443         {
28444             _this.xhrOnError(_this.xhr);
28445         }
28446         
28447         var formData = new FormData();
28448
28449         formData.append('returnHTML', 'NO');
28450         
28451         if(crop){
28452             formData.append('crop', crop);
28453         }
28454         
28455         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28456             formData.append(this.paramName, file, file.name);
28457         }
28458         
28459         if(typeof(file.filename) != 'undefined'){
28460             formData.append('filename', file.filename);
28461         }
28462         
28463         if(typeof(file.mimetype) != 'undefined'){
28464             formData.append('mimetype', file.mimetype);
28465         }
28466         
28467         if(this.fireEvent('arrange', this, formData) != false){
28468             this.xhr.send(formData);
28469         };
28470     },
28471     
28472     xhrOnLoad : function(xhr)
28473     {
28474         if(this.loadMask){
28475             this.maskEl.unmask();
28476         }
28477         
28478         if (xhr.readyState !== 4) {
28479             this.fireEvent('exception', this, xhr);
28480             return;
28481         }
28482
28483         var response = Roo.decode(xhr.responseText);
28484         
28485         if(!response.success){
28486             this.fireEvent('exception', this, xhr);
28487             return;
28488         }
28489         
28490         var response = Roo.decode(xhr.responseText);
28491         
28492         this.fireEvent('upload', this, response);
28493         
28494     },
28495     
28496     xhrOnError : function()
28497     {
28498         if(this.loadMask){
28499             this.maskEl.unmask();
28500         }
28501         
28502         Roo.log('xhr on error');
28503         
28504         var response = Roo.decode(xhr.responseText);
28505           
28506         Roo.log(response);
28507         
28508     },
28509     
28510     prepare : function(file)
28511     {   
28512         if(this.loadMask){
28513             this.maskEl.mask(this.loadingText);
28514         }
28515         
28516         this.file = false;
28517         this.exif = {};
28518         
28519         if(typeof(file) === 'string'){
28520             this.loadCanvas(file);
28521             return;
28522         }
28523         
28524         if(!file || !this.urlAPI){
28525             return;
28526         }
28527         
28528         this.file = file;
28529         this.cropType = file.type;
28530         
28531         var _this = this;
28532         
28533         if(this.fireEvent('prepare', this, this.file) != false){
28534             
28535             var reader = new FileReader();
28536             
28537             reader.onload = function (e) {
28538                 if (e.target.error) {
28539                     Roo.log(e.target.error);
28540                     return;
28541                 }
28542                 
28543                 var buffer = e.target.result,
28544                     dataView = new DataView(buffer),
28545                     offset = 2,
28546                     maxOffset = dataView.byteLength - 4,
28547                     markerBytes,
28548                     markerLength;
28549                 
28550                 if (dataView.getUint16(0) === 0xffd8) {
28551                     while (offset < maxOffset) {
28552                         markerBytes = dataView.getUint16(offset);
28553                         
28554                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28555                             markerLength = dataView.getUint16(offset + 2) + 2;
28556                             if (offset + markerLength > dataView.byteLength) {
28557                                 Roo.log('Invalid meta data: Invalid segment size.');
28558                                 break;
28559                             }
28560                             
28561                             if(markerBytes == 0xffe1){
28562                                 _this.parseExifData(
28563                                     dataView,
28564                                     offset,
28565                                     markerLength
28566                                 );
28567                             }
28568                             
28569                             offset += markerLength;
28570                             
28571                             continue;
28572                         }
28573                         
28574                         break;
28575                     }
28576                     
28577                 }
28578                 
28579                 var url = _this.urlAPI.createObjectURL(_this.file);
28580                 
28581                 _this.loadCanvas(url);
28582                 
28583                 return;
28584             }
28585             
28586             reader.readAsArrayBuffer(this.file);
28587             
28588         }
28589         
28590     },
28591     
28592     parseExifData : function(dataView, offset, length)
28593     {
28594         var tiffOffset = offset + 10,
28595             littleEndian,
28596             dirOffset;
28597     
28598         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28599             // No Exif data, might be XMP data instead
28600             return;
28601         }
28602         
28603         // Check for the ASCII code for "Exif" (0x45786966):
28604         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28605             // No Exif data, might be XMP data instead
28606             return;
28607         }
28608         if (tiffOffset + 8 > dataView.byteLength) {
28609             Roo.log('Invalid Exif data: Invalid segment size.');
28610             return;
28611         }
28612         // Check for the two null bytes:
28613         if (dataView.getUint16(offset + 8) !== 0x0000) {
28614             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28615             return;
28616         }
28617         // Check the byte alignment:
28618         switch (dataView.getUint16(tiffOffset)) {
28619         case 0x4949:
28620             littleEndian = true;
28621             break;
28622         case 0x4D4D:
28623             littleEndian = false;
28624             break;
28625         default:
28626             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28627             return;
28628         }
28629         // Check for the TIFF tag marker (0x002A):
28630         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28631             Roo.log('Invalid Exif data: Missing TIFF marker.');
28632             return;
28633         }
28634         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28635         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28636         
28637         this.parseExifTags(
28638             dataView,
28639             tiffOffset,
28640             tiffOffset + dirOffset,
28641             littleEndian
28642         );
28643     },
28644     
28645     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28646     {
28647         var tagsNumber,
28648             dirEndOffset,
28649             i;
28650         if (dirOffset + 6 > dataView.byteLength) {
28651             Roo.log('Invalid Exif data: Invalid directory offset.');
28652             return;
28653         }
28654         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28655         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28656         if (dirEndOffset + 4 > dataView.byteLength) {
28657             Roo.log('Invalid Exif data: Invalid directory size.');
28658             return;
28659         }
28660         for (i = 0; i < tagsNumber; i += 1) {
28661             this.parseExifTag(
28662                 dataView,
28663                 tiffOffset,
28664                 dirOffset + 2 + 12 * i, // tag offset
28665                 littleEndian
28666             );
28667         }
28668         // Return the offset to the next directory:
28669         return dataView.getUint32(dirEndOffset, littleEndian);
28670     },
28671     
28672     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28673     {
28674         var tag = dataView.getUint16(offset, littleEndian);
28675         
28676         this.exif[tag] = this.getExifValue(
28677             dataView,
28678             tiffOffset,
28679             offset,
28680             dataView.getUint16(offset + 2, littleEndian), // tag type
28681             dataView.getUint32(offset + 4, littleEndian), // tag length
28682             littleEndian
28683         );
28684     },
28685     
28686     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28687     {
28688         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28689             tagSize,
28690             dataOffset,
28691             values,
28692             i,
28693             str,
28694             c;
28695     
28696         if (!tagType) {
28697             Roo.log('Invalid Exif data: Invalid tag type.');
28698             return;
28699         }
28700         
28701         tagSize = tagType.size * length;
28702         // Determine if the value is contained in the dataOffset bytes,
28703         // or if the value at the dataOffset is a pointer to the actual data:
28704         dataOffset = tagSize > 4 ?
28705                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28706         if (dataOffset + tagSize > dataView.byteLength) {
28707             Roo.log('Invalid Exif data: Invalid data offset.');
28708             return;
28709         }
28710         if (length === 1) {
28711             return tagType.getValue(dataView, dataOffset, littleEndian);
28712         }
28713         values = [];
28714         for (i = 0; i < length; i += 1) {
28715             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28716         }
28717         
28718         if (tagType.ascii) {
28719             str = '';
28720             // Concatenate the chars:
28721             for (i = 0; i < values.length; i += 1) {
28722                 c = values[i];
28723                 // Ignore the terminating NULL byte(s):
28724                 if (c === '\u0000') {
28725                     break;
28726                 }
28727                 str += c;
28728             }
28729             return str;
28730         }
28731         return values;
28732     }
28733     
28734 });
28735
28736 Roo.apply(Roo.bootstrap.UploadCropbox, {
28737     tags : {
28738         'Orientation': 0x0112
28739     },
28740     
28741     Orientation: {
28742             1: 0, //'top-left',
28743 //            2: 'top-right',
28744             3: 180, //'bottom-right',
28745 //            4: 'bottom-left',
28746 //            5: 'left-top',
28747             6: 90, //'right-top',
28748 //            7: 'right-bottom',
28749             8: 270 //'left-bottom'
28750     },
28751     
28752     exifTagTypes : {
28753         // byte, 8-bit unsigned int:
28754         1: {
28755             getValue: function (dataView, dataOffset) {
28756                 return dataView.getUint8(dataOffset);
28757             },
28758             size: 1
28759         },
28760         // ascii, 8-bit byte:
28761         2: {
28762             getValue: function (dataView, dataOffset) {
28763                 return String.fromCharCode(dataView.getUint8(dataOffset));
28764             },
28765             size: 1,
28766             ascii: true
28767         },
28768         // short, 16 bit int:
28769         3: {
28770             getValue: function (dataView, dataOffset, littleEndian) {
28771                 return dataView.getUint16(dataOffset, littleEndian);
28772             },
28773             size: 2
28774         },
28775         // long, 32 bit int:
28776         4: {
28777             getValue: function (dataView, dataOffset, littleEndian) {
28778                 return dataView.getUint32(dataOffset, littleEndian);
28779             },
28780             size: 4
28781         },
28782         // rational = two long values, first is numerator, second is denominator:
28783         5: {
28784             getValue: function (dataView, dataOffset, littleEndian) {
28785                 return dataView.getUint32(dataOffset, littleEndian) /
28786                     dataView.getUint32(dataOffset + 4, littleEndian);
28787             },
28788             size: 8
28789         },
28790         // slong, 32 bit signed int:
28791         9: {
28792             getValue: function (dataView, dataOffset, littleEndian) {
28793                 return dataView.getInt32(dataOffset, littleEndian);
28794             },
28795             size: 4
28796         },
28797         // srational, two slongs, first is numerator, second is denominator:
28798         10: {
28799             getValue: function (dataView, dataOffset, littleEndian) {
28800                 return dataView.getInt32(dataOffset, littleEndian) /
28801                     dataView.getInt32(dataOffset + 4, littleEndian);
28802             },
28803             size: 8
28804         }
28805     },
28806     
28807     footer : {
28808         STANDARD : [
28809             {
28810                 tag : 'div',
28811                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28812                 action : 'rotate-left',
28813                 cn : [
28814                     {
28815                         tag : 'button',
28816                         cls : 'btn btn-default',
28817                         html : '<i class="fa fa-undo"></i>'
28818                     }
28819                 ]
28820             },
28821             {
28822                 tag : 'div',
28823                 cls : 'btn-group roo-upload-cropbox-picture',
28824                 action : 'picture',
28825                 cn : [
28826                     {
28827                         tag : 'button',
28828                         cls : 'btn btn-default',
28829                         html : '<i class="fa fa-picture-o"></i>'
28830                     }
28831                 ]
28832             },
28833             {
28834                 tag : 'div',
28835                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28836                 action : 'rotate-right',
28837                 cn : [
28838                     {
28839                         tag : 'button',
28840                         cls : 'btn btn-default',
28841                         html : '<i class="fa fa-repeat"></i>'
28842                     }
28843                 ]
28844             }
28845         ],
28846         DOCUMENT : [
28847             {
28848                 tag : 'div',
28849                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28850                 action : 'rotate-left',
28851                 cn : [
28852                     {
28853                         tag : 'button',
28854                         cls : 'btn btn-default',
28855                         html : '<i class="fa fa-undo"></i>'
28856                     }
28857                 ]
28858             },
28859             {
28860                 tag : 'div',
28861                 cls : 'btn-group roo-upload-cropbox-download',
28862                 action : 'download',
28863                 cn : [
28864                     {
28865                         tag : 'button',
28866                         cls : 'btn btn-default',
28867                         html : '<i class="fa fa-download"></i>'
28868                     }
28869                 ]
28870             },
28871             {
28872                 tag : 'div',
28873                 cls : 'btn-group roo-upload-cropbox-crop',
28874                 action : 'crop',
28875                 cn : [
28876                     {
28877                         tag : 'button',
28878                         cls : 'btn btn-default',
28879                         html : '<i class="fa fa-crop"></i>'
28880                     }
28881                 ]
28882             },
28883             {
28884                 tag : 'div',
28885                 cls : 'btn-group roo-upload-cropbox-trash',
28886                 action : 'trash',
28887                 cn : [
28888                     {
28889                         tag : 'button',
28890                         cls : 'btn btn-default',
28891                         html : '<i class="fa fa-trash"></i>'
28892                     }
28893                 ]
28894             },
28895             {
28896                 tag : 'div',
28897                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28898                 action : 'rotate-right',
28899                 cn : [
28900                     {
28901                         tag : 'button',
28902                         cls : 'btn btn-default',
28903                         html : '<i class="fa fa-repeat"></i>'
28904                     }
28905                 ]
28906             }
28907         ],
28908         ROTATOR : [
28909             {
28910                 tag : 'div',
28911                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28912                 action : 'rotate-left',
28913                 cn : [
28914                     {
28915                         tag : 'button',
28916                         cls : 'btn btn-default',
28917                         html : '<i class="fa fa-undo"></i>'
28918                     }
28919                 ]
28920             },
28921             {
28922                 tag : 'div',
28923                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28924                 action : 'rotate-right',
28925                 cn : [
28926                     {
28927                         tag : 'button',
28928                         cls : 'btn btn-default',
28929                         html : '<i class="fa fa-repeat"></i>'
28930                     }
28931                 ]
28932             }
28933         ]
28934     }
28935 });
28936
28937 /*
28938 * Licence: LGPL
28939 */
28940
28941 /**
28942  * @class Roo.bootstrap.DocumentManager
28943  * @extends Roo.bootstrap.Component
28944  * Bootstrap DocumentManager class
28945  * @cfg {String} paramName default 'imageUpload'
28946  * @cfg {String} toolTipName default 'filename'
28947  * @cfg {String} method default POST
28948  * @cfg {String} url action url
28949  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28950  * @cfg {Boolean} multiple multiple upload default true
28951  * @cfg {Number} thumbSize default 300
28952  * @cfg {String} fieldLabel
28953  * @cfg {Number} labelWidth default 4
28954  * @cfg {String} labelAlign (left|top) default left
28955  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28956 * @cfg {Number} labellg set the width of label (1-12)
28957  * @cfg {Number} labelmd set the width of label (1-12)
28958  * @cfg {Number} labelsm set the width of label (1-12)
28959  * @cfg {Number} labelxs set the width of label (1-12)
28960  * 
28961  * @constructor
28962  * Create a new DocumentManager
28963  * @param {Object} config The config object
28964  */
28965
28966 Roo.bootstrap.DocumentManager = function(config){
28967     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28968     
28969     this.files = [];
28970     this.delegates = [];
28971     
28972     this.addEvents({
28973         /**
28974          * @event initial
28975          * Fire when initial the DocumentManager
28976          * @param {Roo.bootstrap.DocumentManager} this
28977          */
28978         "initial" : true,
28979         /**
28980          * @event inspect
28981          * inspect selected file
28982          * @param {Roo.bootstrap.DocumentManager} this
28983          * @param {File} file
28984          */
28985         "inspect" : true,
28986         /**
28987          * @event exception
28988          * Fire when xhr load exception
28989          * @param {Roo.bootstrap.DocumentManager} this
28990          * @param {XMLHttpRequest} xhr
28991          */
28992         "exception" : true,
28993         /**
28994          * @event afterupload
28995          * Fire when xhr load exception
28996          * @param {Roo.bootstrap.DocumentManager} this
28997          * @param {XMLHttpRequest} xhr
28998          */
28999         "afterupload" : true,
29000         /**
29001          * @event prepare
29002          * prepare the form data
29003          * @param {Roo.bootstrap.DocumentManager} this
29004          * @param {Object} formData
29005          */
29006         "prepare" : true,
29007         /**
29008          * @event remove
29009          * Fire when remove the file
29010          * @param {Roo.bootstrap.DocumentManager} this
29011          * @param {Object} file
29012          */
29013         "remove" : true,
29014         /**
29015          * @event refresh
29016          * Fire after refresh the file
29017          * @param {Roo.bootstrap.DocumentManager} this
29018          */
29019         "refresh" : true,
29020         /**
29021          * @event click
29022          * Fire after click the image
29023          * @param {Roo.bootstrap.DocumentManager} this
29024          * @param {Object} file
29025          */
29026         "click" : true,
29027         /**
29028          * @event edit
29029          * Fire when upload a image and editable set to true
29030          * @param {Roo.bootstrap.DocumentManager} this
29031          * @param {Object} file
29032          */
29033         "edit" : true,
29034         /**
29035          * @event beforeselectfile
29036          * Fire before select file
29037          * @param {Roo.bootstrap.DocumentManager} this
29038          */
29039         "beforeselectfile" : true,
29040         /**
29041          * @event process
29042          * Fire before process file
29043          * @param {Roo.bootstrap.DocumentManager} this
29044          * @param {Object} file
29045          */
29046         "process" : true,
29047         /**
29048          * @event previewrendered
29049          * Fire when preview rendered
29050          * @param {Roo.bootstrap.DocumentManager} this
29051          * @param {Object} file
29052          */
29053         "previewrendered" : true,
29054         /**
29055          */
29056         "previewResize" : true
29057         
29058     });
29059 };
29060
29061 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29062     
29063     boxes : 0,
29064     inputName : '',
29065     thumbSize : 300,
29066     multiple : true,
29067     files : false,
29068     method : 'POST',
29069     url : '',
29070     paramName : 'imageUpload',
29071     toolTipName : 'filename',
29072     fieldLabel : '',
29073     labelWidth : 4,
29074     labelAlign : 'left',
29075     editable : true,
29076     delegates : false,
29077     xhr : false, 
29078     
29079     labellg : 0,
29080     labelmd : 0,
29081     labelsm : 0,
29082     labelxs : 0,
29083     
29084     getAutoCreate : function()
29085     {   
29086         var managerWidget = {
29087             tag : 'div',
29088             cls : 'roo-document-manager',
29089             cn : [
29090                 {
29091                     tag : 'input',
29092                     cls : 'roo-document-manager-selector',
29093                     type : 'file'
29094                 },
29095                 {
29096                     tag : 'div',
29097                     cls : 'roo-document-manager-uploader',
29098                     cn : [
29099                         {
29100                             tag : 'div',
29101                             cls : 'roo-document-manager-upload-btn',
29102                             html : '<i class="fa fa-plus"></i>'
29103                         }
29104                     ]
29105                     
29106                 }
29107             ]
29108         };
29109         
29110         var content = [
29111             {
29112                 tag : 'div',
29113                 cls : 'column col-md-12',
29114                 cn : managerWidget
29115             }
29116         ];
29117         
29118         if(this.fieldLabel.length){
29119             
29120             content = [
29121                 {
29122                     tag : 'div',
29123                     cls : 'column col-md-12',
29124                     html : this.fieldLabel
29125                 },
29126                 {
29127                     tag : 'div',
29128                     cls : 'column col-md-12',
29129                     cn : managerWidget
29130                 }
29131             ];
29132
29133             if(this.labelAlign == 'left'){
29134                 content = [
29135                     {
29136                         tag : 'div',
29137                         cls : 'column',
29138                         html : this.fieldLabel
29139                     },
29140                     {
29141                         tag : 'div',
29142                         cls : 'column',
29143                         cn : managerWidget
29144                     }
29145                 ];
29146                 
29147                 if(this.labelWidth > 12){
29148                     content[0].style = "width: " + this.labelWidth + 'px';
29149                 }
29150
29151                 if(this.labelWidth < 13 && this.labelmd == 0){
29152                     this.labelmd = this.labelWidth;
29153                 }
29154
29155                 if(this.labellg > 0){
29156                     content[0].cls += ' col-lg-' + this.labellg;
29157                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29158                 }
29159
29160                 if(this.labelmd > 0){
29161                     content[0].cls += ' col-md-' + this.labelmd;
29162                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29163                 }
29164
29165                 if(this.labelsm > 0){
29166                     content[0].cls += ' col-sm-' + this.labelsm;
29167                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29168                 }
29169
29170                 if(this.labelxs > 0){
29171                     content[0].cls += ' col-xs-' + this.labelxs;
29172                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29173                 }
29174                 
29175             }
29176         }
29177         
29178         var cfg = {
29179             tag : 'div',
29180             cls : 'row clearfix',
29181             cn : content
29182         };
29183         
29184         return cfg;
29185         
29186     },
29187     
29188     initEvents : function()
29189     {
29190         this.managerEl = this.el.select('.roo-document-manager', true).first();
29191         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29192         
29193         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29194         this.selectorEl.hide();
29195         
29196         if(this.multiple){
29197             this.selectorEl.attr('multiple', 'multiple');
29198         }
29199         
29200         this.selectorEl.on('change', this.onFileSelected, this);
29201         
29202         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29203         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29204         
29205         this.uploader.on('click', this.onUploaderClick, this);
29206         
29207         this.renderProgressDialog();
29208         
29209         var _this = this;
29210         
29211         window.addEventListener("resize", function() { _this.refresh(); } );
29212         
29213         this.fireEvent('initial', this);
29214     },
29215     
29216     renderProgressDialog : function()
29217     {
29218         var _this = this;
29219         
29220         this.progressDialog = new Roo.bootstrap.Modal({
29221             cls : 'roo-document-manager-progress-dialog',
29222             allow_close : false,
29223             animate : false,
29224             title : '',
29225             buttons : [
29226                 {
29227                     name  :'cancel',
29228                     weight : 'danger',
29229                     html : 'Cancel'
29230                 }
29231             ], 
29232             listeners : { 
29233                 btnclick : function() {
29234                     _this.uploadCancel();
29235                     this.hide();
29236                 }
29237             }
29238         });
29239          
29240         this.progressDialog.render(Roo.get(document.body));
29241          
29242         this.progress = new Roo.bootstrap.Progress({
29243             cls : 'roo-document-manager-progress',
29244             active : true,
29245             striped : true
29246         });
29247         
29248         this.progress.render(this.progressDialog.getChildContainer());
29249         
29250         this.progressBar = new Roo.bootstrap.ProgressBar({
29251             cls : 'roo-document-manager-progress-bar',
29252             aria_valuenow : 0,
29253             aria_valuemin : 0,
29254             aria_valuemax : 12,
29255             panel : 'success'
29256         });
29257         
29258         this.progressBar.render(this.progress.getChildContainer());
29259     },
29260     
29261     onUploaderClick : function(e)
29262     {
29263         e.preventDefault();
29264      
29265         if(this.fireEvent('beforeselectfile', this) != false){
29266             this.selectorEl.dom.click();
29267         }
29268         
29269     },
29270     
29271     onFileSelected : function(e)
29272     {
29273         e.preventDefault();
29274         
29275         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29276             return;
29277         }
29278         
29279         Roo.each(this.selectorEl.dom.files, function(file){
29280             if(this.fireEvent('inspect', this, file) != false){
29281                 this.files.push(file);
29282             }
29283         }, this);
29284         
29285         this.queue();
29286         
29287     },
29288     
29289     queue : function()
29290     {
29291         this.selectorEl.dom.value = '';
29292         
29293         if(!this.files || !this.files.length){
29294             return;
29295         }
29296         
29297         if(this.boxes > 0 && this.files.length > this.boxes){
29298             this.files = this.files.slice(0, this.boxes);
29299         }
29300         
29301         this.uploader.show();
29302         
29303         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29304             this.uploader.hide();
29305         }
29306         
29307         var _this = this;
29308         
29309         var files = [];
29310         
29311         var docs = [];
29312         
29313         Roo.each(this.files, function(file){
29314             
29315             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29316                 var f = this.renderPreview(file);
29317                 files.push(f);
29318                 return;
29319             }
29320             
29321             if(file.type.indexOf('image') != -1){
29322                 this.delegates.push(
29323                     (function(){
29324                         _this.process(file);
29325                     }).createDelegate(this)
29326                 );
29327         
29328                 return;
29329             }
29330             
29331             docs.push(
29332                 (function(){
29333                     _this.process(file);
29334                 }).createDelegate(this)
29335             );
29336             
29337         }, this);
29338         
29339         this.files = files;
29340         
29341         this.delegates = this.delegates.concat(docs);
29342         
29343         if(!this.delegates.length){
29344             this.refresh();
29345             return;
29346         }
29347         
29348         this.progressBar.aria_valuemax = this.delegates.length;
29349         
29350         this.arrange();
29351         
29352         return;
29353     },
29354     
29355     arrange : function()
29356     {
29357         if(!this.delegates.length){
29358             this.progressDialog.hide();
29359             this.refresh();
29360             return;
29361         }
29362         
29363         var delegate = this.delegates.shift();
29364         
29365         this.progressDialog.show();
29366         
29367         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29368         
29369         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29370         
29371         delegate();
29372     },
29373     
29374     refresh : function()
29375     {
29376         this.uploader.show();
29377         
29378         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29379             this.uploader.hide();
29380         }
29381         
29382         Roo.isTouch ? this.closable(false) : this.closable(true);
29383         
29384         this.fireEvent('refresh', this);
29385     },
29386     
29387     onRemove : function(e, el, o)
29388     {
29389         e.preventDefault();
29390         
29391         this.fireEvent('remove', this, o);
29392         
29393     },
29394     
29395     remove : function(o)
29396     {
29397         var files = [];
29398         
29399         Roo.each(this.files, function(file){
29400             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29401                 files.push(file);
29402                 return;
29403             }
29404
29405             o.target.remove();
29406
29407         }, this);
29408         
29409         this.files = files;
29410         
29411         this.refresh();
29412     },
29413     
29414     clear : function()
29415     {
29416         Roo.each(this.files, function(file){
29417             if(!file.target){
29418                 return;
29419             }
29420             
29421             file.target.remove();
29422
29423         }, this);
29424         
29425         this.files = [];
29426         
29427         this.refresh();
29428     },
29429     
29430     onClick : function(e, el, o)
29431     {
29432         e.preventDefault();
29433         
29434         this.fireEvent('click', this, o);
29435         
29436     },
29437     
29438     closable : function(closable)
29439     {
29440         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29441             
29442             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29443             
29444             if(closable){
29445                 el.show();
29446                 return;
29447             }
29448             
29449             el.hide();
29450             
29451         }, this);
29452     },
29453     
29454     xhrOnLoad : function(xhr)
29455     {
29456         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29457             el.remove();
29458         }, this);
29459         
29460         if (xhr.readyState !== 4) {
29461             this.arrange();
29462             this.fireEvent('exception', this, xhr);
29463             return;
29464         }
29465
29466         var response = Roo.decode(xhr.responseText);
29467         
29468         if(!response.success){
29469             this.arrange();
29470             this.fireEvent('exception', this, xhr);
29471             return;
29472         }
29473         
29474         var file = this.renderPreview(response.data);
29475         
29476         this.files.push(file);
29477         
29478         this.arrange();
29479         
29480         this.fireEvent('afterupload', this, xhr);
29481         
29482     },
29483     
29484     xhrOnError : function(xhr)
29485     {
29486         Roo.log('xhr on error');
29487         
29488         var response = Roo.decode(xhr.responseText);
29489           
29490         Roo.log(response);
29491         
29492         this.arrange();
29493     },
29494     
29495     process : function(file)
29496     {
29497         if(this.fireEvent('process', this, file) !== false){
29498             if(this.editable && file.type.indexOf('image') != -1){
29499                 this.fireEvent('edit', this, file);
29500                 return;
29501             }
29502
29503             this.uploadStart(file, false);
29504
29505             return;
29506         }
29507         
29508     },
29509     
29510     uploadStart : function(file, crop)
29511     {
29512         this.xhr = new XMLHttpRequest();
29513         
29514         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29515             this.arrange();
29516             return;
29517         }
29518         
29519         file.xhr = this.xhr;
29520             
29521         this.managerEl.createChild({
29522             tag : 'div',
29523             cls : 'roo-document-manager-loading',
29524             cn : [
29525                 {
29526                     tag : 'div',
29527                     tooltip : file.name,
29528                     cls : 'roo-document-manager-thumb',
29529                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29530                 }
29531             ]
29532
29533         });
29534
29535         this.xhr.open(this.method, this.url, true);
29536         
29537         var headers = {
29538             "Accept": "application/json",
29539             "Cache-Control": "no-cache",
29540             "X-Requested-With": "XMLHttpRequest"
29541         };
29542         
29543         for (var headerName in headers) {
29544             var headerValue = headers[headerName];
29545             if (headerValue) {
29546                 this.xhr.setRequestHeader(headerName, headerValue);
29547             }
29548         }
29549         
29550         var _this = this;
29551         
29552         this.xhr.onload = function()
29553         {
29554             _this.xhrOnLoad(_this.xhr);
29555         }
29556         
29557         this.xhr.onerror = function()
29558         {
29559             _this.xhrOnError(_this.xhr);
29560         }
29561         
29562         var formData = new FormData();
29563
29564         formData.append('returnHTML', 'NO');
29565         
29566         if(crop){
29567             formData.append('crop', crop);
29568         }
29569         
29570         formData.append(this.paramName, file, file.name);
29571         
29572         var options = {
29573             file : file, 
29574             manually : false
29575         };
29576         
29577         if(this.fireEvent('prepare', this, formData, options) != false){
29578             
29579             if(options.manually){
29580                 return;
29581             }
29582             
29583             this.xhr.send(formData);
29584             return;
29585         };
29586         
29587         this.uploadCancel();
29588     },
29589     
29590     uploadCancel : function()
29591     {
29592         if (this.xhr) {
29593             this.xhr.abort();
29594         }
29595         
29596         this.delegates = [];
29597         
29598         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29599             el.remove();
29600         }, this);
29601         
29602         this.arrange();
29603     },
29604     
29605     renderPreview : function(file)
29606     {
29607         if(typeof(file.target) != 'undefined' && file.target){
29608             return file;
29609         }
29610         
29611         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29612         
29613         var previewEl = this.managerEl.createChild({
29614             tag : 'div',
29615             cls : 'roo-document-manager-preview',
29616             cn : [
29617                 {
29618                     tag : 'div',
29619                     tooltip : file[this.toolTipName],
29620                     cls : 'roo-document-manager-thumb',
29621                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29622                 },
29623                 {
29624                     tag : 'button',
29625                     cls : 'close',
29626                     html : '<i class="fa fa-times-circle"></i>'
29627                 }
29628             ]
29629         });
29630
29631         var close = previewEl.select('button.close', true).first();
29632
29633         close.on('click', this.onRemove, this, file);
29634
29635         file.target = previewEl;
29636
29637         var image = previewEl.select('img', true).first();
29638         
29639         var _this = this;
29640         
29641         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29642         
29643         image.on('click', this.onClick, this, file);
29644         
29645         this.fireEvent('previewrendered', this, file);
29646         
29647         return file;
29648         
29649     },
29650     
29651     onPreviewLoad : function(file, image)
29652     {
29653         if(typeof(file.target) == 'undefined' || !file.target){
29654             return;
29655         }
29656         
29657         var width = image.dom.naturalWidth || image.dom.width;
29658         var height = image.dom.naturalHeight || image.dom.height;
29659         
29660         if(!this.previewResize) {
29661             return;
29662         }
29663         
29664         if(width > height){
29665             file.target.addClass('wide');
29666             return;
29667         }
29668         
29669         file.target.addClass('tall');
29670         return;
29671         
29672     },
29673     
29674     uploadFromSource : function(file, crop)
29675     {
29676         this.xhr = new XMLHttpRequest();
29677         
29678         this.managerEl.createChild({
29679             tag : 'div',
29680             cls : 'roo-document-manager-loading',
29681             cn : [
29682                 {
29683                     tag : 'div',
29684                     tooltip : file.name,
29685                     cls : 'roo-document-manager-thumb',
29686                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29687                 }
29688             ]
29689
29690         });
29691
29692         this.xhr.open(this.method, this.url, true);
29693         
29694         var headers = {
29695             "Accept": "application/json",
29696             "Cache-Control": "no-cache",
29697             "X-Requested-With": "XMLHttpRequest"
29698         };
29699         
29700         for (var headerName in headers) {
29701             var headerValue = headers[headerName];
29702             if (headerValue) {
29703                 this.xhr.setRequestHeader(headerName, headerValue);
29704             }
29705         }
29706         
29707         var _this = this;
29708         
29709         this.xhr.onload = function()
29710         {
29711             _this.xhrOnLoad(_this.xhr);
29712         }
29713         
29714         this.xhr.onerror = function()
29715         {
29716             _this.xhrOnError(_this.xhr);
29717         }
29718         
29719         var formData = new FormData();
29720
29721         formData.append('returnHTML', 'NO');
29722         
29723         formData.append('crop', crop);
29724         
29725         if(typeof(file.filename) != 'undefined'){
29726             formData.append('filename', file.filename);
29727         }
29728         
29729         if(typeof(file.mimetype) != 'undefined'){
29730             formData.append('mimetype', file.mimetype);
29731         }
29732         
29733         Roo.log(formData);
29734         
29735         if(this.fireEvent('prepare', this, formData) != false){
29736             this.xhr.send(formData);
29737         };
29738     }
29739 });
29740
29741 /*
29742 * Licence: LGPL
29743 */
29744
29745 /**
29746  * @class Roo.bootstrap.DocumentViewer
29747  * @extends Roo.bootstrap.Component
29748  * Bootstrap DocumentViewer class
29749  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29750  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29751  * 
29752  * @constructor
29753  * Create a new DocumentViewer
29754  * @param {Object} config The config object
29755  */
29756
29757 Roo.bootstrap.DocumentViewer = function(config){
29758     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29759     
29760     this.addEvents({
29761         /**
29762          * @event initial
29763          * Fire after initEvent
29764          * @param {Roo.bootstrap.DocumentViewer} this
29765          */
29766         "initial" : true,
29767         /**
29768          * @event click
29769          * Fire after click
29770          * @param {Roo.bootstrap.DocumentViewer} this
29771          */
29772         "click" : true,
29773         /**
29774          * @event download
29775          * Fire after download button
29776          * @param {Roo.bootstrap.DocumentViewer} this
29777          */
29778         "download" : true,
29779         /**
29780          * @event trash
29781          * Fire after trash button
29782          * @param {Roo.bootstrap.DocumentViewer} this
29783          */
29784         "trash" : true
29785         
29786     });
29787 };
29788
29789 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29790     
29791     showDownload : true,
29792     
29793     showTrash : true,
29794     
29795     getAutoCreate : function()
29796     {
29797         var cfg = {
29798             tag : 'div',
29799             cls : 'roo-document-viewer',
29800             cn : [
29801                 {
29802                     tag : 'div',
29803                     cls : 'roo-document-viewer-body',
29804                     cn : [
29805                         {
29806                             tag : 'div',
29807                             cls : 'roo-document-viewer-thumb',
29808                             cn : [
29809                                 {
29810                                     tag : 'img',
29811                                     cls : 'roo-document-viewer-image'
29812                                 }
29813                             ]
29814                         }
29815                     ]
29816                 },
29817                 {
29818                     tag : 'div',
29819                     cls : 'roo-document-viewer-footer',
29820                     cn : {
29821                         tag : 'div',
29822                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29823                         cn : [
29824                             {
29825                                 tag : 'div',
29826                                 cls : 'btn-group roo-document-viewer-download',
29827                                 cn : [
29828                                     {
29829                                         tag : 'button',
29830                                         cls : 'btn btn-default',
29831                                         html : '<i class="fa fa-download"></i>'
29832                                     }
29833                                 ]
29834                             },
29835                             {
29836                                 tag : 'div',
29837                                 cls : 'btn-group roo-document-viewer-trash',
29838                                 cn : [
29839                                     {
29840                                         tag : 'button',
29841                                         cls : 'btn btn-default',
29842                                         html : '<i class="fa fa-trash"></i>'
29843                                     }
29844                                 ]
29845                             }
29846                         ]
29847                     }
29848                 }
29849             ]
29850         };
29851         
29852         return cfg;
29853     },
29854     
29855     initEvents : function()
29856     {
29857         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29858         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29859         
29860         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29861         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29862         
29863         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29864         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29865         
29866         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29867         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29868         
29869         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29870         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29871         
29872         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29873         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29874         
29875         this.bodyEl.on('click', this.onClick, this);
29876         this.downloadBtn.on('click', this.onDownload, this);
29877         this.trashBtn.on('click', this.onTrash, this);
29878         
29879         this.downloadBtn.hide();
29880         this.trashBtn.hide();
29881         
29882         if(this.showDownload){
29883             this.downloadBtn.show();
29884         }
29885         
29886         if(this.showTrash){
29887             this.trashBtn.show();
29888         }
29889         
29890         if(!this.showDownload && !this.showTrash) {
29891             this.footerEl.hide();
29892         }
29893         
29894     },
29895     
29896     initial : function()
29897     {
29898         this.fireEvent('initial', this);
29899         
29900     },
29901     
29902     onClick : function(e)
29903     {
29904         e.preventDefault();
29905         
29906         this.fireEvent('click', this);
29907     },
29908     
29909     onDownload : function(e)
29910     {
29911         e.preventDefault();
29912         
29913         this.fireEvent('download', this);
29914     },
29915     
29916     onTrash : function(e)
29917     {
29918         e.preventDefault();
29919         
29920         this.fireEvent('trash', this);
29921     }
29922     
29923 });
29924 /*
29925  * - LGPL
29926  *
29927  * nav progress bar
29928  * 
29929  */
29930
29931 /**
29932  * @class Roo.bootstrap.NavProgressBar
29933  * @extends Roo.bootstrap.Component
29934  * Bootstrap NavProgressBar class
29935  * 
29936  * @constructor
29937  * Create a new nav progress bar
29938  * @param {Object} config The config object
29939  */
29940
29941 Roo.bootstrap.NavProgressBar = function(config){
29942     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29943
29944     this.bullets = this.bullets || [];
29945    
29946 //    Roo.bootstrap.NavProgressBar.register(this);
29947      this.addEvents({
29948         /**
29949              * @event changed
29950              * Fires when the active item changes
29951              * @param {Roo.bootstrap.NavProgressBar} this
29952              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29953              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29954          */
29955         'changed': true
29956      });
29957     
29958 };
29959
29960 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29961     
29962     bullets : [],
29963     barItems : [],
29964     
29965     getAutoCreate : function()
29966     {
29967         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29968         
29969         cfg = {
29970             tag : 'div',
29971             cls : 'roo-navigation-bar-group',
29972             cn : [
29973                 {
29974                     tag : 'div',
29975                     cls : 'roo-navigation-top-bar'
29976                 },
29977                 {
29978                     tag : 'div',
29979                     cls : 'roo-navigation-bullets-bar',
29980                     cn : [
29981                         {
29982                             tag : 'ul',
29983                             cls : 'roo-navigation-bar'
29984                         }
29985                     ]
29986                 },
29987                 
29988                 {
29989                     tag : 'div',
29990                     cls : 'roo-navigation-bottom-bar'
29991                 }
29992             ]
29993             
29994         };
29995         
29996         return cfg;
29997         
29998     },
29999     
30000     initEvents: function() 
30001     {
30002         
30003     },
30004     
30005     onRender : function(ct, position) 
30006     {
30007         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30008         
30009         if(this.bullets.length){
30010             Roo.each(this.bullets, function(b){
30011                this.addItem(b);
30012             }, this);
30013         }
30014         
30015         this.format();
30016         
30017     },
30018     
30019     addItem : function(cfg)
30020     {
30021         var item = new Roo.bootstrap.NavProgressItem(cfg);
30022         
30023         item.parentId = this.id;
30024         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30025         
30026         if(cfg.html){
30027             var top = new Roo.bootstrap.Element({
30028                 tag : 'div',
30029                 cls : 'roo-navigation-bar-text'
30030             });
30031             
30032             var bottom = new Roo.bootstrap.Element({
30033                 tag : 'div',
30034                 cls : 'roo-navigation-bar-text'
30035             });
30036             
30037             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30038             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30039             
30040             var topText = new Roo.bootstrap.Element({
30041                 tag : 'span',
30042                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30043             });
30044             
30045             var bottomText = new Roo.bootstrap.Element({
30046                 tag : 'span',
30047                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30048             });
30049             
30050             topText.onRender(top.el, null);
30051             bottomText.onRender(bottom.el, null);
30052             
30053             item.topEl = top;
30054             item.bottomEl = bottom;
30055         }
30056         
30057         this.barItems.push(item);
30058         
30059         return item;
30060     },
30061     
30062     getActive : function()
30063     {
30064         var active = false;
30065         
30066         Roo.each(this.barItems, function(v){
30067             
30068             if (!v.isActive()) {
30069                 return;
30070             }
30071             
30072             active = v;
30073             return false;
30074             
30075         });
30076         
30077         return active;
30078     },
30079     
30080     setActiveItem : function(item)
30081     {
30082         var prev = false;
30083         
30084         Roo.each(this.barItems, function(v){
30085             if (v.rid == item.rid) {
30086                 return ;
30087             }
30088             
30089             if (v.isActive()) {
30090                 v.setActive(false);
30091                 prev = v;
30092             }
30093         });
30094
30095         item.setActive(true);
30096         
30097         this.fireEvent('changed', this, item, prev);
30098     },
30099     
30100     getBarItem: function(rid)
30101     {
30102         var ret = false;
30103         
30104         Roo.each(this.barItems, function(e) {
30105             if (e.rid != rid) {
30106                 return;
30107             }
30108             
30109             ret =  e;
30110             return false;
30111         });
30112         
30113         return ret;
30114     },
30115     
30116     indexOfItem : function(item)
30117     {
30118         var index = false;
30119         
30120         Roo.each(this.barItems, function(v, i){
30121             
30122             if (v.rid != item.rid) {
30123                 return;
30124             }
30125             
30126             index = i;
30127             return false
30128         });
30129         
30130         return index;
30131     },
30132     
30133     setActiveNext : function()
30134     {
30135         var i = this.indexOfItem(this.getActive());
30136         
30137         if (i > this.barItems.length) {
30138             return;
30139         }
30140         
30141         this.setActiveItem(this.barItems[i+1]);
30142     },
30143     
30144     setActivePrev : function()
30145     {
30146         var i = this.indexOfItem(this.getActive());
30147         
30148         if (i  < 1) {
30149             return;
30150         }
30151         
30152         this.setActiveItem(this.barItems[i-1]);
30153     },
30154     
30155     format : function()
30156     {
30157         if(!this.barItems.length){
30158             return;
30159         }
30160      
30161         var width = 100 / this.barItems.length;
30162         
30163         Roo.each(this.barItems, function(i){
30164             i.el.setStyle('width', width + '%');
30165             i.topEl.el.setStyle('width', width + '%');
30166             i.bottomEl.el.setStyle('width', width + '%');
30167         }, this);
30168         
30169     }
30170     
30171 });
30172 /*
30173  * - LGPL
30174  *
30175  * Nav Progress Item
30176  * 
30177  */
30178
30179 /**
30180  * @class Roo.bootstrap.NavProgressItem
30181  * @extends Roo.bootstrap.Component
30182  * Bootstrap NavProgressItem class
30183  * @cfg {String} rid the reference id
30184  * @cfg {Boolean} active (true|false) Is item active default false
30185  * @cfg {Boolean} disabled (true|false) Is item active default false
30186  * @cfg {String} html
30187  * @cfg {String} position (top|bottom) text position default bottom
30188  * @cfg {String} icon show icon instead of number
30189  * 
30190  * @constructor
30191  * Create a new NavProgressItem
30192  * @param {Object} config The config object
30193  */
30194 Roo.bootstrap.NavProgressItem = function(config){
30195     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30196     this.addEvents({
30197         // raw events
30198         /**
30199          * @event click
30200          * The raw click event for the entire grid.
30201          * @param {Roo.bootstrap.NavProgressItem} this
30202          * @param {Roo.EventObject} e
30203          */
30204         "click" : true
30205     });
30206    
30207 };
30208
30209 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30210     
30211     rid : '',
30212     active : false,
30213     disabled : false,
30214     html : '',
30215     position : 'bottom',
30216     icon : false,
30217     
30218     getAutoCreate : function()
30219     {
30220         var iconCls = 'roo-navigation-bar-item-icon';
30221         
30222         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30223         
30224         var cfg = {
30225             tag: 'li',
30226             cls: 'roo-navigation-bar-item',
30227             cn : [
30228                 {
30229                     tag : 'i',
30230                     cls : iconCls
30231                 }
30232             ]
30233         };
30234         
30235         if(this.active){
30236             cfg.cls += ' active';
30237         }
30238         if(this.disabled){
30239             cfg.cls += ' disabled';
30240         }
30241         
30242         return cfg;
30243     },
30244     
30245     disable : function()
30246     {
30247         this.setDisabled(true);
30248     },
30249     
30250     enable : function()
30251     {
30252         this.setDisabled(false);
30253     },
30254     
30255     initEvents: function() 
30256     {
30257         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30258         
30259         this.iconEl.on('click', this.onClick, this);
30260     },
30261     
30262     onClick : function(e)
30263     {
30264         e.preventDefault();
30265         
30266         if(this.disabled){
30267             return;
30268         }
30269         
30270         if(this.fireEvent('click', this, e) === false){
30271             return;
30272         };
30273         
30274         this.parent().setActiveItem(this);
30275     },
30276     
30277     isActive: function () 
30278     {
30279         return this.active;
30280     },
30281     
30282     setActive : function(state)
30283     {
30284         if(this.active == state){
30285             return;
30286         }
30287         
30288         this.active = state;
30289         
30290         if (state) {
30291             this.el.addClass('active');
30292             return;
30293         }
30294         
30295         this.el.removeClass('active');
30296         
30297         return;
30298     },
30299     
30300     setDisabled : function(state)
30301     {
30302         if(this.disabled == state){
30303             return;
30304         }
30305         
30306         this.disabled = state;
30307         
30308         if (state) {
30309             this.el.addClass('disabled');
30310             return;
30311         }
30312         
30313         this.el.removeClass('disabled');
30314     },
30315     
30316     tooltipEl : function()
30317     {
30318         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30319     }
30320 });
30321  
30322
30323  /*
30324  * - LGPL
30325  *
30326  * FieldLabel
30327  * 
30328  */
30329
30330 /**
30331  * @class Roo.bootstrap.FieldLabel
30332  * @extends Roo.bootstrap.Component
30333  * Bootstrap FieldLabel class
30334  * @cfg {String} html contents of the element
30335  * @cfg {String} tag tag of the element default label
30336  * @cfg {String} cls class of the element
30337  * @cfg {String} target label target 
30338  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30339  * @cfg {String} invalidClass default "text-warning"
30340  * @cfg {String} validClass default "text-success"
30341  * @cfg {String} iconTooltip default "This field is required"
30342  * @cfg {String} indicatorpos (left|right) default left
30343  * 
30344  * @constructor
30345  * Create a new FieldLabel
30346  * @param {Object} config The config object
30347  */
30348
30349 Roo.bootstrap.FieldLabel = function(config){
30350     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30351     
30352     this.addEvents({
30353             /**
30354              * @event invalid
30355              * Fires after the field has been marked as invalid.
30356              * @param {Roo.form.FieldLabel} this
30357              * @param {String} msg The validation message
30358              */
30359             invalid : true,
30360             /**
30361              * @event valid
30362              * Fires after the field has been validated with no errors.
30363              * @param {Roo.form.FieldLabel} this
30364              */
30365             valid : true
30366         });
30367 };
30368
30369 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30370     
30371     tag: 'label',
30372     cls: '',
30373     html: '',
30374     target: '',
30375     allowBlank : true,
30376     invalidClass : 'has-warning',
30377     validClass : 'has-success',
30378     iconTooltip : 'This field is required',
30379     indicatorpos : 'left',
30380     
30381     getAutoCreate : function(){
30382         
30383         var cls = "";
30384         if (!this.allowBlank) {
30385             cls  = "visible";
30386         }
30387         
30388         var cfg = {
30389             tag : this.tag,
30390             cls : 'roo-bootstrap-field-label ' + this.cls,
30391             for : this.target,
30392             cn : [
30393                 {
30394                     tag : 'i',
30395                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30396                     tooltip : this.iconTooltip
30397                 },
30398                 {
30399                     tag : 'span',
30400                     html : this.html
30401                 }
30402             ] 
30403         };
30404         
30405         if(this.indicatorpos == 'right'){
30406             var cfg = {
30407                 tag : this.tag,
30408                 cls : 'roo-bootstrap-field-label ' + this.cls,
30409                 for : this.target,
30410                 cn : [
30411                     {
30412                         tag : 'span',
30413                         html : this.html
30414                     },
30415                     {
30416                         tag : 'i',
30417                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30418                         tooltip : this.iconTooltip
30419                     }
30420                 ] 
30421             };
30422         }
30423         
30424         return cfg;
30425     },
30426     
30427     initEvents: function() 
30428     {
30429         Roo.bootstrap.Element.superclass.initEvents.call(this);
30430         
30431         this.indicator = this.indicatorEl();
30432         
30433         if(this.indicator){
30434             this.indicator.removeClass('visible');
30435             this.indicator.addClass('invisible');
30436         }
30437         
30438         Roo.bootstrap.FieldLabel.register(this);
30439     },
30440     
30441     indicatorEl : function()
30442     {
30443         var indicator = this.el.select('i.roo-required-indicator',true).first();
30444         
30445         if(!indicator){
30446             return false;
30447         }
30448         
30449         return indicator;
30450         
30451     },
30452     
30453     /**
30454      * Mark this field as valid
30455      */
30456     markValid : function()
30457     {
30458         if(this.indicator){
30459             this.indicator.removeClass('visible');
30460             this.indicator.addClass('invisible');
30461         }
30462         
30463         this.el.removeClass(this.invalidClass);
30464         
30465         this.el.addClass(this.validClass);
30466         
30467         this.fireEvent('valid', this);
30468     },
30469     
30470     /**
30471      * Mark this field as invalid
30472      * @param {String} msg The validation message
30473      */
30474     markInvalid : function(msg)
30475     {
30476         if(this.indicator){
30477             this.indicator.removeClass('invisible');
30478             this.indicator.addClass('visible');
30479         }
30480         
30481         this.el.removeClass(this.validClass);
30482         
30483         this.el.addClass(this.invalidClass);
30484         
30485         this.fireEvent('invalid', this, msg);
30486     }
30487     
30488    
30489 });
30490
30491 Roo.apply(Roo.bootstrap.FieldLabel, {
30492     
30493     groups: {},
30494     
30495      /**
30496     * register a FieldLabel Group
30497     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30498     */
30499     register : function(label)
30500     {
30501         if(this.groups.hasOwnProperty(label.target)){
30502             return;
30503         }
30504      
30505         this.groups[label.target] = label;
30506         
30507     },
30508     /**
30509     * fetch a FieldLabel Group based on the target
30510     * @param {string} target
30511     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30512     */
30513     get: function(target) {
30514         if (typeof(this.groups[target]) == 'undefined') {
30515             return false;
30516         }
30517         
30518         return this.groups[target] ;
30519     }
30520 });
30521
30522  
30523
30524  /*
30525  * - LGPL
30526  *
30527  * page DateSplitField.
30528  * 
30529  */
30530
30531
30532 /**
30533  * @class Roo.bootstrap.DateSplitField
30534  * @extends Roo.bootstrap.Component
30535  * Bootstrap DateSplitField class
30536  * @cfg {string} fieldLabel - the label associated
30537  * @cfg {Number} labelWidth set the width of label (0-12)
30538  * @cfg {String} labelAlign (top|left)
30539  * @cfg {Boolean} dayAllowBlank (true|false) default false
30540  * @cfg {Boolean} monthAllowBlank (true|false) default false
30541  * @cfg {Boolean} yearAllowBlank (true|false) default false
30542  * @cfg {string} dayPlaceholder 
30543  * @cfg {string} monthPlaceholder
30544  * @cfg {string} yearPlaceholder
30545  * @cfg {string} dayFormat default 'd'
30546  * @cfg {string} monthFormat default 'm'
30547  * @cfg {string} yearFormat default 'Y'
30548  * @cfg {Number} labellg set the width of label (1-12)
30549  * @cfg {Number} labelmd set the width of label (1-12)
30550  * @cfg {Number} labelsm set the width of label (1-12)
30551  * @cfg {Number} labelxs set the width of label (1-12)
30552
30553  *     
30554  * @constructor
30555  * Create a new DateSplitField
30556  * @param {Object} config The config object
30557  */
30558
30559 Roo.bootstrap.DateSplitField = function(config){
30560     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30561     
30562     this.addEvents({
30563         // raw events
30564          /**
30565          * @event years
30566          * getting the data of years
30567          * @param {Roo.bootstrap.DateSplitField} this
30568          * @param {Object} years
30569          */
30570         "years" : true,
30571         /**
30572          * @event days
30573          * getting the data of days
30574          * @param {Roo.bootstrap.DateSplitField} this
30575          * @param {Object} days
30576          */
30577         "days" : true,
30578         /**
30579          * @event invalid
30580          * Fires after the field has been marked as invalid.
30581          * @param {Roo.form.Field} this
30582          * @param {String} msg The validation message
30583          */
30584         invalid : true,
30585        /**
30586          * @event valid
30587          * Fires after the field has been validated with no errors.
30588          * @param {Roo.form.Field} this
30589          */
30590         valid : true
30591     });
30592 };
30593
30594 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30595     
30596     fieldLabel : '',
30597     labelAlign : 'top',
30598     labelWidth : 3,
30599     dayAllowBlank : false,
30600     monthAllowBlank : false,
30601     yearAllowBlank : false,
30602     dayPlaceholder : '',
30603     monthPlaceholder : '',
30604     yearPlaceholder : '',
30605     dayFormat : 'd',
30606     monthFormat : 'm',
30607     yearFormat : 'Y',
30608     isFormField : true,
30609     labellg : 0,
30610     labelmd : 0,
30611     labelsm : 0,
30612     labelxs : 0,
30613     
30614     getAutoCreate : function()
30615     {
30616         var cfg = {
30617             tag : 'div',
30618             cls : 'row roo-date-split-field-group',
30619             cn : [
30620                 {
30621                     tag : 'input',
30622                     type : 'hidden',
30623                     cls : 'form-hidden-field roo-date-split-field-group-value',
30624                     name : this.name
30625                 }
30626             ]
30627         };
30628         
30629         var labelCls = 'col-md-12';
30630         var contentCls = 'col-md-4';
30631         
30632         if(this.fieldLabel){
30633             
30634             var label = {
30635                 tag : 'div',
30636                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30637                 cn : [
30638                     {
30639                         tag : 'label',
30640                         html : this.fieldLabel
30641                     }
30642                 ]
30643             };
30644             
30645             if(this.labelAlign == 'left'){
30646             
30647                 if(this.labelWidth > 12){
30648                     label.style = "width: " + this.labelWidth + 'px';
30649                 }
30650
30651                 if(this.labelWidth < 13 && this.labelmd == 0){
30652                     this.labelmd = this.labelWidth;
30653                 }
30654
30655                 if(this.labellg > 0){
30656                     labelCls = ' col-lg-' + this.labellg;
30657                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30658                 }
30659
30660                 if(this.labelmd > 0){
30661                     labelCls = ' col-md-' + this.labelmd;
30662                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30663                 }
30664
30665                 if(this.labelsm > 0){
30666                     labelCls = ' col-sm-' + this.labelsm;
30667                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30668                 }
30669
30670                 if(this.labelxs > 0){
30671                     labelCls = ' col-xs-' + this.labelxs;
30672                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30673                 }
30674             }
30675             
30676             label.cls += ' ' + labelCls;
30677             
30678             cfg.cn.push(label);
30679         }
30680         
30681         Roo.each(['day', 'month', 'year'], function(t){
30682             cfg.cn.push({
30683                 tag : 'div',
30684                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30685             });
30686         }, this);
30687         
30688         return cfg;
30689     },
30690     
30691     inputEl: function ()
30692     {
30693         return this.el.select('.roo-date-split-field-group-value', true).first();
30694     },
30695     
30696     onRender : function(ct, position) 
30697     {
30698         var _this = this;
30699         
30700         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30701         
30702         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30703         
30704         this.dayField = new Roo.bootstrap.ComboBox({
30705             allowBlank : this.dayAllowBlank,
30706             alwaysQuery : true,
30707             displayField : 'value',
30708             editable : false,
30709             fieldLabel : '',
30710             forceSelection : true,
30711             mode : 'local',
30712             placeholder : this.dayPlaceholder,
30713             selectOnFocus : true,
30714             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30715             triggerAction : 'all',
30716             typeAhead : true,
30717             valueField : 'value',
30718             store : new Roo.data.SimpleStore({
30719                 data : (function() {    
30720                     var days = [];
30721                     _this.fireEvent('days', _this, days);
30722                     return days;
30723                 })(),
30724                 fields : [ 'value' ]
30725             }),
30726             listeners : {
30727                 select : function (_self, record, index)
30728                 {
30729                     _this.setValue(_this.getValue());
30730                 }
30731             }
30732         });
30733
30734         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30735         
30736         this.monthField = new Roo.bootstrap.MonthField({
30737             after : '<i class=\"fa fa-calendar\"></i>',
30738             allowBlank : this.monthAllowBlank,
30739             placeholder : this.monthPlaceholder,
30740             readOnly : true,
30741             listeners : {
30742                 render : function (_self)
30743                 {
30744                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30745                         e.preventDefault();
30746                         _self.focus();
30747                     });
30748                 },
30749                 select : function (_self, oldvalue, newvalue)
30750                 {
30751                     _this.setValue(_this.getValue());
30752                 }
30753             }
30754         });
30755         
30756         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30757         
30758         this.yearField = new Roo.bootstrap.ComboBox({
30759             allowBlank : this.yearAllowBlank,
30760             alwaysQuery : true,
30761             displayField : 'value',
30762             editable : false,
30763             fieldLabel : '',
30764             forceSelection : true,
30765             mode : 'local',
30766             placeholder : this.yearPlaceholder,
30767             selectOnFocus : true,
30768             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30769             triggerAction : 'all',
30770             typeAhead : true,
30771             valueField : 'value',
30772             store : new Roo.data.SimpleStore({
30773                 data : (function() {
30774                     var years = [];
30775                     _this.fireEvent('years', _this, years);
30776                     return years;
30777                 })(),
30778                 fields : [ 'value' ]
30779             }),
30780             listeners : {
30781                 select : function (_self, record, index)
30782                 {
30783                     _this.setValue(_this.getValue());
30784                 }
30785             }
30786         });
30787
30788         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30789     },
30790     
30791     setValue : function(v, format)
30792     {
30793         this.inputEl.dom.value = v;
30794         
30795         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30796         
30797         var d = Date.parseDate(v, f);
30798         
30799         if(!d){
30800             this.validate();
30801             return;
30802         }
30803         
30804         this.setDay(d.format(this.dayFormat));
30805         this.setMonth(d.format(this.monthFormat));
30806         this.setYear(d.format(this.yearFormat));
30807         
30808         this.validate();
30809         
30810         return;
30811     },
30812     
30813     setDay : function(v)
30814     {
30815         this.dayField.setValue(v);
30816         this.inputEl.dom.value = this.getValue();
30817         this.validate();
30818         return;
30819     },
30820     
30821     setMonth : function(v)
30822     {
30823         this.monthField.setValue(v, true);
30824         this.inputEl.dom.value = this.getValue();
30825         this.validate();
30826         return;
30827     },
30828     
30829     setYear : function(v)
30830     {
30831         this.yearField.setValue(v);
30832         this.inputEl.dom.value = this.getValue();
30833         this.validate();
30834         return;
30835     },
30836     
30837     getDay : function()
30838     {
30839         return this.dayField.getValue();
30840     },
30841     
30842     getMonth : function()
30843     {
30844         return this.monthField.getValue();
30845     },
30846     
30847     getYear : function()
30848     {
30849         return this.yearField.getValue();
30850     },
30851     
30852     getValue : function()
30853     {
30854         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30855         
30856         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30857         
30858         return date;
30859     },
30860     
30861     reset : function()
30862     {
30863         this.setDay('');
30864         this.setMonth('');
30865         this.setYear('');
30866         this.inputEl.dom.value = '';
30867         this.validate();
30868         return;
30869     },
30870     
30871     validate : function()
30872     {
30873         var d = this.dayField.validate();
30874         var m = this.monthField.validate();
30875         var y = this.yearField.validate();
30876         
30877         var valid = true;
30878         
30879         if(
30880                 (!this.dayAllowBlank && !d) ||
30881                 (!this.monthAllowBlank && !m) ||
30882                 (!this.yearAllowBlank && !y)
30883         ){
30884             valid = false;
30885         }
30886         
30887         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30888             return valid;
30889         }
30890         
30891         if(valid){
30892             this.markValid();
30893             return valid;
30894         }
30895         
30896         this.markInvalid();
30897         
30898         return valid;
30899     },
30900     
30901     markValid : function()
30902     {
30903         
30904         var label = this.el.select('label', true).first();
30905         var icon = this.el.select('i.fa-star', true).first();
30906
30907         if(label && icon){
30908             icon.remove();
30909         }
30910         
30911         this.fireEvent('valid', this);
30912     },
30913     
30914      /**
30915      * Mark this field as invalid
30916      * @param {String} msg The validation message
30917      */
30918     markInvalid : function(msg)
30919     {
30920         
30921         var label = this.el.select('label', true).first();
30922         var icon = this.el.select('i.fa-star', true).first();
30923
30924         if(label && !icon){
30925             this.el.select('.roo-date-split-field-label', true).createChild({
30926                 tag : 'i',
30927                 cls : 'text-danger fa fa-lg fa-star',
30928                 tooltip : 'This field is required',
30929                 style : 'margin-right:5px;'
30930             }, label, true);
30931         }
30932         
30933         this.fireEvent('invalid', this, msg);
30934     },
30935     
30936     clearInvalid : function()
30937     {
30938         var label = this.el.select('label', true).first();
30939         var icon = this.el.select('i.fa-star', true).first();
30940
30941         if(label && icon){
30942             icon.remove();
30943         }
30944         
30945         this.fireEvent('valid', this);
30946     },
30947     
30948     getName: function()
30949     {
30950         return this.name;
30951     }
30952     
30953 });
30954
30955  /**
30956  *
30957  * This is based on 
30958  * http://masonry.desandro.com
30959  *
30960  * The idea is to render all the bricks based on vertical width...
30961  *
30962  * The original code extends 'outlayer' - we might need to use that....
30963  * 
30964  */
30965
30966
30967 /**
30968  * @class Roo.bootstrap.LayoutMasonry
30969  * @extends Roo.bootstrap.Component
30970  * Bootstrap Layout Masonry class
30971  * 
30972  * @constructor
30973  * Create a new Element
30974  * @param {Object} config The config object
30975  */
30976
30977 Roo.bootstrap.LayoutMasonry = function(config){
30978     
30979     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30980     
30981     this.bricks = [];
30982     
30983     Roo.bootstrap.LayoutMasonry.register(this);
30984     
30985     this.addEvents({
30986         // raw events
30987         /**
30988          * @event layout
30989          * Fire after layout the items
30990          * @param {Roo.bootstrap.LayoutMasonry} this
30991          * @param {Roo.EventObject} e
30992          */
30993         "layout" : true
30994     });
30995     
30996 };
30997
30998 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30999     
31000     /**
31001      * @cfg {Boolean} isLayoutInstant = no animation?
31002      */   
31003     isLayoutInstant : false, // needed?
31004    
31005     /**
31006      * @cfg {Number} boxWidth  width of the columns
31007      */   
31008     boxWidth : 450,
31009     
31010       /**
31011      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31012      */   
31013     boxHeight : 0,
31014     
31015     /**
31016      * @cfg {Number} padWidth padding below box..
31017      */   
31018     padWidth : 10, 
31019     
31020     /**
31021      * @cfg {Number} gutter gutter width..
31022      */   
31023     gutter : 10,
31024     
31025      /**
31026      * @cfg {Number} maxCols maximum number of columns
31027      */   
31028     
31029     maxCols: 0,
31030     
31031     /**
31032      * @cfg {Boolean} isAutoInitial defalut true
31033      */   
31034     isAutoInitial : true, 
31035     
31036     containerWidth: 0,
31037     
31038     /**
31039      * @cfg {Boolean} isHorizontal defalut false
31040      */   
31041     isHorizontal : false, 
31042
31043     currentSize : null,
31044     
31045     tag: 'div',
31046     
31047     cls: '',
31048     
31049     bricks: null, //CompositeElement
31050     
31051     cols : 1,
31052     
31053     _isLayoutInited : false,
31054     
31055 //    isAlternative : false, // only use for vertical layout...
31056     
31057     /**
31058      * @cfg {Number} alternativePadWidth padding below box..
31059      */   
31060     alternativePadWidth : 50,
31061     
31062     selectedBrick : [],
31063     
31064     getAutoCreate : function(){
31065         
31066         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31067         
31068         var cfg = {
31069             tag: this.tag,
31070             cls: 'blog-masonary-wrapper ' + this.cls,
31071             cn : {
31072                 cls : 'mas-boxes masonary'
31073             }
31074         };
31075         
31076         return cfg;
31077     },
31078     
31079     getChildContainer: function( )
31080     {
31081         if (this.boxesEl) {
31082             return this.boxesEl;
31083         }
31084         
31085         this.boxesEl = this.el.select('.mas-boxes').first();
31086         
31087         return this.boxesEl;
31088     },
31089     
31090     
31091     initEvents : function()
31092     {
31093         var _this = this;
31094         
31095         if(this.isAutoInitial){
31096             Roo.log('hook children rendered');
31097             this.on('childrenrendered', function() {
31098                 Roo.log('children rendered');
31099                 _this.initial();
31100             } ,this);
31101         }
31102     },
31103     
31104     initial : function()
31105     {
31106         this.selectedBrick = [];
31107         
31108         this.currentSize = this.el.getBox(true);
31109         
31110         Roo.EventManager.onWindowResize(this.resize, this); 
31111
31112         if(!this.isAutoInitial){
31113             this.layout();
31114             return;
31115         }
31116         
31117         this.layout();
31118         
31119         return;
31120         //this.layout.defer(500,this);
31121         
31122     },
31123     
31124     resize : function()
31125     {
31126         var cs = this.el.getBox(true);
31127         
31128         if (
31129                 this.currentSize.width == cs.width && 
31130                 this.currentSize.x == cs.x && 
31131                 this.currentSize.height == cs.height && 
31132                 this.currentSize.y == cs.y 
31133         ) {
31134             Roo.log("no change in with or X or Y");
31135             return;
31136         }
31137         
31138         this.currentSize = cs;
31139         
31140         this.layout();
31141         
31142     },
31143     
31144     layout : function()
31145     {   
31146         this._resetLayout();
31147         
31148         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31149         
31150         this.layoutItems( isInstant );
31151       
31152         this._isLayoutInited = true;
31153         
31154         this.fireEvent('layout', this);
31155         
31156     },
31157     
31158     _resetLayout : function()
31159     {
31160         if(this.isHorizontal){
31161             this.horizontalMeasureColumns();
31162             return;
31163         }
31164         
31165         this.verticalMeasureColumns();
31166         
31167     },
31168     
31169     verticalMeasureColumns : function()
31170     {
31171         this.getContainerWidth();
31172         
31173 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31174 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31175 //            return;
31176 //        }
31177         
31178         var boxWidth = this.boxWidth + this.padWidth;
31179         
31180         if(this.containerWidth < this.boxWidth){
31181             boxWidth = this.containerWidth
31182         }
31183         
31184         var containerWidth = this.containerWidth;
31185         
31186         var cols = Math.floor(containerWidth / boxWidth);
31187         
31188         this.cols = Math.max( cols, 1 );
31189         
31190         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31191         
31192         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31193         
31194         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31195         
31196         this.colWidth = boxWidth + avail - this.padWidth;
31197         
31198         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31199         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31200     },
31201     
31202     horizontalMeasureColumns : function()
31203     {
31204         this.getContainerWidth();
31205         
31206         var boxWidth = this.boxWidth;
31207         
31208         if(this.containerWidth < boxWidth){
31209             boxWidth = this.containerWidth;
31210         }
31211         
31212         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31213         
31214         this.el.setHeight(boxWidth);
31215         
31216     },
31217     
31218     getContainerWidth : function()
31219     {
31220         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31221     },
31222     
31223     layoutItems : function( isInstant )
31224     {
31225         Roo.log(this.bricks);
31226         
31227         var items = Roo.apply([], this.bricks);
31228         
31229         if(this.isHorizontal){
31230             this._horizontalLayoutItems( items , isInstant );
31231             return;
31232         }
31233         
31234 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31235 //            this._verticalAlternativeLayoutItems( items , isInstant );
31236 //            return;
31237 //        }
31238         
31239         this._verticalLayoutItems( items , isInstant );
31240         
31241     },
31242     
31243     _verticalLayoutItems : function ( items , isInstant)
31244     {
31245         if ( !items || !items.length ) {
31246             return;
31247         }
31248         
31249         var standard = [
31250             ['xs', 'xs', 'xs', 'tall'],
31251             ['xs', 'xs', 'tall'],
31252             ['xs', 'xs', 'sm'],
31253             ['xs', 'xs', 'xs'],
31254             ['xs', 'tall'],
31255             ['xs', 'sm'],
31256             ['xs', 'xs'],
31257             ['xs'],
31258             
31259             ['sm', 'xs', 'xs'],
31260             ['sm', 'xs'],
31261             ['sm'],
31262             
31263             ['tall', 'xs', 'xs', 'xs'],
31264             ['tall', 'xs', 'xs'],
31265             ['tall', 'xs'],
31266             ['tall']
31267             
31268         ];
31269         
31270         var queue = [];
31271         
31272         var boxes = [];
31273         
31274         var box = [];
31275         
31276         Roo.each(items, function(item, k){
31277             
31278             switch (item.size) {
31279                 // these layouts take up a full box,
31280                 case 'md' :
31281                 case 'md-left' :
31282                 case 'md-right' :
31283                 case 'wide' :
31284                     
31285                     if(box.length){
31286                         boxes.push(box);
31287                         box = [];
31288                     }
31289                     
31290                     boxes.push([item]);
31291                     
31292                     break;
31293                     
31294                 case 'xs' :
31295                 case 'sm' :
31296                 case 'tall' :
31297                     
31298                     box.push(item);
31299                     
31300                     break;
31301                 default :
31302                     break;
31303                     
31304             }
31305             
31306         }, this);
31307         
31308         if(box.length){
31309             boxes.push(box);
31310             box = [];
31311         }
31312         
31313         var filterPattern = function(box, length)
31314         {
31315             if(!box.length){
31316                 return;
31317             }
31318             
31319             var match = false;
31320             
31321             var pattern = box.slice(0, length);
31322             
31323             var format = [];
31324             
31325             Roo.each(pattern, function(i){
31326                 format.push(i.size);
31327             }, this);
31328             
31329             Roo.each(standard, function(s){
31330                 
31331                 if(String(s) != String(format)){
31332                     return;
31333                 }
31334                 
31335                 match = true;
31336                 return false;
31337                 
31338             }, this);
31339             
31340             if(!match && length == 1){
31341                 return;
31342             }
31343             
31344             if(!match){
31345                 filterPattern(box, length - 1);
31346                 return;
31347             }
31348                 
31349             queue.push(pattern);
31350
31351             box = box.slice(length, box.length);
31352
31353             filterPattern(box, 4);
31354
31355             return;
31356             
31357         }
31358         
31359         Roo.each(boxes, function(box, k){
31360             
31361             if(!box.length){
31362                 return;
31363             }
31364             
31365             if(box.length == 1){
31366                 queue.push(box);
31367                 return;
31368             }
31369             
31370             filterPattern(box, 4);
31371             
31372         }, this);
31373         
31374         this._processVerticalLayoutQueue( queue, isInstant );
31375         
31376     },
31377     
31378 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31379 //    {
31380 //        if ( !items || !items.length ) {
31381 //            return;
31382 //        }
31383 //
31384 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31385 //        
31386 //    },
31387     
31388     _horizontalLayoutItems : function ( items , isInstant)
31389     {
31390         if ( !items || !items.length || items.length < 3) {
31391             return;
31392         }
31393         
31394         items.reverse();
31395         
31396         var eItems = items.slice(0, 3);
31397         
31398         items = items.slice(3, items.length);
31399         
31400         var standard = [
31401             ['xs', 'xs', 'xs', 'wide'],
31402             ['xs', 'xs', 'wide'],
31403             ['xs', 'xs', 'sm'],
31404             ['xs', 'xs', 'xs'],
31405             ['xs', 'wide'],
31406             ['xs', 'sm'],
31407             ['xs', 'xs'],
31408             ['xs'],
31409             
31410             ['sm', 'xs', 'xs'],
31411             ['sm', 'xs'],
31412             ['sm'],
31413             
31414             ['wide', 'xs', 'xs', 'xs'],
31415             ['wide', 'xs', 'xs'],
31416             ['wide', 'xs'],
31417             ['wide'],
31418             
31419             ['wide-thin']
31420         ];
31421         
31422         var queue = [];
31423         
31424         var boxes = [];
31425         
31426         var box = [];
31427         
31428         Roo.each(items, function(item, k){
31429             
31430             switch (item.size) {
31431                 case 'md' :
31432                 case 'md-left' :
31433                 case 'md-right' :
31434                 case 'tall' :
31435                     
31436                     if(box.length){
31437                         boxes.push(box);
31438                         box = [];
31439                     }
31440                     
31441                     boxes.push([item]);
31442                     
31443                     break;
31444                     
31445                 case 'xs' :
31446                 case 'sm' :
31447                 case 'wide' :
31448                 case 'wide-thin' :
31449                     
31450                     box.push(item);
31451                     
31452                     break;
31453                 default :
31454                     break;
31455                     
31456             }
31457             
31458         }, this);
31459         
31460         if(box.length){
31461             boxes.push(box);
31462             box = [];
31463         }
31464         
31465         var filterPattern = function(box, length)
31466         {
31467             if(!box.length){
31468                 return;
31469             }
31470             
31471             var match = false;
31472             
31473             var pattern = box.slice(0, length);
31474             
31475             var format = [];
31476             
31477             Roo.each(pattern, function(i){
31478                 format.push(i.size);
31479             }, this);
31480             
31481             Roo.each(standard, function(s){
31482                 
31483                 if(String(s) != String(format)){
31484                     return;
31485                 }
31486                 
31487                 match = true;
31488                 return false;
31489                 
31490             }, this);
31491             
31492             if(!match && length == 1){
31493                 return;
31494             }
31495             
31496             if(!match){
31497                 filterPattern(box, length - 1);
31498                 return;
31499             }
31500                 
31501             queue.push(pattern);
31502
31503             box = box.slice(length, box.length);
31504
31505             filterPattern(box, 4);
31506
31507             return;
31508             
31509         }
31510         
31511         Roo.each(boxes, function(box, k){
31512             
31513             if(!box.length){
31514                 return;
31515             }
31516             
31517             if(box.length == 1){
31518                 queue.push(box);
31519                 return;
31520             }
31521             
31522             filterPattern(box, 4);
31523             
31524         }, this);
31525         
31526         
31527         var prune = [];
31528         
31529         var pos = this.el.getBox(true);
31530         
31531         var minX = pos.x;
31532         
31533         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31534         
31535         var hit_end = false;
31536         
31537         Roo.each(queue, function(box){
31538             
31539             if(hit_end){
31540                 
31541                 Roo.each(box, function(b){
31542                 
31543                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31544                     b.el.hide();
31545
31546                 }, this);
31547
31548                 return;
31549             }
31550             
31551             var mx = 0;
31552             
31553             Roo.each(box, function(b){
31554                 
31555                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31556                 b.el.show();
31557
31558                 mx = Math.max(mx, b.x);
31559                 
31560             }, this);
31561             
31562             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31563             
31564             if(maxX < minX){
31565                 
31566                 Roo.each(box, function(b){
31567                 
31568                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31569                     b.el.hide();
31570                     
31571                 }, this);
31572                 
31573                 hit_end = true;
31574                 
31575                 return;
31576             }
31577             
31578             prune.push(box);
31579             
31580         }, this);
31581         
31582         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31583     },
31584     
31585     /** Sets position of item in DOM
31586     * @param {Element} item
31587     * @param {Number} x - horizontal position
31588     * @param {Number} y - vertical position
31589     * @param {Boolean} isInstant - disables transitions
31590     */
31591     _processVerticalLayoutQueue : function( queue, isInstant )
31592     {
31593         var pos = this.el.getBox(true);
31594         var x = pos.x;
31595         var y = pos.y;
31596         var maxY = [];
31597         
31598         for (var i = 0; i < this.cols; i++){
31599             maxY[i] = pos.y;
31600         }
31601         
31602         Roo.each(queue, function(box, k){
31603             
31604             var col = k % this.cols;
31605             
31606             Roo.each(box, function(b,kk){
31607                 
31608                 b.el.position('absolute');
31609                 
31610                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31611                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31612                 
31613                 if(b.size == 'md-left' || b.size == 'md-right'){
31614                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31615                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31616                 }
31617                 
31618                 b.el.setWidth(width);
31619                 b.el.setHeight(height);
31620                 // iframe?
31621                 b.el.select('iframe',true).setSize(width,height);
31622                 
31623             }, this);
31624             
31625             for (var i = 0; i < this.cols; i++){
31626                 
31627                 if(maxY[i] < maxY[col]){
31628                     col = i;
31629                     continue;
31630                 }
31631                 
31632                 col = Math.min(col, i);
31633                 
31634             }
31635             
31636             x = pos.x + col * (this.colWidth + this.padWidth);
31637             
31638             y = maxY[col];
31639             
31640             var positions = [];
31641             
31642             switch (box.length){
31643                 case 1 :
31644                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31645                     break;
31646                 case 2 :
31647                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31648                     break;
31649                 case 3 :
31650                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31651                     break;
31652                 case 4 :
31653                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31654                     break;
31655                 default :
31656                     break;
31657             }
31658             
31659             Roo.each(box, function(b,kk){
31660                 
31661                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31662                 
31663                 var sz = b.el.getSize();
31664                 
31665                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31666                 
31667             }, this);
31668             
31669         }, this);
31670         
31671         var mY = 0;
31672         
31673         for (var i = 0; i < this.cols; i++){
31674             mY = Math.max(mY, maxY[i]);
31675         }
31676         
31677         this.el.setHeight(mY - pos.y);
31678         
31679     },
31680     
31681 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31682 //    {
31683 //        var pos = this.el.getBox(true);
31684 //        var x = pos.x;
31685 //        var y = pos.y;
31686 //        var maxX = pos.right;
31687 //        
31688 //        var maxHeight = 0;
31689 //        
31690 //        Roo.each(items, function(item, k){
31691 //            
31692 //            var c = k % 2;
31693 //            
31694 //            item.el.position('absolute');
31695 //                
31696 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31697 //
31698 //            item.el.setWidth(width);
31699 //
31700 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31701 //
31702 //            item.el.setHeight(height);
31703 //            
31704 //            if(c == 0){
31705 //                item.el.setXY([x, y], isInstant ? false : true);
31706 //            } else {
31707 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31708 //            }
31709 //            
31710 //            y = y + height + this.alternativePadWidth;
31711 //            
31712 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31713 //            
31714 //        }, this);
31715 //        
31716 //        this.el.setHeight(maxHeight);
31717 //        
31718 //    },
31719     
31720     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31721     {
31722         var pos = this.el.getBox(true);
31723         
31724         var minX = pos.x;
31725         var minY = pos.y;
31726         
31727         var maxX = pos.right;
31728         
31729         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31730         
31731         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31732         
31733         Roo.each(queue, function(box, k){
31734             
31735             Roo.each(box, function(b, kk){
31736                 
31737                 b.el.position('absolute');
31738                 
31739                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31740                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31741                 
31742                 if(b.size == 'md-left' || b.size == 'md-right'){
31743                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31744                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31745                 }
31746                 
31747                 b.el.setWidth(width);
31748                 b.el.setHeight(height);
31749                 
31750             }, this);
31751             
31752             if(!box.length){
31753                 return;
31754             }
31755             
31756             var positions = [];
31757             
31758             switch (box.length){
31759                 case 1 :
31760                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31761                     break;
31762                 case 2 :
31763                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31764                     break;
31765                 case 3 :
31766                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31767                     break;
31768                 case 4 :
31769                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31770                     break;
31771                 default :
31772                     break;
31773             }
31774             
31775             Roo.each(box, function(b,kk){
31776                 
31777                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31778                 
31779                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31780                 
31781             }, this);
31782             
31783         }, this);
31784         
31785     },
31786     
31787     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31788     {
31789         Roo.each(eItems, function(b,k){
31790             
31791             b.size = (k == 0) ? 'sm' : 'xs';
31792             b.x = (k == 0) ? 2 : 1;
31793             b.y = (k == 0) ? 2 : 1;
31794             
31795             b.el.position('absolute');
31796             
31797             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31798                 
31799             b.el.setWidth(width);
31800             
31801             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31802             
31803             b.el.setHeight(height);
31804             
31805         }, this);
31806
31807         var positions = [];
31808         
31809         positions.push({
31810             x : maxX - this.unitWidth * 2 - this.gutter,
31811             y : minY
31812         });
31813         
31814         positions.push({
31815             x : maxX - this.unitWidth,
31816             y : minY + (this.unitWidth + this.gutter) * 2
31817         });
31818         
31819         positions.push({
31820             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31821             y : minY
31822         });
31823         
31824         Roo.each(eItems, function(b,k){
31825             
31826             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31827
31828         }, this);
31829         
31830     },
31831     
31832     getVerticalOneBoxColPositions : function(x, y, box)
31833     {
31834         var pos = [];
31835         
31836         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31837         
31838         if(box[0].size == 'md-left'){
31839             rand = 0;
31840         }
31841         
31842         if(box[0].size == 'md-right'){
31843             rand = 1;
31844         }
31845         
31846         pos.push({
31847             x : x + (this.unitWidth + this.gutter) * rand,
31848             y : y
31849         });
31850         
31851         return pos;
31852     },
31853     
31854     getVerticalTwoBoxColPositions : function(x, y, box)
31855     {
31856         var pos = [];
31857         
31858         if(box[0].size == 'xs'){
31859             
31860             pos.push({
31861                 x : x,
31862                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31863             });
31864
31865             pos.push({
31866                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31867                 y : y
31868             });
31869             
31870             return pos;
31871             
31872         }
31873         
31874         pos.push({
31875             x : x,
31876             y : y
31877         });
31878
31879         pos.push({
31880             x : x + (this.unitWidth + this.gutter) * 2,
31881             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31882         });
31883         
31884         return pos;
31885         
31886     },
31887     
31888     getVerticalThreeBoxColPositions : function(x, y, box)
31889     {
31890         var pos = [];
31891         
31892         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31893             
31894             pos.push({
31895                 x : x,
31896                 y : y
31897             });
31898
31899             pos.push({
31900                 x : x + (this.unitWidth + this.gutter) * 1,
31901                 y : y
31902             });
31903             
31904             pos.push({
31905                 x : x + (this.unitWidth + this.gutter) * 2,
31906                 y : y
31907             });
31908             
31909             return pos;
31910             
31911         }
31912         
31913         if(box[0].size == 'xs' && box[1].size == 'xs'){
31914             
31915             pos.push({
31916                 x : x,
31917                 y : y
31918             });
31919
31920             pos.push({
31921                 x : x,
31922                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31923             });
31924             
31925             pos.push({
31926                 x : x + (this.unitWidth + this.gutter) * 1,
31927                 y : y
31928             });
31929             
31930             return pos;
31931             
31932         }
31933         
31934         pos.push({
31935             x : x,
31936             y : y
31937         });
31938
31939         pos.push({
31940             x : x + (this.unitWidth + this.gutter) * 2,
31941             y : y
31942         });
31943
31944         pos.push({
31945             x : x + (this.unitWidth + this.gutter) * 2,
31946             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31947         });
31948             
31949         return pos;
31950         
31951     },
31952     
31953     getVerticalFourBoxColPositions : function(x, y, box)
31954     {
31955         var pos = [];
31956         
31957         if(box[0].size == 'xs'){
31958             
31959             pos.push({
31960                 x : x,
31961                 y : y
31962             });
31963
31964             pos.push({
31965                 x : x,
31966                 y : y + (this.unitHeight + this.gutter) * 1
31967             });
31968             
31969             pos.push({
31970                 x : x,
31971                 y : y + (this.unitHeight + this.gutter) * 2
31972             });
31973             
31974             pos.push({
31975                 x : x + (this.unitWidth + this.gutter) * 1,
31976                 y : y
31977             });
31978             
31979             return pos;
31980             
31981         }
31982         
31983         pos.push({
31984             x : x,
31985             y : y
31986         });
31987
31988         pos.push({
31989             x : x + (this.unitWidth + this.gutter) * 2,
31990             y : y
31991         });
31992
31993         pos.push({
31994             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31995             y : y + (this.unitHeight + this.gutter) * 1
31996         });
31997
31998         pos.push({
31999             x : x + (this.unitWidth + this.gutter) * 2,
32000             y : y + (this.unitWidth + this.gutter) * 2
32001         });
32002
32003         return pos;
32004         
32005     },
32006     
32007     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32008     {
32009         var pos = [];
32010         
32011         if(box[0].size == 'md-left'){
32012             pos.push({
32013                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32014                 y : minY
32015             });
32016             
32017             return pos;
32018         }
32019         
32020         if(box[0].size == 'md-right'){
32021             pos.push({
32022                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32023                 y : minY + (this.unitWidth + this.gutter) * 1
32024             });
32025             
32026             return pos;
32027         }
32028         
32029         var rand = Math.floor(Math.random() * (4 - box[0].y));
32030         
32031         pos.push({
32032             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32033             y : minY + (this.unitWidth + this.gutter) * rand
32034         });
32035         
32036         return pos;
32037         
32038     },
32039     
32040     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32041     {
32042         var pos = [];
32043         
32044         if(box[0].size == 'xs'){
32045             
32046             pos.push({
32047                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32048                 y : minY
32049             });
32050
32051             pos.push({
32052                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32053                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32054             });
32055             
32056             return pos;
32057             
32058         }
32059         
32060         pos.push({
32061             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32062             y : minY
32063         });
32064
32065         pos.push({
32066             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32067             y : minY + (this.unitWidth + this.gutter) * 2
32068         });
32069         
32070         return pos;
32071         
32072     },
32073     
32074     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32075     {
32076         var pos = [];
32077         
32078         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32079             
32080             pos.push({
32081                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32082                 y : minY
32083             });
32084
32085             pos.push({
32086                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32087                 y : minY + (this.unitWidth + this.gutter) * 1
32088             });
32089             
32090             pos.push({
32091                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32092                 y : minY + (this.unitWidth + this.gutter) * 2
32093             });
32094             
32095             return pos;
32096             
32097         }
32098         
32099         if(box[0].size == 'xs' && box[1].size == 'xs'){
32100             
32101             pos.push({
32102                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32103                 y : minY
32104             });
32105
32106             pos.push({
32107                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32108                 y : minY
32109             });
32110             
32111             pos.push({
32112                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32113                 y : minY + (this.unitWidth + this.gutter) * 1
32114             });
32115             
32116             return pos;
32117             
32118         }
32119         
32120         pos.push({
32121             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32122             y : minY
32123         });
32124
32125         pos.push({
32126             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32127             y : minY + (this.unitWidth + this.gutter) * 2
32128         });
32129
32130         pos.push({
32131             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32132             y : minY + (this.unitWidth + this.gutter) * 2
32133         });
32134             
32135         return pos;
32136         
32137     },
32138     
32139     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32140     {
32141         var pos = [];
32142         
32143         if(box[0].size == 'xs'){
32144             
32145             pos.push({
32146                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32147                 y : minY
32148             });
32149
32150             pos.push({
32151                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32152                 y : minY
32153             });
32154             
32155             pos.push({
32156                 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),
32157                 y : minY
32158             });
32159             
32160             pos.push({
32161                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32162                 y : minY + (this.unitWidth + this.gutter) * 1
32163             });
32164             
32165             return pos;
32166             
32167         }
32168         
32169         pos.push({
32170             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32171             y : minY
32172         });
32173         
32174         pos.push({
32175             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32176             y : minY + (this.unitWidth + this.gutter) * 2
32177         });
32178         
32179         pos.push({
32180             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32181             y : minY + (this.unitWidth + this.gutter) * 2
32182         });
32183         
32184         pos.push({
32185             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),
32186             y : minY + (this.unitWidth + this.gutter) * 2
32187         });
32188
32189         return pos;
32190         
32191     },
32192     
32193     /**
32194     * remove a Masonry Brick
32195     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32196     */
32197     removeBrick : function(brick_id)
32198     {
32199         if (!brick_id) {
32200             return;
32201         }
32202         
32203         for (var i = 0; i<this.bricks.length; i++) {
32204             if (this.bricks[i].id == brick_id) {
32205                 this.bricks.splice(i,1);
32206                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32207                 this.initial();
32208             }
32209         }
32210     },
32211     
32212     /**
32213     * adds a Masonry Brick
32214     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32215     */
32216     addBrick : function(cfg)
32217     {
32218         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32219         //this.register(cn);
32220         cn.parentId = this.id;
32221         cn.render(this.el);
32222         return cn;
32223     },
32224     
32225     /**
32226     * register a Masonry Brick
32227     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32228     */
32229     
32230     register : function(brick)
32231     {
32232         this.bricks.push(brick);
32233         brick.masonryId = this.id;
32234     },
32235     
32236     /**
32237     * clear all the Masonry Brick
32238     */
32239     clearAll : function()
32240     {
32241         this.bricks = [];
32242         //this.getChildContainer().dom.innerHTML = "";
32243         this.el.dom.innerHTML = '';
32244     },
32245     
32246     getSelected : function()
32247     {
32248         if (!this.selectedBrick) {
32249             return false;
32250         }
32251         
32252         return this.selectedBrick;
32253     }
32254 });
32255
32256 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32257     
32258     groups: {},
32259      /**
32260     * register a Masonry Layout
32261     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32262     */
32263     
32264     register : function(layout)
32265     {
32266         this.groups[layout.id] = layout;
32267     },
32268     /**
32269     * fetch a  Masonry Layout based on the masonry layout ID
32270     * @param {string} the masonry layout to add
32271     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32272     */
32273     
32274     get: function(layout_id) {
32275         if (typeof(this.groups[layout_id]) == 'undefined') {
32276             return false;
32277         }
32278         return this.groups[layout_id] ;
32279     }
32280     
32281     
32282     
32283 });
32284
32285  
32286
32287  /**
32288  *
32289  * This is based on 
32290  * http://masonry.desandro.com
32291  *
32292  * The idea is to render all the bricks based on vertical width...
32293  *
32294  * The original code extends 'outlayer' - we might need to use that....
32295  * 
32296  */
32297
32298
32299 /**
32300  * @class Roo.bootstrap.LayoutMasonryAuto
32301  * @extends Roo.bootstrap.Component
32302  * Bootstrap Layout Masonry class
32303  * 
32304  * @constructor
32305  * Create a new Element
32306  * @param {Object} config The config object
32307  */
32308
32309 Roo.bootstrap.LayoutMasonryAuto = function(config){
32310     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32311 };
32312
32313 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32314     
32315       /**
32316      * @cfg {Boolean} isFitWidth  - resize the width..
32317      */   
32318     isFitWidth : false,  // options..
32319     /**
32320      * @cfg {Boolean} isOriginLeft = left align?
32321      */   
32322     isOriginLeft : true,
32323     /**
32324      * @cfg {Boolean} isOriginTop = top align?
32325      */   
32326     isOriginTop : false,
32327     /**
32328      * @cfg {Boolean} isLayoutInstant = no animation?
32329      */   
32330     isLayoutInstant : false, // needed?
32331     /**
32332      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32333      */   
32334     isResizingContainer : true,
32335     /**
32336      * @cfg {Number} columnWidth  width of the columns 
32337      */   
32338     
32339     columnWidth : 0,
32340     
32341     /**
32342      * @cfg {Number} maxCols maximum number of columns
32343      */   
32344     
32345     maxCols: 0,
32346     /**
32347      * @cfg {Number} padHeight padding below box..
32348      */   
32349     
32350     padHeight : 10, 
32351     
32352     /**
32353      * @cfg {Boolean} isAutoInitial defalut true
32354      */   
32355     
32356     isAutoInitial : true, 
32357     
32358     // private?
32359     gutter : 0,
32360     
32361     containerWidth: 0,
32362     initialColumnWidth : 0,
32363     currentSize : null,
32364     
32365     colYs : null, // array.
32366     maxY : 0,
32367     padWidth: 10,
32368     
32369     
32370     tag: 'div',
32371     cls: '',
32372     bricks: null, //CompositeElement
32373     cols : 0, // array?
32374     // element : null, // wrapped now this.el
32375     _isLayoutInited : null, 
32376     
32377     
32378     getAutoCreate : function(){
32379         
32380         var cfg = {
32381             tag: this.tag,
32382             cls: 'blog-masonary-wrapper ' + this.cls,
32383             cn : {
32384                 cls : 'mas-boxes masonary'
32385             }
32386         };
32387         
32388         return cfg;
32389     },
32390     
32391     getChildContainer: function( )
32392     {
32393         if (this.boxesEl) {
32394             return this.boxesEl;
32395         }
32396         
32397         this.boxesEl = this.el.select('.mas-boxes').first();
32398         
32399         return this.boxesEl;
32400     },
32401     
32402     
32403     initEvents : function()
32404     {
32405         var _this = this;
32406         
32407         if(this.isAutoInitial){
32408             Roo.log('hook children rendered');
32409             this.on('childrenrendered', function() {
32410                 Roo.log('children rendered');
32411                 _this.initial();
32412             } ,this);
32413         }
32414         
32415     },
32416     
32417     initial : function()
32418     {
32419         this.reloadItems();
32420
32421         this.currentSize = this.el.getBox(true);
32422
32423         /// was window resize... - let's see if this works..
32424         Roo.EventManager.onWindowResize(this.resize, this); 
32425
32426         if(!this.isAutoInitial){
32427             this.layout();
32428             return;
32429         }
32430         
32431         this.layout.defer(500,this);
32432     },
32433     
32434     reloadItems: function()
32435     {
32436         this.bricks = this.el.select('.masonry-brick', true);
32437         
32438         this.bricks.each(function(b) {
32439             //Roo.log(b.getSize());
32440             if (!b.attr('originalwidth')) {
32441                 b.attr('originalwidth',  b.getSize().width);
32442             }
32443             
32444         });
32445         
32446         Roo.log(this.bricks.elements.length);
32447     },
32448     
32449     resize : function()
32450     {
32451         Roo.log('resize');
32452         var cs = this.el.getBox(true);
32453         
32454         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32455             Roo.log("no change in with or X");
32456             return;
32457         }
32458         this.currentSize = cs;
32459         this.layout();
32460     },
32461     
32462     layout : function()
32463     {
32464          Roo.log('layout');
32465         this._resetLayout();
32466         //this._manageStamps();
32467       
32468         // don't animate first layout
32469         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32470         this.layoutItems( isInstant );
32471       
32472         // flag for initalized
32473         this._isLayoutInited = true;
32474     },
32475     
32476     layoutItems : function( isInstant )
32477     {
32478         //var items = this._getItemsForLayout( this.items );
32479         // original code supports filtering layout items.. we just ignore it..
32480         
32481         this._layoutItems( this.bricks , isInstant );
32482       
32483         this._postLayout();
32484     },
32485     _layoutItems : function ( items , isInstant)
32486     {
32487        //this.fireEvent( 'layout', this, items );
32488     
32489
32490         if ( !items || !items.elements.length ) {
32491           // no items, emit event with empty array
32492             return;
32493         }
32494
32495         var queue = [];
32496         items.each(function(item) {
32497             Roo.log("layout item");
32498             Roo.log(item);
32499             // get x/y object from method
32500             var position = this._getItemLayoutPosition( item );
32501             // enqueue
32502             position.item = item;
32503             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32504             queue.push( position );
32505         }, this);
32506       
32507         this._processLayoutQueue( queue );
32508     },
32509     /** Sets position of item in DOM
32510     * @param {Element} item
32511     * @param {Number} x - horizontal position
32512     * @param {Number} y - vertical position
32513     * @param {Boolean} isInstant - disables transitions
32514     */
32515     _processLayoutQueue : function( queue )
32516     {
32517         for ( var i=0, len = queue.length; i < len; i++ ) {
32518             var obj = queue[i];
32519             obj.item.position('absolute');
32520             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32521         }
32522     },
32523       
32524     
32525     /**
32526     * Any logic you want to do after each layout,
32527     * i.e. size the container
32528     */
32529     _postLayout : function()
32530     {
32531         this.resizeContainer();
32532     },
32533     
32534     resizeContainer : function()
32535     {
32536         if ( !this.isResizingContainer ) {
32537             return;
32538         }
32539         var size = this._getContainerSize();
32540         if ( size ) {
32541             this.el.setSize(size.width,size.height);
32542             this.boxesEl.setSize(size.width,size.height);
32543         }
32544     },
32545     
32546     
32547     
32548     _resetLayout : function()
32549     {
32550         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32551         this.colWidth = this.el.getWidth();
32552         //this.gutter = this.el.getWidth(); 
32553         
32554         this.measureColumns();
32555
32556         // reset column Y
32557         var i = this.cols;
32558         this.colYs = [];
32559         while (i--) {
32560             this.colYs.push( 0 );
32561         }
32562     
32563         this.maxY = 0;
32564     },
32565
32566     measureColumns : function()
32567     {
32568         this.getContainerWidth();
32569       // if columnWidth is 0, default to outerWidth of first item
32570         if ( !this.columnWidth ) {
32571             var firstItem = this.bricks.first();
32572             Roo.log(firstItem);
32573             this.columnWidth  = this.containerWidth;
32574             if (firstItem && firstItem.attr('originalwidth') ) {
32575                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32576             }
32577             // columnWidth fall back to item of first element
32578             Roo.log("set column width?");
32579                         this.initialColumnWidth = this.columnWidth  ;
32580
32581             // if first elem has no width, default to size of container
32582             
32583         }
32584         
32585         
32586         if (this.initialColumnWidth) {
32587             this.columnWidth = this.initialColumnWidth;
32588         }
32589         
32590         
32591             
32592         // column width is fixed at the top - however if container width get's smaller we should
32593         // reduce it...
32594         
32595         // this bit calcs how man columns..
32596             
32597         var columnWidth = this.columnWidth += this.gutter;
32598       
32599         // calculate columns
32600         var containerWidth = this.containerWidth + this.gutter;
32601         
32602         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32603         // fix rounding errors, typically with gutters
32604         var excess = columnWidth - containerWidth % columnWidth;
32605         
32606         
32607         // if overshoot is less than a pixel, round up, otherwise floor it
32608         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32609         cols = Math[ mathMethod ]( cols );
32610         this.cols = Math.max( cols, 1 );
32611         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32612         
32613          // padding positioning..
32614         var totalColWidth = this.cols * this.columnWidth;
32615         var padavail = this.containerWidth - totalColWidth;
32616         // so for 2 columns - we need 3 'pads'
32617         
32618         var padNeeded = (1+this.cols) * this.padWidth;
32619         
32620         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32621         
32622         this.columnWidth += padExtra
32623         //this.padWidth = Math.floor(padavail /  ( this.cols));
32624         
32625         // adjust colum width so that padding is fixed??
32626         
32627         // we have 3 columns ... total = width * 3
32628         // we have X left over... that should be used by 
32629         
32630         //if (this.expandC) {
32631             
32632         //}
32633         
32634         
32635         
32636     },
32637     
32638     getContainerWidth : function()
32639     {
32640        /* // container is parent if fit width
32641         var container = this.isFitWidth ? this.element.parentNode : this.element;
32642         // check that this.size and size are there
32643         // IE8 triggers resize on body size change, so they might not be
32644         
32645         var size = getSize( container );  //FIXME
32646         this.containerWidth = size && size.innerWidth; //FIXME
32647         */
32648          
32649         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32650         
32651     },
32652     
32653     _getItemLayoutPosition : function( item )  // what is item?
32654     {
32655         // we resize the item to our columnWidth..
32656       
32657         item.setWidth(this.columnWidth);
32658         item.autoBoxAdjust  = false;
32659         
32660         var sz = item.getSize();
32661  
32662         // how many columns does this brick span
32663         var remainder = this.containerWidth % this.columnWidth;
32664         
32665         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32666         // round if off by 1 pixel, otherwise use ceil
32667         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32668         colSpan = Math.min( colSpan, this.cols );
32669         
32670         // normally this should be '1' as we dont' currently allow multi width columns..
32671         
32672         var colGroup = this._getColGroup( colSpan );
32673         // get the minimum Y value from the columns
32674         var minimumY = Math.min.apply( Math, colGroup );
32675         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32676         
32677         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32678          
32679         // position the brick
32680         var position = {
32681             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32682             y: this.currentSize.y + minimumY + this.padHeight
32683         };
32684         
32685         Roo.log(position);
32686         // apply setHeight to necessary columns
32687         var setHeight = minimumY + sz.height + this.padHeight;
32688         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32689         
32690         var setSpan = this.cols + 1 - colGroup.length;
32691         for ( var i = 0; i < setSpan; i++ ) {
32692           this.colYs[ shortColIndex + i ] = setHeight ;
32693         }
32694       
32695         return position;
32696     },
32697     
32698     /**
32699      * @param {Number} colSpan - number of columns the element spans
32700      * @returns {Array} colGroup
32701      */
32702     _getColGroup : function( colSpan )
32703     {
32704         if ( colSpan < 2 ) {
32705           // if brick spans only one column, use all the column Ys
32706           return this.colYs;
32707         }
32708       
32709         var colGroup = [];
32710         // how many different places could this brick fit horizontally
32711         var groupCount = this.cols + 1 - colSpan;
32712         // for each group potential horizontal position
32713         for ( var i = 0; i < groupCount; i++ ) {
32714           // make an array of colY values for that one group
32715           var groupColYs = this.colYs.slice( i, i + colSpan );
32716           // and get the max value of the array
32717           colGroup[i] = Math.max.apply( Math, groupColYs );
32718         }
32719         return colGroup;
32720     },
32721     /*
32722     _manageStamp : function( stamp )
32723     {
32724         var stampSize =  stamp.getSize();
32725         var offset = stamp.getBox();
32726         // get the columns that this stamp affects
32727         var firstX = this.isOriginLeft ? offset.x : offset.right;
32728         var lastX = firstX + stampSize.width;
32729         var firstCol = Math.floor( firstX / this.columnWidth );
32730         firstCol = Math.max( 0, firstCol );
32731         
32732         var lastCol = Math.floor( lastX / this.columnWidth );
32733         // lastCol should not go over if multiple of columnWidth #425
32734         lastCol -= lastX % this.columnWidth ? 0 : 1;
32735         lastCol = Math.min( this.cols - 1, lastCol );
32736         
32737         // set colYs to bottom of the stamp
32738         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32739             stampSize.height;
32740             
32741         for ( var i = firstCol; i <= lastCol; i++ ) {
32742           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32743         }
32744     },
32745     */
32746     
32747     _getContainerSize : function()
32748     {
32749         this.maxY = Math.max.apply( Math, this.colYs );
32750         var size = {
32751             height: this.maxY
32752         };
32753       
32754         if ( this.isFitWidth ) {
32755             size.width = this._getContainerFitWidth();
32756         }
32757       
32758         return size;
32759     },
32760     
32761     _getContainerFitWidth : function()
32762     {
32763         var unusedCols = 0;
32764         // count unused columns
32765         var i = this.cols;
32766         while ( --i ) {
32767           if ( this.colYs[i] !== 0 ) {
32768             break;
32769           }
32770           unusedCols++;
32771         }
32772         // fit container to columns that have been used
32773         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32774     },
32775     
32776     needsResizeLayout : function()
32777     {
32778         var previousWidth = this.containerWidth;
32779         this.getContainerWidth();
32780         return previousWidth !== this.containerWidth;
32781     }
32782  
32783 });
32784
32785  
32786
32787  /*
32788  * - LGPL
32789  *
32790  * element
32791  * 
32792  */
32793
32794 /**
32795  * @class Roo.bootstrap.MasonryBrick
32796  * @extends Roo.bootstrap.Component
32797  * Bootstrap MasonryBrick class
32798  * 
32799  * @constructor
32800  * Create a new MasonryBrick
32801  * @param {Object} config The config object
32802  */
32803
32804 Roo.bootstrap.MasonryBrick = function(config){
32805     
32806     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32807     
32808     Roo.bootstrap.MasonryBrick.register(this);
32809     
32810     this.addEvents({
32811         // raw events
32812         /**
32813          * @event click
32814          * When a MasonryBrick is clcik
32815          * @param {Roo.bootstrap.MasonryBrick} this
32816          * @param {Roo.EventObject} e
32817          */
32818         "click" : true
32819     });
32820 };
32821
32822 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32823     
32824     /**
32825      * @cfg {String} title
32826      */   
32827     title : '',
32828     /**
32829      * @cfg {String} html
32830      */   
32831     html : '',
32832     /**
32833      * @cfg {String} bgimage
32834      */   
32835     bgimage : '',
32836     /**
32837      * @cfg {String} videourl
32838      */   
32839     videourl : '',
32840     /**
32841      * @cfg {String} cls
32842      */   
32843     cls : '',
32844     /**
32845      * @cfg {String} href
32846      */   
32847     href : '',
32848     /**
32849      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32850      */   
32851     size : 'xs',
32852     
32853     /**
32854      * @cfg {String} placetitle (center|bottom)
32855      */   
32856     placetitle : '',
32857     
32858     /**
32859      * @cfg {Boolean} isFitContainer defalut true
32860      */   
32861     isFitContainer : true, 
32862     
32863     /**
32864      * @cfg {Boolean} preventDefault defalut false
32865      */   
32866     preventDefault : false, 
32867     
32868     /**
32869      * @cfg {Boolean} inverse defalut false
32870      */   
32871     maskInverse : false, 
32872     
32873     getAutoCreate : function()
32874     {
32875         if(!this.isFitContainer){
32876             return this.getSplitAutoCreate();
32877         }
32878         
32879         var cls = 'masonry-brick masonry-brick-full';
32880         
32881         if(this.href.length){
32882             cls += ' masonry-brick-link';
32883         }
32884         
32885         if(this.bgimage.length){
32886             cls += ' masonry-brick-image';
32887         }
32888         
32889         if(this.maskInverse){
32890             cls += ' mask-inverse';
32891         }
32892         
32893         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32894             cls += ' enable-mask';
32895         }
32896         
32897         if(this.size){
32898             cls += ' masonry-' + this.size + '-brick';
32899         }
32900         
32901         if(this.placetitle.length){
32902             
32903             switch (this.placetitle) {
32904                 case 'center' :
32905                     cls += ' masonry-center-title';
32906                     break;
32907                 case 'bottom' :
32908                     cls += ' masonry-bottom-title';
32909                     break;
32910                 default:
32911                     break;
32912             }
32913             
32914         } else {
32915             if(!this.html.length && !this.bgimage.length){
32916                 cls += ' masonry-center-title';
32917             }
32918
32919             if(!this.html.length && this.bgimage.length){
32920                 cls += ' masonry-bottom-title';
32921             }
32922         }
32923         
32924         if(this.cls){
32925             cls += ' ' + this.cls;
32926         }
32927         
32928         var cfg = {
32929             tag: (this.href.length) ? 'a' : 'div',
32930             cls: cls,
32931             cn: [
32932                 {
32933                     tag: 'div',
32934                     cls: 'masonry-brick-mask'
32935                 },
32936                 {
32937                     tag: 'div',
32938                     cls: 'masonry-brick-paragraph',
32939                     cn: []
32940                 }
32941             ]
32942         };
32943         
32944         if(this.href.length){
32945             cfg.href = this.href;
32946         }
32947         
32948         var cn = cfg.cn[1].cn;
32949         
32950         if(this.title.length){
32951             cn.push({
32952                 tag: 'h4',
32953                 cls: 'masonry-brick-title',
32954                 html: this.title
32955             });
32956         }
32957         
32958         if(this.html.length){
32959             cn.push({
32960                 tag: 'p',
32961                 cls: 'masonry-brick-text',
32962                 html: this.html
32963             });
32964         }
32965         
32966         if (!this.title.length && !this.html.length) {
32967             cfg.cn[1].cls += ' hide';
32968         }
32969         
32970         if(this.bgimage.length){
32971             cfg.cn.push({
32972                 tag: 'img',
32973                 cls: 'masonry-brick-image-view',
32974                 src: this.bgimage
32975             });
32976         }
32977         
32978         if(this.videourl.length){
32979             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32980             // youtube support only?
32981             cfg.cn.push({
32982                 tag: 'iframe',
32983                 cls: 'masonry-brick-image-view',
32984                 src: vurl,
32985                 frameborder : 0,
32986                 allowfullscreen : true
32987             });
32988         }
32989         
32990         return cfg;
32991         
32992     },
32993     
32994     getSplitAutoCreate : function()
32995     {
32996         var cls = 'masonry-brick masonry-brick-split';
32997         
32998         if(this.href.length){
32999             cls += ' masonry-brick-link';
33000         }
33001         
33002         if(this.bgimage.length){
33003             cls += ' masonry-brick-image';
33004         }
33005         
33006         if(this.size){
33007             cls += ' masonry-' + this.size + '-brick';
33008         }
33009         
33010         switch (this.placetitle) {
33011             case 'center' :
33012                 cls += ' masonry-center-title';
33013                 break;
33014             case 'bottom' :
33015                 cls += ' masonry-bottom-title';
33016                 break;
33017             default:
33018                 if(!this.bgimage.length){
33019                     cls += ' masonry-center-title';
33020                 }
33021
33022                 if(this.bgimage.length){
33023                     cls += ' masonry-bottom-title';
33024                 }
33025                 break;
33026         }
33027         
33028         if(this.cls){
33029             cls += ' ' + this.cls;
33030         }
33031         
33032         var cfg = {
33033             tag: (this.href.length) ? 'a' : 'div',
33034             cls: cls,
33035             cn: [
33036                 {
33037                     tag: 'div',
33038                     cls: 'masonry-brick-split-head',
33039                     cn: [
33040                         {
33041                             tag: 'div',
33042                             cls: 'masonry-brick-paragraph',
33043                             cn: []
33044                         }
33045                     ]
33046                 },
33047                 {
33048                     tag: 'div',
33049                     cls: 'masonry-brick-split-body',
33050                     cn: []
33051                 }
33052             ]
33053         };
33054         
33055         if(this.href.length){
33056             cfg.href = this.href;
33057         }
33058         
33059         if(this.title.length){
33060             cfg.cn[0].cn[0].cn.push({
33061                 tag: 'h4',
33062                 cls: 'masonry-brick-title',
33063                 html: this.title
33064             });
33065         }
33066         
33067         if(this.html.length){
33068             cfg.cn[1].cn.push({
33069                 tag: 'p',
33070                 cls: 'masonry-brick-text',
33071                 html: this.html
33072             });
33073         }
33074
33075         if(this.bgimage.length){
33076             cfg.cn[0].cn.push({
33077                 tag: 'img',
33078                 cls: 'masonry-brick-image-view',
33079                 src: this.bgimage
33080             });
33081         }
33082         
33083         if(this.videourl.length){
33084             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33085             // youtube support only?
33086             cfg.cn[0].cn.cn.push({
33087                 tag: 'iframe',
33088                 cls: 'masonry-brick-image-view',
33089                 src: vurl,
33090                 frameborder : 0,
33091                 allowfullscreen : true
33092             });
33093         }
33094         
33095         return cfg;
33096     },
33097     
33098     initEvents: function() 
33099     {
33100         switch (this.size) {
33101             case 'xs' :
33102                 this.x = 1;
33103                 this.y = 1;
33104                 break;
33105             case 'sm' :
33106                 this.x = 2;
33107                 this.y = 2;
33108                 break;
33109             case 'md' :
33110             case 'md-left' :
33111             case 'md-right' :
33112                 this.x = 3;
33113                 this.y = 3;
33114                 break;
33115             case 'tall' :
33116                 this.x = 2;
33117                 this.y = 3;
33118                 break;
33119             case 'wide' :
33120                 this.x = 3;
33121                 this.y = 2;
33122                 break;
33123             case 'wide-thin' :
33124                 this.x = 3;
33125                 this.y = 1;
33126                 break;
33127                         
33128             default :
33129                 break;
33130         }
33131         
33132         if(Roo.isTouch){
33133             this.el.on('touchstart', this.onTouchStart, this);
33134             this.el.on('touchmove', this.onTouchMove, this);
33135             this.el.on('touchend', this.onTouchEnd, this);
33136             this.el.on('contextmenu', this.onContextMenu, this);
33137         } else {
33138             this.el.on('mouseenter'  ,this.enter, this);
33139             this.el.on('mouseleave', this.leave, this);
33140             this.el.on('click', this.onClick, this);
33141         }
33142         
33143         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33144             this.parent().bricks.push(this);   
33145         }
33146         
33147     },
33148     
33149     onClick: function(e, el)
33150     {
33151         var time = this.endTimer - this.startTimer;
33152         // Roo.log(e.preventDefault());
33153         if(Roo.isTouch){
33154             if(time > 1000){
33155                 e.preventDefault();
33156                 return;
33157             }
33158         }
33159         
33160         if(!this.preventDefault){
33161             return;
33162         }
33163         
33164         e.preventDefault();
33165         
33166         if (this.activeClass != '') {
33167             this.selectBrick();
33168         }
33169         
33170         this.fireEvent('click', this, e);
33171     },
33172     
33173     enter: function(e, el)
33174     {
33175         e.preventDefault();
33176         
33177         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33178             return;
33179         }
33180         
33181         if(this.bgimage.length && this.html.length){
33182             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33183         }
33184     },
33185     
33186     leave: function(e, el)
33187     {
33188         e.preventDefault();
33189         
33190         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33191             return;
33192         }
33193         
33194         if(this.bgimage.length && this.html.length){
33195             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33196         }
33197     },
33198     
33199     onTouchStart: function(e, el)
33200     {
33201 //        e.preventDefault();
33202         
33203         this.touchmoved = false;
33204         
33205         if(!this.isFitContainer){
33206             return;
33207         }
33208         
33209         if(!this.bgimage.length || !this.html.length){
33210             return;
33211         }
33212         
33213         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33214         
33215         this.timer = new Date().getTime();
33216         
33217     },
33218     
33219     onTouchMove: function(e, el)
33220     {
33221         this.touchmoved = true;
33222     },
33223     
33224     onContextMenu : function(e,el)
33225     {
33226         e.preventDefault();
33227         e.stopPropagation();
33228         return false;
33229     },
33230     
33231     onTouchEnd: function(e, el)
33232     {
33233 //        e.preventDefault();
33234         
33235         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33236         
33237             this.leave(e,el);
33238             
33239             return;
33240         }
33241         
33242         if(!this.bgimage.length || !this.html.length){
33243             
33244             if(this.href.length){
33245                 window.location.href = this.href;
33246             }
33247             
33248             return;
33249         }
33250         
33251         if(!this.isFitContainer){
33252             return;
33253         }
33254         
33255         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33256         
33257         window.location.href = this.href;
33258     },
33259     
33260     //selection on single brick only
33261     selectBrick : function() {
33262         
33263         if (!this.parentId) {
33264             return;
33265         }
33266         
33267         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33268         var index = m.selectedBrick.indexOf(this.id);
33269         
33270         if ( index > -1) {
33271             m.selectedBrick.splice(index,1);
33272             this.el.removeClass(this.activeClass);
33273             return;
33274         }
33275         
33276         for(var i = 0; i < m.selectedBrick.length; i++) {
33277             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33278             b.el.removeClass(b.activeClass);
33279         }
33280         
33281         m.selectedBrick = [];
33282         
33283         m.selectedBrick.push(this.id);
33284         this.el.addClass(this.activeClass);
33285         return;
33286     },
33287     
33288     isSelected : function(){
33289         return this.el.hasClass(this.activeClass);
33290         
33291     }
33292 });
33293
33294 Roo.apply(Roo.bootstrap.MasonryBrick, {
33295     
33296     //groups: {},
33297     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33298      /**
33299     * register a Masonry Brick
33300     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33301     */
33302     
33303     register : function(brick)
33304     {
33305         //this.groups[brick.id] = brick;
33306         this.groups.add(brick.id, brick);
33307     },
33308     /**
33309     * fetch a  masonry brick based on the masonry brick ID
33310     * @param {string} the masonry brick to add
33311     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33312     */
33313     
33314     get: function(brick_id) 
33315     {
33316         // if (typeof(this.groups[brick_id]) == 'undefined') {
33317         //     return false;
33318         // }
33319         // return this.groups[brick_id] ;
33320         
33321         if(this.groups.key(brick_id)) {
33322             return this.groups.key(brick_id);
33323         }
33324         
33325         return false;
33326     }
33327     
33328     
33329     
33330 });
33331
33332  /*
33333  * - LGPL
33334  *
33335  * element
33336  * 
33337  */
33338
33339 /**
33340  * @class Roo.bootstrap.Brick
33341  * @extends Roo.bootstrap.Component
33342  * Bootstrap Brick class
33343  * 
33344  * @constructor
33345  * Create a new Brick
33346  * @param {Object} config The config object
33347  */
33348
33349 Roo.bootstrap.Brick = function(config){
33350     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33351     
33352     this.addEvents({
33353         // raw events
33354         /**
33355          * @event click
33356          * When a Brick is click
33357          * @param {Roo.bootstrap.Brick} this
33358          * @param {Roo.EventObject} e
33359          */
33360         "click" : true
33361     });
33362 };
33363
33364 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33365     
33366     /**
33367      * @cfg {String} title
33368      */   
33369     title : '',
33370     /**
33371      * @cfg {String} html
33372      */   
33373     html : '',
33374     /**
33375      * @cfg {String} bgimage
33376      */   
33377     bgimage : '',
33378     /**
33379      * @cfg {String} cls
33380      */   
33381     cls : '',
33382     /**
33383      * @cfg {String} href
33384      */   
33385     href : '',
33386     /**
33387      * @cfg {String} video
33388      */   
33389     video : '',
33390     /**
33391      * @cfg {Boolean} square
33392      */   
33393     square : true,
33394     
33395     getAutoCreate : function()
33396     {
33397         var cls = 'roo-brick';
33398         
33399         if(this.href.length){
33400             cls += ' roo-brick-link';
33401         }
33402         
33403         if(this.bgimage.length){
33404             cls += ' roo-brick-image';
33405         }
33406         
33407         if(!this.html.length && !this.bgimage.length){
33408             cls += ' roo-brick-center-title';
33409         }
33410         
33411         if(!this.html.length && this.bgimage.length){
33412             cls += ' roo-brick-bottom-title';
33413         }
33414         
33415         if(this.cls){
33416             cls += ' ' + this.cls;
33417         }
33418         
33419         var cfg = {
33420             tag: (this.href.length) ? 'a' : 'div',
33421             cls: cls,
33422             cn: [
33423                 {
33424                     tag: 'div',
33425                     cls: 'roo-brick-paragraph',
33426                     cn: []
33427                 }
33428             ]
33429         };
33430         
33431         if(this.href.length){
33432             cfg.href = this.href;
33433         }
33434         
33435         var cn = cfg.cn[0].cn;
33436         
33437         if(this.title.length){
33438             cn.push({
33439                 tag: 'h4',
33440                 cls: 'roo-brick-title',
33441                 html: this.title
33442             });
33443         }
33444         
33445         if(this.html.length){
33446             cn.push({
33447                 tag: 'p',
33448                 cls: 'roo-brick-text',
33449                 html: this.html
33450             });
33451         } else {
33452             cn.cls += ' hide';
33453         }
33454         
33455         if(this.bgimage.length){
33456             cfg.cn.push({
33457                 tag: 'img',
33458                 cls: 'roo-brick-image-view',
33459                 src: this.bgimage
33460             });
33461         }
33462         
33463         return cfg;
33464     },
33465     
33466     initEvents: function() 
33467     {
33468         if(this.title.length || this.html.length){
33469             this.el.on('mouseenter'  ,this.enter, this);
33470             this.el.on('mouseleave', this.leave, this);
33471         }
33472         
33473         Roo.EventManager.onWindowResize(this.resize, this); 
33474         
33475         if(this.bgimage.length){
33476             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33477             this.imageEl.on('load', this.onImageLoad, this);
33478             return;
33479         }
33480         
33481         this.resize();
33482     },
33483     
33484     onImageLoad : function()
33485     {
33486         this.resize();
33487     },
33488     
33489     resize : function()
33490     {
33491         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33492         
33493         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33494         
33495         if(this.bgimage.length){
33496             var image = this.el.select('.roo-brick-image-view', true).first();
33497             
33498             image.setWidth(paragraph.getWidth());
33499             
33500             if(this.square){
33501                 image.setHeight(paragraph.getWidth());
33502             }
33503             
33504             this.el.setHeight(image.getHeight());
33505             paragraph.setHeight(image.getHeight());
33506             
33507         }
33508         
33509     },
33510     
33511     enter: function(e, el)
33512     {
33513         e.preventDefault();
33514         
33515         if(this.bgimage.length){
33516             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33517             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33518         }
33519     },
33520     
33521     leave: function(e, el)
33522     {
33523         e.preventDefault();
33524         
33525         if(this.bgimage.length){
33526             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33527             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33528         }
33529     }
33530     
33531 });
33532
33533  
33534
33535  /*
33536  * - LGPL
33537  *
33538  * Number field 
33539  */
33540
33541 /**
33542  * @class Roo.bootstrap.NumberField
33543  * @extends Roo.bootstrap.Input
33544  * Bootstrap NumberField class
33545  * 
33546  * 
33547  * 
33548  * 
33549  * @constructor
33550  * Create a new NumberField
33551  * @param {Object} config The config object
33552  */
33553
33554 Roo.bootstrap.NumberField = function(config){
33555     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33556 };
33557
33558 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33559     
33560     /**
33561      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33562      */
33563     allowDecimals : true,
33564     /**
33565      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33566      */
33567     decimalSeparator : ".",
33568     /**
33569      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33570      */
33571     decimalPrecision : 2,
33572     /**
33573      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33574      */
33575     allowNegative : true,
33576     
33577     /**
33578      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33579      */
33580     allowZero: true,
33581     /**
33582      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33583      */
33584     minValue : Number.NEGATIVE_INFINITY,
33585     /**
33586      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33587      */
33588     maxValue : Number.MAX_VALUE,
33589     /**
33590      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33591      */
33592     minText : "The minimum value for this field is {0}",
33593     /**
33594      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33595      */
33596     maxText : "The maximum value for this field is {0}",
33597     /**
33598      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33599      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33600      */
33601     nanText : "{0} is not a valid number",
33602     /**
33603      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33604      */
33605     thousandsDelimiter : false,
33606     /**
33607      * @cfg {String} valueAlign alignment of value
33608      */
33609     valueAlign : "left",
33610
33611     getAutoCreate : function()
33612     {
33613         var hiddenInput = {
33614             tag: 'input',
33615             type: 'hidden',
33616             id: Roo.id(),
33617             cls: 'hidden-number-input'
33618         };
33619         
33620         if (this.name) {
33621             hiddenInput.name = this.name;
33622         }
33623         
33624         this.name = '';
33625         
33626         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33627         
33628         this.name = hiddenInput.name;
33629         
33630         if(cfg.cn.length > 0) {
33631             cfg.cn.push(hiddenInput);
33632         }
33633         
33634         return cfg;
33635     },
33636
33637     // private
33638     initEvents : function()
33639     {   
33640         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33641         
33642         var allowed = "0123456789";
33643         
33644         if(this.allowDecimals){
33645             allowed += this.decimalSeparator;
33646         }
33647         
33648         if(this.allowNegative){
33649             allowed += "-";
33650         }
33651         
33652         if(this.thousandsDelimiter) {
33653             allowed += ",";
33654         }
33655         
33656         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33657         
33658         var keyPress = function(e){
33659             
33660             var k = e.getKey();
33661             
33662             var c = e.getCharCode();
33663             
33664             if(
33665                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33666                     allowed.indexOf(String.fromCharCode(c)) === -1
33667             ){
33668                 e.stopEvent();
33669                 return;
33670             }
33671             
33672             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33673                 return;
33674             }
33675             
33676             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33677                 e.stopEvent();
33678             }
33679         };
33680         
33681         this.el.on("keypress", keyPress, this);
33682     },
33683     
33684     validateValue : function(value)
33685     {
33686         
33687         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33688             return false;
33689         }
33690         
33691         var num = this.parseValue(value);
33692         
33693         if(isNaN(num)){
33694             this.markInvalid(String.format(this.nanText, value));
33695             return false;
33696         }
33697         
33698         if(num < this.minValue){
33699             this.markInvalid(String.format(this.minText, this.minValue));
33700             return false;
33701         }
33702         
33703         if(num > this.maxValue){
33704             this.markInvalid(String.format(this.maxText, this.maxValue));
33705             return false;
33706         }
33707         
33708         return true;
33709     },
33710
33711     getValue : function()
33712     {
33713         var v = this.hiddenEl().getValue();
33714         
33715         return this.fixPrecision(this.parseValue(v));
33716     },
33717
33718     parseValue : function(value)
33719     {
33720         if(this.thousandsDelimiter) {
33721             value += "";
33722             r = new RegExp(",", "g");
33723             value = value.replace(r, "");
33724         }
33725         
33726         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33727         return isNaN(value) ? '' : value;
33728     },
33729
33730     fixPrecision : function(value)
33731     {
33732         if(this.thousandsDelimiter) {
33733             value += "";
33734             r = new RegExp(",", "g");
33735             value = value.replace(r, "");
33736         }
33737         
33738         var nan = isNaN(value);
33739         
33740         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33741             return nan ? '' : value;
33742         }
33743         return parseFloat(value).toFixed(this.decimalPrecision);
33744     },
33745
33746     setValue : function(v)
33747     {
33748         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33749         
33750         this.value = v;
33751         
33752         if(this.rendered){
33753             
33754             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33755             
33756             this.inputEl().dom.value = (v == '') ? '' :
33757                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33758             
33759             if(!this.allowZero && v === '0') {
33760                 this.hiddenEl().dom.value = '';
33761                 this.inputEl().dom.value = '';
33762             }
33763             
33764             this.validate();
33765         }
33766     },
33767
33768     decimalPrecisionFcn : function(v)
33769     {
33770         return Math.floor(v);
33771     },
33772
33773     beforeBlur : function()
33774     {
33775         var v = this.parseValue(this.getRawValue());
33776         
33777         if(v || v === 0 || v === ''){
33778             this.setValue(v);
33779         }
33780     },
33781     
33782     hiddenEl : function()
33783     {
33784         return this.el.select('input.hidden-number-input',true).first();
33785     }
33786     
33787 });
33788
33789  
33790
33791 /*
33792 * Licence: LGPL
33793 */
33794
33795 /**
33796  * @class Roo.bootstrap.DocumentSlider
33797  * @extends Roo.bootstrap.Component
33798  * Bootstrap DocumentSlider class
33799  * 
33800  * @constructor
33801  * Create a new DocumentViewer
33802  * @param {Object} config The config object
33803  */
33804
33805 Roo.bootstrap.DocumentSlider = function(config){
33806     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33807     
33808     this.files = [];
33809     
33810     this.addEvents({
33811         /**
33812          * @event initial
33813          * Fire after initEvent
33814          * @param {Roo.bootstrap.DocumentSlider} this
33815          */
33816         "initial" : true,
33817         /**
33818          * @event update
33819          * Fire after update
33820          * @param {Roo.bootstrap.DocumentSlider} this
33821          */
33822         "update" : true,
33823         /**
33824          * @event click
33825          * Fire after click
33826          * @param {Roo.bootstrap.DocumentSlider} this
33827          */
33828         "click" : true
33829     });
33830 };
33831
33832 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33833     
33834     files : false,
33835     
33836     indicator : 0,
33837     
33838     getAutoCreate : function()
33839     {
33840         var cfg = {
33841             tag : 'div',
33842             cls : 'roo-document-slider',
33843             cn : [
33844                 {
33845                     tag : 'div',
33846                     cls : 'roo-document-slider-header',
33847                     cn : [
33848                         {
33849                             tag : 'div',
33850                             cls : 'roo-document-slider-header-title'
33851                         }
33852                     ]
33853                 },
33854                 {
33855                     tag : 'div',
33856                     cls : 'roo-document-slider-body',
33857                     cn : [
33858                         {
33859                             tag : 'div',
33860                             cls : 'roo-document-slider-prev',
33861                             cn : [
33862                                 {
33863                                     tag : 'i',
33864                                     cls : 'fa fa-chevron-left'
33865                                 }
33866                             ]
33867                         },
33868                         {
33869                             tag : 'div',
33870                             cls : 'roo-document-slider-thumb',
33871                             cn : [
33872                                 {
33873                                     tag : 'img',
33874                                     cls : 'roo-document-slider-image'
33875                                 }
33876                             ]
33877                         },
33878                         {
33879                             tag : 'div',
33880                             cls : 'roo-document-slider-next',
33881                             cn : [
33882                                 {
33883                                     tag : 'i',
33884                                     cls : 'fa fa-chevron-right'
33885                                 }
33886                             ]
33887                         }
33888                     ]
33889                 }
33890             ]
33891         };
33892         
33893         return cfg;
33894     },
33895     
33896     initEvents : function()
33897     {
33898         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33899         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33900         
33901         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33902         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33903         
33904         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33905         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33906         
33907         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33908         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33909         
33910         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33911         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33912         
33913         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33914         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33915         
33916         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33917         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33918         
33919         this.thumbEl.on('click', this.onClick, this);
33920         
33921         this.prevIndicator.on('click', this.prev, this);
33922         
33923         this.nextIndicator.on('click', this.next, this);
33924         
33925     },
33926     
33927     initial : function()
33928     {
33929         if(this.files.length){
33930             this.indicator = 1;
33931             this.update()
33932         }
33933         
33934         this.fireEvent('initial', this);
33935     },
33936     
33937     update : function()
33938     {
33939         this.imageEl.attr('src', this.files[this.indicator - 1]);
33940         
33941         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33942         
33943         this.prevIndicator.show();
33944         
33945         if(this.indicator == 1){
33946             this.prevIndicator.hide();
33947         }
33948         
33949         this.nextIndicator.show();
33950         
33951         if(this.indicator == this.files.length){
33952             this.nextIndicator.hide();
33953         }
33954         
33955         this.thumbEl.scrollTo('top');
33956         
33957         this.fireEvent('update', this);
33958     },
33959     
33960     onClick : function(e)
33961     {
33962         e.preventDefault();
33963         
33964         this.fireEvent('click', this);
33965     },
33966     
33967     prev : function(e)
33968     {
33969         e.preventDefault();
33970         
33971         this.indicator = Math.max(1, this.indicator - 1);
33972         
33973         this.update();
33974     },
33975     
33976     next : function(e)
33977     {
33978         e.preventDefault();
33979         
33980         this.indicator = Math.min(this.files.length, this.indicator + 1);
33981         
33982         this.update();
33983     }
33984 });
33985 /*
33986  * - LGPL
33987  *
33988  * RadioSet
33989  *
33990  *
33991  */
33992
33993 /**
33994  * @class Roo.bootstrap.RadioSet
33995  * @extends Roo.bootstrap.Input
33996  * Bootstrap RadioSet class
33997  * @cfg {String} indicatorpos (left|right) default left
33998  * @cfg {Boolean} inline (true|false) inline the element (default true)
33999  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34000  * @constructor
34001  * Create a new RadioSet
34002  * @param {Object} config The config object
34003  */
34004
34005 Roo.bootstrap.RadioSet = function(config){
34006     
34007     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34008     
34009     this.radioes = [];
34010     
34011     Roo.bootstrap.RadioSet.register(this);
34012     
34013     this.addEvents({
34014         /**
34015         * @event check
34016         * Fires when the element is checked or unchecked.
34017         * @param {Roo.bootstrap.RadioSet} this This radio
34018         * @param {Roo.bootstrap.Radio} item The checked item
34019         */
34020        check : true,
34021        /**
34022         * @event click
34023         * Fires when the element is click.
34024         * @param {Roo.bootstrap.RadioSet} this This radio set
34025         * @param {Roo.bootstrap.Radio} item The checked item
34026         * @param {Roo.EventObject} e The event object
34027         */
34028        click : true
34029     });
34030     
34031 };
34032
34033 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34034
34035     radioes : false,
34036     
34037     inline : true,
34038     
34039     weight : '',
34040     
34041     indicatorpos : 'left',
34042     
34043     getAutoCreate : function()
34044     {
34045         var label = {
34046             tag : 'label',
34047             cls : 'roo-radio-set-label',
34048             cn : [
34049                 {
34050                     tag : 'span',
34051                     html : this.fieldLabel
34052                 }
34053             ]
34054         };
34055         
34056         if(this.indicatorpos == 'left'){
34057             label.cn.unshift({
34058                 tag : 'i',
34059                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34060                 tooltip : 'This field is required'
34061             });
34062         } else {
34063             label.cn.push({
34064                 tag : 'i',
34065                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34066                 tooltip : 'This field is required'
34067             });
34068         }
34069         
34070         var items = {
34071             tag : 'div',
34072             cls : 'roo-radio-set-items'
34073         };
34074         
34075         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34076         
34077         if (align === 'left' && this.fieldLabel.length) {
34078             
34079             items = {
34080                 cls : "roo-radio-set-right", 
34081                 cn: [
34082                     items
34083                 ]
34084             };
34085             
34086             if(this.labelWidth > 12){
34087                 label.style = "width: " + this.labelWidth + 'px';
34088             }
34089             
34090             if(this.labelWidth < 13 && this.labelmd == 0){
34091                 this.labelmd = this.labelWidth;
34092             }
34093             
34094             if(this.labellg > 0){
34095                 label.cls += ' col-lg-' + this.labellg;
34096                 items.cls += ' col-lg-' + (12 - this.labellg);
34097             }
34098             
34099             if(this.labelmd > 0){
34100                 label.cls += ' col-md-' + this.labelmd;
34101                 items.cls += ' col-md-' + (12 - this.labelmd);
34102             }
34103             
34104             if(this.labelsm > 0){
34105                 label.cls += ' col-sm-' + this.labelsm;
34106                 items.cls += ' col-sm-' + (12 - this.labelsm);
34107             }
34108             
34109             if(this.labelxs > 0){
34110                 label.cls += ' col-xs-' + this.labelxs;
34111                 items.cls += ' col-xs-' + (12 - this.labelxs);
34112             }
34113         }
34114         
34115         var cfg = {
34116             tag : 'div',
34117             cls : 'roo-radio-set',
34118             cn : [
34119                 {
34120                     tag : 'input',
34121                     cls : 'roo-radio-set-input',
34122                     type : 'hidden',
34123                     name : this.name,
34124                     value : this.value ? this.value :  ''
34125                 },
34126                 label,
34127                 items
34128             ]
34129         };
34130         
34131         if(this.weight.length){
34132             cfg.cls += ' roo-radio-' + this.weight;
34133         }
34134         
34135         if(this.inline) {
34136             cfg.cls += ' roo-radio-set-inline';
34137         }
34138         
34139         var settings=this;
34140         ['xs','sm','md','lg'].map(function(size){
34141             if (settings[size]) {
34142                 cfg.cls += ' col-' + size + '-' + settings[size];
34143             }
34144         });
34145         
34146         return cfg;
34147         
34148     },
34149
34150     initEvents : function()
34151     {
34152         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34153         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34154         
34155         if(!this.fieldLabel.length){
34156             this.labelEl.hide();
34157         }
34158         
34159         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34160         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34161         
34162         this.indicator = this.indicatorEl();
34163         
34164         if(this.indicator){
34165             this.indicator.addClass('invisible');
34166         }
34167         
34168         this.originalValue = this.getValue();
34169         
34170     },
34171     
34172     inputEl: function ()
34173     {
34174         return this.el.select('.roo-radio-set-input', true).first();
34175     },
34176     
34177     getChildContainer : function()
34178     {
34179         return this.itemsEl;
34180     },
34181     
34182     register : function(item)
34183     {
34184         this.radioes.push(item);
34185         
34186     },
34187     
34188     validate : function()
34189     {   
34190         if(this.getVisibilityEl().hasClass('hidden')){
34191             return true;
34192         }
34193         
34194         var valid = false;
34195         
34196         Roo.each(this.radioes, function(i){
34197             if(!i.checked){
34198                 return;
34199             }
34200             
34201             valid = true;
34202             return false;
34203         });
34204         
34205         if(this.allowBlank) {
34206             return true;
34207         }
34208         
34209         if(this.disabled || valid){
34210             this.markValid();
34211             return true;
34212         }
34213         
34214         this.markInvalid();
34215         return false;
34216         
34217     },
34218     
34219     markValid : function()
34220     {
34221         if(this.labelEl.isVisible(true)){
34222             this.indicatorEl().removeClass('visible');
34223             this.indicatorEl().addClass('invisible');
34224         }
34225         
34226         this.el.removeClass([this.invalidClass, this.validClass]);
34227         this.el.addClass(this.validClass);
34228         
34229         this.fireEvent('valid', this);
34230     },
34231     
34232     markInvalid : function(msg)
34233     {
34234         if(this.allowBlank || this.disabled){
34235             return;
34236         }
34237         
34238         if(this.labelEl.isVisible(true)){
34239             this.indicatorEl().removeClass('invisible');
34240             this.indicatorEl().addClass('visible');
34241         }
34242         
34243         this.el.removeClass([this.invalidClass, this.validClass]);
34244         this.el.addClass(this.invalidClass);
34245         
34246         this.fireEvent('invalid', this, msg);
34247         
34248     },
34249     
34250     setValue : function(v, suppressEvent)
34251     {   
34252         if(this.value === v){
34253             return;
34254         }
34255         
34256         this.value = v;
34257         
34258         if(this.rendered){
34259             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34260         }
34261         
34262         Roo.each(this.radioes, function(i){
34263             i.checked = false;
34264             i.el.removeClass('checked');
34265         });
34266         
34267         Roo.each(this.radioes, function(i){
34268             
34269             if(i.value === v || i.value.toString() === v.toString()){
34270                 i.checked = true;
34271                 i.el.addClass('checked');
34272                 
34273                 if(suppressEvent !== true){
34274                     this.fireEvent('check', this, i);
34275                 }
34276                 
34277                 return false;
34278             }
34279             
34280         }, this);
34281         
34282         this.validate();
34283     },
34284     
34285     clearInvalid : function(){
34286         
34287         if(!this.el || this.preventMark){
34288             return;
34289         }
34290         
34291         this.el.removeClass([this.invalidClass]);
34292         
34293         this.fireEvent('valid', this);
34294     }
34295     
34296 });
34297
34298 Roo.apply(Roo.bootstrap.RadioSet, {
34299     
34300     groups: {},
34301     
34302     register : function(set)
34303     {
34304         this.groups[set.name] = set;
34305     },
34306     
34307     get: function(name) 
34308     {
34309         if (typeof(this.groups[name]) == 'undefined') {
34310             return false;
34311         }
34312         
34313         return this.groups[name] ;
34314     }
34315     
34316 });
34317 /*
34318  * Based on:
34319  * Ext JS Library 1.1.1
34320  * Copyright(c) 2006-2007, Ext JS, LLC.
34321  *
34322  * Originally Released Under LGPL - original licence link has changed is not relivant.
34323  *
34324  * Fork - LGPL
34325  * <script type="text/javascript">
34326  */
34327
34328
34329 /**
34330  * @class Roo.bootstrap.SplitBar
34331  * @extends Roo.util.Observable
34332  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34333  * <br><br>
34334  * Usage:
34335  * <pre><code>
34336 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34337                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34338 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34339 split.minSize = 100;
34340 split.maxSize = 600;
34341 split.animate = true;
34342 split.on('moved', splitterMoved);
34343 </code></pre>
34344  * @constructor
34345  * Create a new SplitBar
34346  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34347  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34348  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34349  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34350                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34351                         position of the SplitBar).
34352  */
34353 Roo.bootstrap.SplitBar = function(cfg){
34354     
34355     /** @private */
34356     
34357     //{
34358     //  dragElement : elm
34359     //  resizingElement: el,
34360         // optional..
34361     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34362     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34363         // existingProxy ???
34364     //}
34365     
34366     this.el = Roo.get(cfg.dragElement, true);
34367     this.el.dom.unselectable = "on";
34368     /** @private */
34369     this.resizingEl = Roo.get(cfg.resizingElement, true);
34370
34371     /**
34372      * @private
34373      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34374      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34375      * @type Number
34376      */
34377     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34378     
34379     /**
34380      * The minimum size of the resizing element. (Defaults to 0)
34381      * @type Number
34382      */
34383     this.minSize = 0;
34384     
34385     /**
34386      * The maximum size of the resizing element. (Defaults to 2000)
34387      * @type Number
34388      */
34389     this.maxSize = 2000;
34390     
34391     /**
34392      * Whether to animate the transition to the new size
34393      * @type Boolean
34394      */
34395     this.animate = false;
34396     
34397     /**
34398      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34399      * @type Boolean
34400      */
34401     this.useShim = false;
34402     
34403     /** @private */
34404     this.shim = null;
34405     
34406     if(!cfg.existingProxy){
34407         /** @private */
34408         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34409     }else{
34410         this.proxy = Roo.get(cfg.existingProxy).dom;
34411     }
34412     /** @private */
34413     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34414     
34415     /** @private */
34416     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34417     
34418     /** @private */
34419     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34420     
34421     /** @private */
34422     this.dragSpecs = {};
34423     
34424     /**
34425      * @private The adapter to use to positon and resize elements
34426      */
34427     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34428     this.adapter.init(this);
34429     
34430     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34431         /** @private */
34432         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34433         this.el.addClass("roo-splitbar-h");
34434     }else{
34435         /** @private */
34436         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34437         this.el.addClass("roo-splitbar-v");
34438     }
34439     
34440     this.addEvents({
34441         /**
34442          * @event resize
34443          * Fires when the splitter is moved (alias for {@link #event-moved})
34444          * @param {Roo.bootstrap.SplitBar} this
34445          * @param {Number} newSize the new width or height
34446          */
34447         "resize" : true,
34448         /**
34449          * @event moved
34450          * Fires when the splitter is moved
34451          * @param {Roo.bootstrap.SplitBar} this
34452          * @param {Number} newSize the new width or height
34453          */
34454         "moved" : true,
34455         /**
34456          * @event beforeresize
34457          * Fires before the splitter is dragged
34458          * @param {Roo.bootstrap.SplitBar} this
34459          */
34460         "beforeresize" : true,
34461
34462         "beforeapply" : true
34463     });
34464
34465     Roo.util.Observable.call(this);
34466 };
34467
34468 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34469     onStartProxyDrag : function(x, y){
34470         this.fireEvent("beforeresize", this);
34471         if(!this.overlay){
34472             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34473             o.unselectable();
34474             o.enableDisplayMode("block");
34475             // all splitbars share the same overlay
34476             Roo.bootstrap.SplitBar.prototype.overlay = o;
34477         }
34478         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34479         this.overlay.show();
34480         Roo.get(this.proxy).setDisplayed("block");
34481         var size = this.adapter.getElementSize(this);
34482         this.activeMinSize = this.getMinimumSize();;
34483         this.activeMaxSize = this.getMaximumSize();;
34484         var c1 = size - this.activeMinSize;
34485         var c2 = Math.max(this.activeMaxSize - size, 0);
34486         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34487             this.dd.resetConstraints();
34488             this.dd.setXConstraint(
34489                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34490                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34491             );
34492             this.dd.setYConstraint(0, 0);
34493         }else{
34494             this.dd.resetConstraints();
34495             this.dd.setXConstraint(0, 0);
34496             this.dd.setYConstraint(
34497                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34498                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34499             );
34500          }
34501         this.dragSpecs.startSize = size;
34502         this.dragSpecs.startPoint = [x, y];
34503         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34504     },
34505     
34506     /** 
34507      * @private Called after the drag operation by the DDProxy
34508      */
34509     onEndProxyDrag : function(e){
34510         Roo.get(this.proxy).setDisplayed(false);
34511         var endPoint = Roo.lib.Event.getXY(e);
34512         if(this.overlay){
34513             this.overlay.hide();
34514         }
34515         var newSize;
34516         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34517             newSize = this.dragSpecs.startSize + 
34518                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34519                     endPoint[0] - this.dragSpecs.startPoint[0] :
34520                     this.dragSpecs.startPoint[0] - endPoint[0]
34521                 );
34522         }else{
34523             newSize = this.dragSpecs.startSize + 
34524                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34525                     endPoint[1] - this.dragSpecs.startPoint[1] :
34526                     this.dragSpecs.startPoint[1] - endPoint[1]
34527                 );
34528         }
34529         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34530         if(newSize != this.dragSpecs.startSize){
34531             if(this.fireEvent('beforeapply', this, newSize) !== false){
34532                 this.adapter.setElementSize(this, newSize);
34533                 this.fireEvent("moved", this, newSize);
34534                 this.fireEvent("resize", this, newSize);
34535             }
34536         }
34537     },
34538     
34539     /**
34540      * Get the adapter this SplitBar uses
34541      * @return The adapter object
34542      */
34543     getAdapter : function(){
34544         return this.adapter;
34545     },
34546     
34547     /**
34548      * Set the adapter this SplitBar uses
34549      * @param {Object} adapter A SplitBar adapter object
34550      */
34551     setAdapter : function(adapter){
34552         this.adapter = adapter;
34553         this.adapter.init(this);
34554     },
34555     
34556     /**
34557      * Gets the minimum size for the resizing element
34558      * @return {Number} The minimum size
34559      */
34560     getMinimumSize : function(){
34561         return this.minSize;
34562     },
34563     
34564     /**
34565      * Sets the minimum size for the resizing element
34566      * @param {Number} minSize The minimum size
34567      */
34568     setMinimumSize : function(minSize){
34569         this.minSize = minSize;
34570     },
34571     
34572     /**
34573      * Gets the maximum size for the resizing element
34574      * @return {Number} The maximum size
34575      */
34576     getMaximumSize : function(){
34577         return this.maxSize;
34578     },
34579     
34580     /**
34581      * Sets the maximum size for the resizing element
34582      * @param {Number} maxSize The maximum size
34583      */
34584     setMaximumSize : function(maxSize){
34585         this.maxSize = maxSize;
34586     },
34587     
34588     /**
34589      * Sets the initialize size for the resizing element
34590      * @param {Number} size The initial size
34591      */
34592     setCurrentSize : function(size){
34593         var oldAnimate = this.animate;
34594         this.animate = false;
34595         this.adapter.setElementSize(this, size);
34596         this.animate = oldAnimate;
34597     },
34598     
34599     /**
34600      * Destroy this splitbar. 
34601      * @param {Boolean} removeEl True to remove the element
34602      */
34603     destroy : function(removeEl){
34604         if(this.shim){
34605             this.shim.remove();
34606         }
34607         this.dd.unreg();
34608         this.proxy.parentNode.removeChild(this.proxy);
34609         if(removeEl){
34610             this.el.remove();
34611         }
34612     }
34613 });
34614
34615 /**
34616  * @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.
34617  */
34618 Roo.bootstrap.SplitBar.createProxy = function(dir){
34619     var proxy = new Roo.Element(document.createElement("div"));
34620     proxy.unselectable();
34621     var cls = 'roo-splitbar-proxy';
34622     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34623     document.body.appendChild(proxy.dom);
34624     return proxy.dom;
34625 };
34626
34627 /** 
34628  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34629  * Default Adapter. It assumes the splitter and resizing element are not positioned
34630  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34631  */
34632 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34633 };
34634
34635 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34636     // do nothing for now
34637     init : function(s){
34638     
34639     },
34640     /**
34641      * Called before drag operations to get the current size of the resizing element. 
34642      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34643      */
34644      getElementSize : function(s){
34645         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34646             return s.resizingEl.getWidth();
34647         }else{
34648             return s.resizingEl.getHeight();
34649         }
34650     },
34651     
34652     /**
34653      * Called after drag operations to set the size of the resizing element.
34654      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34655      * @param {Number} newSize The new size to set
34656      * @param {Function} onComplete A function to be invoked when resizing is complete
34657      */
34658     setElementSize : function(s, newSize, onComplete){
34659         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34660             if(!s.animate){
34661                 s.resizingEl.setWidth(newSize);
34662                 if(onComplete){
34663                     onComplete(s, newSize);
34664                 }
34665             }else{
34666                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34667             }
34668         }else{
34669             
34670             if(!s.animate){
34671                 s.resizingEl.setHeight(newSize);
34672                 if(onComplete){
34673                     onComplete(s, newSize);
34674                 }
34675             }else{
34676                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34677             }
34678         }
34679     }
34680 };
34681
34682 /** 
34683  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34684  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34685  * Adapter that  moves the splitter element to align with the resized sizing element. 
34686  * Used with an absolute positioned SplitBar.
34687  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34688  * document.body, make sure you assign an id to the body element.
34689  */
34690 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34691     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34692     this.container = Roo.get(container);
34693 };
34694
34695 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34696     init : function(s){
34697         this.basic.init(s);
34698     },
34699     
34700     getElementSize : function(s){
34701         return this.basic.getElementSize(s);
34702     },
34703     
34704     setElementSize : function(s, newSize, onComplete){
34705         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34706     },
34707     
34708     moveSplitter : function(s){
34709         var yes = Roo.bootstrap.SplitBar;
34710         switch(s.placement){
34711             case yes.LEFT:
34712                 s.el.setX(s.resizingEl.getRight());
34713                 break;
34714             case yes.RIGHT:
34715                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34716                 break;
34717             case yes.TOP:
34718                 s.el.setY(s.resizingEl.getBottom());
34719                 break;
34720             case yes.BOTTOM:
34721                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34722                 break;
34723         }
34724     }
34725 };
34726
34727 /**
34728  * Orientation constant - Create a vertical SplitBar
34729  * @static
34730  * @type Number
34731  */
34732 Roo.bootstrap.SplitBar.VERTICAL = 1;
34733
34734 /**
34735  * Orientation constant - Create a horizontal SplitBar
34736  * @static
34737  * @type Number
34738  */
34739 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34740
34741 /**
34742  * Placement constant - The resizing element is to the left of the splitter element
34743  * @static
34744  * @type Number
34745  */
34746 Roo.bootstrap.SplitBar.LEFT = 1;
34747
34748 /**
34749  * Placement constant - The resizing element is to the right of the splitter element
34750  * @static
34751  * @type Number
34752  */
34753 Roo.bootstrap.SplitBar.RIGHT = 2;
34754
34755 /**
34756  * Placement constant - The resizing element is positioned above the splitter element
34757  * @static
34758  * @type Number
34759  */
34760 Roo.bootstrap.SplitBar.TOP = 3;
34761
34762 /**
34763  * Placement constant - The resizing element is positioned under splitter element
34764  * @static
34765  * @type Number
34766  */
34767 Roo.bootstrap.SplitBar.BOTTOM = 4;
34768 Roo.namespace("Roo.bootstrap.layout");/*
34769  * Based on:
34770  * Ext JS Library 1.1.1
34771  * Copyright(c) 2006-2007, Ext JS, LLC.
34772  *
34773  * Originally Released Under LGPL - original licence link has changed is not relivant.
34774  *
34775  * Fork - LGPL
34776  * <script type="text/javascript">
34777  */
34778
34779 /**
34780  * @class Roo.bootstrap.layout.Manager
34781  * @extends Roo.bootstrap.Component
34782  * Base class for layout managers.
34783  */
34784 Roo.bootstrap.layout.Manager = function(config)
34785 {
34786     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34787
34788
34789
34790
34791
34792     /** false to disable window resize monitoring @type Boolean */
34793     this.monitorWindowResize = true;
34794     this.regions = {};
34795     this.addEvents({
34796         /**
34797          * @event layout
34798          * Fires when a layout is performed.
34799          * @param {Roo.LayoutManager} this
34800          */
34801         "layout" : true,
34802         /**
34803          * @event regionresized
34804          * Fires when the user resizes a region.
34805          * @param {Roo.LayoutRegion} region The resized region
34806          * @param {Number} newSize The new size (width for east/west, height for north/south)
34807          */
34808         "regionresized" : true,
34809         /**
34810          * @event regioncollapsed
34811          * Fires when a region is collapsed.
34812          * @param {Roo.LayoutRegion} region The collapsed region
34813          */
34814         "regioncollapsed" : true,
34815         /**
34816          * @event regionexpanded
34817          * Fires when a region is expanded.
34818          * @param {Roo.LayoutRegion} region The expanded region
34819          */
34820         "regionexpanded" : true
34821     });
34822     this.updating = false;
34823
34824     if (config.el) {
34825         this.el = Roo.get(config.el);
34826         this.initEvents();
34827     }
34828
34829 };
34830
34831 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34832
34833
34834     regions : null,
34835
34836     monitorWindowResize : true,
34837
34838
34839     updating : false,
34840
34841
34842     onRender : function(ct, position)
34843     {
34844         if(!this.el){
34845             this.el = Roo.get(ct);
34846             this.initEvents();
34847         }
34848         //this.fireEvent('render',this);
34849     },
34850
34851
34852     initEvents: function()
34853     {
34854
34855
34856         // ie scrollbar fix
34857         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34858             document.body.scroll = "no";
34859         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34860             this.el.position('relative');
34861         }
34862         this.id = this.el.id;
34863         this.el.addClass("roo-layout-container");
34864         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34865         if(this.el.dom != document.body ) {
34866             this.el.on('resize', this.layout,this);
34867             this.el.on('show', this.layout,this);
34868         }
34869
34870     },
34871
34872     /**
34873      * Returns true if this layout is currently being updated
34874      * @return {Boolean}
34875      */
34876     isUpdating : function(){
34877         return this.updating;
34878     },
34879
34880     /**
34881      * Suspend the LayoutManager from doing auto-layouts while
34882      * making multiple add or remove calls
34883      */
34884     beginUpdate : function(){
34885         this.updating = true;
34886     },
34887
34888     /**
34889      * Restore auto-layouts and optionally disable the manager from performing a layout
34890      * @param {Boolean} noLayout true to disable a layout update
34891      */
34892     endUpdate : function(noLayout){
34893         this.updating = false;
34894         if(!noLayout){
34895             this.layout();
34896         }
34897     },
34898
34899     layout: function(){
34900         // abstract...
34901     },
34902
34903     onRegionResized : function(region, newSize){
34904         this.fireEvent("regionresized", region, newSize);
34905         this.layout();
34906     },
34907
34908     onRegionCollapsed : function(region){
34909         this.fireEvent("regioncollapsed", region);
34910     },
34911
34912     onRegionExpanded : function(region){
34913         this.fireEvent("regionexpanded", region);
34914     },
34915
34916     /**
34917      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34918      * performs box-model adjustments.
34919      * @return {Object} The size as an object {width: (the width), height: (the height)}
34920      */
34921     getViewSize : function()
34922     {
34923         var size;
34924         if(this.el.dom != document.body){
34925             size = this.el.getSize();
34926         }else{
34927             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34928         }
34929         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34930         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34931         return size;
34932     },
34933
34934     /**
34935      * Returns the Element this layout is bound to.
34936      * @return {Roo.Element}
34937      */
34938     getEl : function(){
34939         return this.el;
34940     },
34941
34942     /**
34943      * Returns the specified region.
34944      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34945      * @return {Roo.LayoutRegion}
34946      */
34947     getRegion : function(target){
34948         return this.regions[target.toLowerCase()];
34949     },
34950
34951     onWindowResize : function(){
34952         if(this.monitorWindowResize){
34953             this.layout();
34954         }
34955     }
34956 });
34957 /*
34958  * Based on:
34959  * Ext JS Library 1.1.1
34960  * Copyright(c) 2006-2007, Ext JS, LLC.
34961  *
34962  * Originally Released Under LGPL - original licence link has changed is not relivant.
34963  *
34964  * Fork - LGPL
34965  * <script type="text/javascript">
34966  */
34967 /**
34968  * @class Roo.bootstrap.layout.Border
34969  * @extends Roo.bootstrap.layout.Manager
34970  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34971  * please see: examples/bootstrap/nested.html<br><br>
34972  
34973 <b>The container the layout is rendered into can be either the body element or any other element.
34974 If it is not the body element, the container needs to either be an absolute positioned element,
34975 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34976 the container size if it is not the body element.</b>
34977
34978 * @constructor
34979 * Create a new Border
34980 * @param {Object} config Configuration options
34981  */
34982 Roo.bootstrap.layout.Border = function(config){
34983     config = config || {};
34984     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34985     
34986     
34987     
34988     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34989         if(config[region]){
34990             config[region].region = region;
34991             this.addRegion(config[region]);
34992         }
34993     },this);
34994     
34995 };
34996
34997 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34998
34999 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35000     /**
35001      * Creates and adds a new region if it doesn't already exist.
35002      * @param {String} target The target region key (north, south, east, west or center).
35003      * @param {Object} config The regions config object
35004      * @return {BorderLayoutRegion} The new region
35005      */
35006     addRegion : function(config)
35007     {
35008         if(!this.regions[config.region]){
35009             var r = this.factory(config);
35010             this.bindRegion(r);
35011         }
35012         return this.regions[config.region];
35013     },
35014
35015     // private (kinda)
35016     bindRegion : function(r){
35017         this.regions[r.config.region] = r;
35018         
35019         r.on("visibilitychange",    this.layout, this);
35020         r.on("paneladded",          this.layout, this);
35021         r.on("panelremoved",        this.layout, this);
35022         r.on("invalidated",         this.layout, this);
35023         r.on("resized",             this.onRegionResized, this);
35024         r.on("collapsed",           this.onRegionCollapsed, this);
35025         r.on("expanded",            this.onRegionExpanded, this);
35026     },
35027
35028     /**
35029      * Performs a layout update.
35030      */
35031     layout : function()
35032     {
35033         if(this.updating) {
35034             return;
35035         }
35036         
35037         // render all the rebions if they have not been done alreayd?
35038         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35039             if(this.regions[region] && !this.regions[region].bodyEl){
35040                 this.regions[region].onRender(this.el)
35041             }
35042         },this);
35043         
35044         var size = this.getViewSize();
35045         var w = size.width;
35046         var h = size.height;
35047         var centerW = w;
35048         var centerH = h;
35049         var centerY = 0;
35050         var centerX = 0;
35051         //var x = 0, y = 0;
35052
35053         var rs = this.regions;
35054         var north = rs["north"];
35055         var south = rs["south"]; 
35056         var west = rs["west"];
35057         var east = rs["east"];
35058         var center = rs["center"];
35059         //if(this.hideOnLayout){ // not supported anymore
35060             //c.el.setStyle("display", "none");
35061         //}
35062         if(north && north.isVisible()){
35063             var b = north.getBox();
35064             var m = north.getMargins();
35065             b.width = w - (m.left+m.right);
35066             b.x = m.left;
35067             b.y = m.top;
35068             centerY = b.height + b.y + m.bottom;
35069             centerH -= centerY;
35070             north.updateBox(this.safeBox(b));
35071         }
35072         if(south && south.isVisible()){
35073             var b = south.getBox();
35074             var m = south.getMargins();
35075             b.width = w - (m.left+m.right);
35076             b.x = m.left;
35077             var totalHeight = (b.height + m.top + m.bottom);
35078             b.y = h - totalHeight + m.top;
35079             centerH -= totalHeight;
35080             south.updateBox(this.safeBox(b));
35081         }
35082         if(west && west.isVisible()){
35083             var b = west.getBox();
35084             var m = west.getMargins();
35085             b.height = centerH - (m.top+m.bottom);
35086             b.x = m.left;
35087             b.y = centerY + m.top;
35088             var totalWidth = (b.width + m.left + m.right);
35089             centerX += totalWidth;
35090             centerW -= totalWidth;
35091             west.updateBox(this.safeBox(b));
35092         }
35093         if(east && east.isVisible()){
35094             var b = east.getBox();
35095             var m = east.getMargins();
35096             b.height = centerH - (m.top+m.bottom);
35097             var totalWidth = (b.width + m.left + m.right);
35098             b.x = w - totalWidth + m.left;
35099             b.y = centerY + m.top;
35100             centerW -= totalWidth;
35101             east.updateBox(this.safeBox(b));
35102         }
35103         if(center){
35104             var m = center.getMargins();
35105             var centerBox = {
35106                 x: centerX + m.left,
35107                 y: centerY + m.top,
35108                 width: centerW - (m.left+m.right),
35109                 height: centerH - (m.top+m.bottom)
35110             };
35111             //if(this.hideOnLayout){
35112                 //center.el.setStyle("display", "block");
35113             //}
35114             center.updateBox(this.safeBox(centerBox));
35115         }
35116         this.el.repaint();
35117         this.fireEvent("layout", this);
35118     },
35119
35120     // private
35121     safeBox : function(box){
35122         box.width = Math.max(0, box.width);
35123         box.height = Math.max(0, box.height);
35124         return box;
35125     },
35126
35127     /**
35128      * Adds a ContentPanel (or subclass) to this layout.
35129      * @param {String} target The target region key (north, south, east, west or center).
35130      * @param {Roo.ContentPanel} panel The panel to add
35131      * @return {Roo.ContentPanel} The added panel
35132      */
35133     add : function(target, panel){
35134          
35135         target = target.toLowerCase();
35136         return this.regions[target].add(panel);
35137     },
35138
35139     /**
35140      * Remove a ContentPanel (or subclass) to this layout.
35141      * @param {String} target The target region key (north, south, east, west or center).
35142      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35143      * @return {Roo.ContentPanel} The removed panel
35144      */
35145     remove : function(target, panel){
35146         target = target.toLowerCase();
35147         return this.regions[target].remove(panel);
35148     },
35149
35150     /**
35151      * Searches all regions for a panel with the specified id
35152      * @param {String} panelId
35153      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35154      */
35155     findPanel : function(panelId){
35156         var rs = this.regions;
35157         for(var target in rs){
35158             if(typeof rs[target] != "function"){
35159                 var p = rs[target].getPanel(panelId);
35160                 if(p){
35161                     return p;
35162                 }
35163             }
35164         }
35165         return null;
35166     },
35167
35168     /**
35169      * Searches all regions for a panel with the specified id and activates (shows) it.
35170      * @param {String/ContentPanel} panelId The panels id or the panel itself
35171      * @return {Roo.ContentPanel} The shown panel or null
35172      */
35173     showPanel : function(panelId) {
35174       var rs = this.regions;
35175       for(var target in rs){
35176          var r = rs[target];
35177          if(typeof r != "function"){
35178             if(r.hasPanel(panelId)){
35179                return r.showPanel(panelId);
35180             }
35181          }
35182       }
35183       return null;
35184    },
35185
35186    /**
35187      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35188      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35189      */
35190    /*
35191     restoreState : function(provider){
35192         if(!provider){
35193             provider = Roo.state.Manager;
35194         }
35195         var sm = new Roo.LayoutStateManager();
35196         sm.init(this, provider);
35197     },
35198 */
35199  
35200  
35201     /**
35202      * Adds a xtype elements to the layout.
35203      * <pre><code>
35204
35205 layout.addxtype({
35206        xtype : 'ContentPanel',
35207        region: 'west',
35208        items: [ .... ]
35209    }
35210 );
35211
35212 layout.addxtype({
35213         xtype : 'NestedLayoutPanel',
35214         region: 'west',
35215         layout: {
35216            center: { },
35217            west: { }   
35218         },
35219         items : [ ... list of content panels or nested layout panels.. ]
35220    }
35221 );
35222 </code></pre>
35223      * @param {Object} cfg Xtype definition of item to add.
35224      */
35225     addxtype : function(cfg)
35226     {
35227         // basically accepts a pannel...
35228         // can accept a layout region..!?!?
35229         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35230         
35231         
35232         // theory?  children can only be panels??
35233         
35234         //if (!cfg.xtype.match(/Panel$/)) {
35235         //    return false;
35236         //}
35237         var ret = false;
35238         
35239         if (typeof(cfg.region) == 'undefined') {
35240             Roo.log("Failed to add Panel, region was not set");
35241             Roo.log(cfg);
35242             return false;
35243         }
35244         var region = cfg.region;
35245         delete cfg.region;
35246         
35247           
35248         var xitems = [];
35249         if (cfg.items) {
35250             xitems = cfg.items;
35251             delete cfg.items;
35252         }
35253         var nb = false;
35254         
35255         switch(cfg.xtype) 
35256         {
35257             case 'Content':  // ContentPanel (el, cfg)
35258             case 'Scroll':  // ContentPanel (el, cfg)
35259             case 'View': 
35260                 cfg.autoCreate = true;
35261                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35262                 //} else {
35263                 //    var el = this.el.createChild();
35264                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35265                 //}
35266                 
35267                 this.add(region, ret);
35268                 break;
35269             
35270             /*
35271             case 'TreePanel': // our new panel!
35272                 cfg.el = this.el.createChild();
35273                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35274                 this.add(region, ret);
35275                 break;
35276             */
35277             
35278             case 'Nest': 
35279                 // create a new Layout (which is  a Border Layout...
35280                 
35281                 var clayout = cfg.layout;
35282                 clayout.el  = this.el.createChild();
35283                 clayout.items   = clayout.items  || [];
35284                 
35285                 delete cfg.layout;
35286                 
35287                 // replace this exitems with the clayout ones..
35288                 xitems = clayout.items;
35289                  
35290                 // force background off if it's in center...
35291                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35292                     cfg.background = false;
35293                 }
35294                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35295                 
35296                 
35297                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35298                 //console.log('adding nested layout panel '  + cfg.toSource());
35299                 this.add(region, ret);
35300                 nb = {}; /// find first...
35301                 break;
35302             
35303             case 'Grid':
35304                 
35305                 // needs grid and region
35306                 
35307                 //var el = this.getRegion(region).el.createChild();
35308                 /*
35309                  *var el = this.el.createChild();
35310                 // create the grid first...
35311                 cfg.grid.container = el;
35312                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35313                 */
35314                 
35315                 if (region == 'center' && this.active ) {
35316                     cfg.background = false;
35317                 }
35318                 
35319                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35320                 
35321                 this.add(region, ret);
35322                 /*
35323                 if (cfg.background) {
35324                     // render grid on panel activation (if panel background)
35325                     ret.on('activate', function(gp) {
35326                         if (!gp.grid.rendered) {
35327                     //        gp.grid.render(el);
35328                         }
35329                     });
35330                 } else {
35331                   //  cfg.grid.render(el);
35332                 }
35333                 */
35334                 break;
35335            
35336            
35337             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35338                 // it was the old xcomponent building that caused this before.
35339                 // espeically if border is the top element in the tree.
35340                 ret = this;
35341                 break; 
35342                 
35343                     
35344                 
35345                 
35346                 
35347             default:
35348                 /*
35349                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35350                     
35351                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35352                     this.add(region, ret);
35353                 } else {
35354                 */
35355                     Roo.log(cfg);
35356                     throw "Can not add '" + cfg.xtype + "' to Border";
35357                     return null;
35358              
35359                                 
35360              
35361         }
35362         this.beginUpdate();
35363         // add children..
35364         var region = '';
35365         var abn = {};
35366         Roo.each(xitems, function(i)  {
35367             region = nb && i.region ? i.region : false;
35368             
35369             var add = ret.addxtype(i);
35370            
35371             if (region) {
35372                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35373                 if (!i.background) {
35374                     abn[region] = nb[region] ;
35375                 }
35376             }
35377             
35378         });
35379         this.endUpdate();
35380
35381         // make the last non-background panel active..
35382         //if (nb) { Roo.log(abn); }
35383         if (nb) {
35384             
35385             for(var r in abn) {
35386                 region = this.getRegion(r);
35387                 if (region) {
35388                     // tried using nb[r], but it does not work..
35389                      
35390                     region.showPanel(abn[r]);
35391                    
35392                 }
35393             }
35394         }
35395         return ret;
35396         
35397     },
35398     
35399     
35400 // private
35401     factory : function(cfg)
35402     {
35403         
35404         var validRegions = Roo.bootstrap.layout.Border.regions;
35405
35406         var target = cfg.region;
35407         cfg.mgr = this;
35408         
35409         var r = Roo.bootstrap.layout;
35410         Roo.log(target);
35411         switch(target){
35412             case "north":
35413                 return new r.North(cfg);
35414             case "south":
35415                 return new r.South(cfg);
35416             case "east":
35417                 return new r.East(cfg);
35418             case "west":
35419                 return new r.West(cfg);
35420             case "center":
35421                 return new r.Center(cfg);
35422         }
35423         throw 'Layout region "'+target+'" not supported.';
35424     }
35425     
35426     
35427 });
35428  /*
35429  * Based on:
35430  * Ext JS Library 1.1.1
35431  * Copyright(c) 2006-2007, Ext JS, LLC.
35432  *
35433  * Originally Released Under LGPL - original licence link has changed is not relivant.
35434  *
35435  * Fork - LGPL
35436  * <script type="text/javascript">
35437  */
35438  
35439 /**
35440  * @class Roo.bootstrap.layout.Basic
35441  * @extends Roo.util.Observable
35442  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35443  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35444  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35445  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35446  * @cfg {string}   region  the region that it inhabits..
35447  * @cfg {bool}   skipConfig skip config?
35448  * 
35449
35450  */
35451 Roo.bootstrap.layout.Basic = function(config){
35452     
35453     this.mgr = config.mgr;
35454     
35455     this.position = config.region;
35456     
35457     var skipConfig = config.skipConfig;
35458     
35459     this.events = {
35460         /**
35461          * @scope Roo.BasicLayoutRegion
35462          */
35463         
35464         /**
35465          * @event beforeremove
35466          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35467          * @param {Roo.LayoutRegion} this
35468          * @param {Roo.ContentPanel} panel The panel
35469          * @param {Object} e The cancel event object
35470          */
35471         "beforeremove" : true,
35472         /**
35473          * @event invalidated
35474          * Fires when the layout for this region is changed.
35475          * @param {Roo.LayoutRegion} this
35476          */
35477         "invalidated" : true,
35478         /**
35479          * @event visibilitychange
35480          * Fires when this region is shown or hidden 
35481          * @param {Roo.LayoutRegion} this
35482          * @param {Boolean} visibility true or false
35483          */
35484         "visibilitychange" : true,
35485         /**
35486          * @event paneladded
35487          * Fires when a panel is added. 
35488          * @param {Roo.LayoutRegion} this
35489          * @param {Roo.ContentPanel} panel The panel
35490          */
35491         "paneladded" : true,
35492         /**
35493          * @event panelremoved
35494          * Fires when a panel is removed. 
35495          * @param {Roo.LayoutRegion} this
35496          * @param {Roo.ContentPanel} panel The panel
35497          */
35498         "panelremoved" : true,
35499         /**
35500          * @event beforecollapse
35501          * Fires when this region before collapse.
35502          * @param {Roo.LayoutRegion} this
35503          */
35504         "beforecollapse" : true,
35505         /**
35506          * @event collapsed
35507          * Fires when this region is collapsed.
35508          * @param {Roo.LayoutRegion} this
35509          */
35510         "collapsed" : true,
35511         /**
35512          * @event expanded
35513          * Fires when this region is expanded.
35514          * @param {Roo.LayoutRegion} this
35515          */
35516         "expanded" : true,
35517         /**
35518          * @event slideshow
35519          * Fires when this region is slid into view.
35520          * @param {Roo.LayoutRegion} this
35521          */
35522         "slideshow" : true,
35523         /**
35524          * @event slidehide
35525          * Fires when this region slides out of view. 
35526          * @param {Roo.LayoutRegion} this
35527          */
35528         "slidehide" : true,
35529         /**
35530          * @event panelactivated
35531          * Fires when a panel is activated. 
35532          * @param {Roo.LayoutRegion} this
35533          * @param {Roo.ContentPanel} panel The activated panel
35534          */
35535         "panelactivated" : true,
35536         /**
35537          * @event resized
35538          * Fires when the user resizes this region. 
35539          * @param {Roo.LayoutRegion} this
35540          * @param {Number} newSize The new size (width for east/west, height for north/south)
35541          */
35542         "resized" : true
35543     };
35544     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35545     this.panels = new Roo.util.MixedCollection();
35546     this.panels.getKey = this.getPanelId.createDelegate(this);
35547     this.box = null;
35548     this.activePanel = null;
35549     // ensure listeners are added...
35550     
35551     if (config.listeners || config.events) {
35552         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35553             listeners : config.listeners || {},
35554             events : config.events || {}
35555         });
35556     }
35557     
35558     if(skipConfig !== true){
35559         this.applyConfig(config);
35560     }
35561 };
35562
35563 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35564 {
35565     getPanelId : function(p){
35566         return p.getId();
35567     },
35568     
35569     applyConfig : function(config){
35570         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35571         this.config = config;
35572         
35573     },
35574     
35575     /**
35576      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35577      * the width, for horizontal (north, south) the height.
35578      * @param {Number} newSize The new width or height
35579      */
35580     resizeTo : function(newSize){
35581         var el = this.el ? this.el :
35582                  (this.activePanel ? this.activePanel.getEl() : null);
35583         if(el){
35584             switch(this.position){
35585                 case "east":
35586                 case "west":
35587                     el.setWidth(newSize);
35588                     this.fireEvent("resized", this, newSize);
35589                 break;
35590                 case "north":
35591                 case "south":
35592                     el.setHeight(newSize);
35593                     this.fireEvent("resized", this, newSize);
35594                 break;                
35595             }
35596         }
35597     },
35598     
35599     getBox : function(){
35600         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35601     },
35602     
35603     getMargins : function(){
35604         return this.margins;
35605     },
35606     
35607     updateBox : function(box){
35608         this.box = box;
35609         var el = this.activePanel.getEl();
35610         el.dom.style.left = box.x + "px";
35611         el.dom.style.top = box.y + "px";
35612         this.activePanel.setSize(box.width, box.height);
35613     },
35614     
35615     /**
35616      * Returns the container element for this region.
35617      * @return {Roo.Element}
35618      */
35619     getEl : function(){
35620         return this.activePanel;
35621     },
35622     
35623     /**
35624      * Returns true if this region is currently visible.
35625      * @return {Boolean}
35626      */
35627     isVisible : function(){
35628         return this.activePanel ? true : false;
35629     },
35630     
35631     setActivePanel : function(panel){
35632         panel = this.getPanel(panel);
35633         if(this.activePanel && this.activePanel != panel){
35634             this.activePanel.setActiveState(false);
35635             this.activePanel.getEl().setLeftTop(-10000,-10000);
35636         }
35637         this.activePanel = panel;
35638         panel.setActiveState(true);
35639         if(this.box){
35640             panel.setSize(this.box.width, this.box.height);
35641         }
35642         this.fireEvent("panelactivated", this, panel);
35643         this.fireEvent("invalidated");
35644     },
35645     
35646     /**
35647      * Show the specified panel.
35648      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35649      * @return {Roo.ContentPanel} The shown panel or null
35650      */
35651     showPanel : function(panel){
35652         panel = this.getPanel(panel);
35653         if(panel){
35654             this.setActivePanel(panel);
35655         }
35656         return panel;
35657     },
35658     
35659     /**
35660      * Get the active panel for this region.
35661      * @return {Roo.ContentPanel} The active panel or null
35662      */
35663     getActivePanel : function(){
35664         return this.activePanel;
35665     },
35666     
35667     /**
35668      * Add the passed ContentPanel(s)
35669      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35670      * @return {Roo.ContentPanel} The panel added (if only one was added)
35671      */
35672     add : function(panel){
35673         if(arguments.length > 1){
35674             for(var i = 0, len = arguments.length; i < len; i++) {
35675                 this.add(arguments[i]);
35676             }
35677             return null;
35678         }
35679         if(this.hasPanel(panel)){
35680             this.showPanel(panel);
35681             return panel;
35682         }
35683         var el = panel.getEl();
35684         if(el.dom.parentNode != this.mgr.el.dom){
35685             this.mgr.el.dom.appendChild(el.dom);
35686         }
35687         if(panel.setRegion){
35688             panel.setRegion(this);
35689         }
35690         this.panels.add(panel);
35691         el.setStyle("position", "absolute");
35692         if(!panel.background){
35693             this.setActivePanel(panel);
35694             if(this.config.initialSize && this.panels.getCount()==1){
35695                 this.resizeTo(this.config.initialSize);
35696             }
35697         }
35698         this.fireEvent("paneladded", this, panel);
35699         return panel;
35700     },
35701     
35702     /**
35703      * Returns true if the panel is in this region.
35704      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35705      * @return {Boolean}
35706      */
35707     hasPanel : function(panel){
35708         if(typeof panel == "object"){ // must be panel obj
35709             panel = panel.getId();
35710         }
35711         return this.getPanel(panel) ? true : false;
35712     },
35713     
35714     /**
35715      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35716      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35717      * @param {Boolean} preservePanel Overrides the config preservePanel option
35718      * @return {Roo.ContentPanel} The panel that was removed
35719      */
35720     remove : function(panel, preservePanel){
35721         panel = this.getPanel(panel);
35722         if(!panel){
35723             return null;
35724         }
35725         var e = {};
35726         this.fireEvent("beforeremove", this, panel, e);
35727         if(e.cancel === true){
35728             return null;
35729         }
35730         var panelId = panel.getId();
35731         this.panels.removeKey(panelId);
35732         return panel;
35733     },
35734     
35735     /**
35736      * Returns the panel specified or null if it's not in this region.
35737      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35738      * @return {Roo.ContentPanel}
35739      */
35740     getPanel : function(id){
35741         if(typeof id == "object"){ // must be panel obj
35742             return id;
35743         }
35744         return this.panels.get(id);
35745     },
35746     
35747     /**
35748      * Returns this regions position (north/south/east/west/center).
35749      * @return {String} 
35750      */
35751     getPosition: function(){
35752         return this.position;    
35753     }
35754 });/*
35755  * Based on:
35756  * Ext JS Library 1.1.1
35757  * Copyright(c) 2006-2007, Ext JS, LLC.
35758  *
35759  * Originally Released Under LGPL - original licence link has changed is not relivant.
35760  *
35761  * Fork - LGPL
35762  * <script type="text/javascript">
35763  */
35764  
35765 /**
35766  * @class Roo.bootstrap.layout.Region
35767  * @extends Roo.bootstrap.layout.Basic
35768  * This class represents a region in a layout manager.
35769  
35770  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35771  * @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})
35772  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35773  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35774  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35775  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35776  * @cfg {String}    title           The title for the region (overrides panel titles)
35777  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35778  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35779  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35780  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35781  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35782  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35783  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35784  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35785  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35786  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35787
35788  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35789  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35790  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35791  * @cfg {Number}    width           For East/West panels
35792  * @cfg {Number}    height          For North/South panels
35793  * @cfg {Boolean}   split           To show the splitter
35794  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35795  * 
35796  * @cfg {string}   cls             Extra CSS classes to add to region
35797  * 
35798  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35799  * @cfg {string}   region  the region that it inhabits..
35800  *
35801
35802  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35803  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35804
35805  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35806  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35807  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35808  */
35809 Roo.bootstrap.layout.Region = function(config)
35810 {
35811     this.applyConfig(config);
35812
35813     var mgr = config.mgr;
35814     var pos = config.region;
35815     config.skipConfig = true;
35816     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35817     
35818     if (mgr.el) {
35819         this.onRender(mgr.el);   
35820     }
35821      
35822     this.visible = true;
35823     this.collapsed = false;
35824     this.unrendered_panels = [];
35825 };
35826
35827 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35828
35829     position: '', // set by wrapper (eg. north/south etc..)
35830     unrendered_panels : null,  // unrendered panels.
35831     createBody : function(){
35832         /** This region's body element 
35833         * @type Roo.Element */
35834         this.bodyEl = this.el.createChild({
35835                 tag: "div",
35836                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35837         });
35838     },
35839
35840     onRender: function(ctr, pos)
35841     {
35842         var dh = Roo.DomHelper;
35843         /** This region's container element 
35844         * @type Roo.Element */
35845         this.el = dh.append(ctr.dom, {
35846                 tag: "div",
35847                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35848             }, true);
35849         /** This region's title element 
35850         * @type Roo.Element */
35851     
35852         this.titleEl = dh.append(this.el.dom,
35853             {
35854                     tag: "div",
35855                     unselectable: "on",
35856                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35857                     children:[
35858                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35859                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35860                     ]}, true);
35861         
35862         this.titleEl.enableDisplayMode();
35863         /** This region's title text element 
35864         * @type HTMLElement */
35865         this.titleTextEl = this.titleEl.dom.firstChild;
35866         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35867         /*
35868         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35869         this.closeBtn.enableDisplayMode();
35870         this.closeBtn.on("click", this.closeClicked, this);
35871         this.closeBtn.hide();
35872     */
35873         this.createBody(this.config);
35874         if(this.config.hideWhenEmpty){
35875             this.hide();
35876             this.on("paneladded", this.validateVisibility, this);
35877             this.on("panelremoved", this.validateVisibility, this);
35878         }
35879         if(this.autoScroll){
35880             this.bodyEl.setStyle("overflow", "auto");
35881         }else{
35882             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35883         }
35884         //if(c.titlebar !== false){
35885             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35886                 this.titleEl.hide();
35887             }else{
35888                 this.titleEl.show();
35889                 if(this.config.title){
35890                     this.titleTextEl.innerHTML = this.config.title;
35891                 }
35892             }
35893         //}
35894         if(this.config.collapsed){
35895             this.collapse(true);
35896         }
35897         if(this.config.hidden){
35898             this.hide();
35899         }
35900         
35901         if (this.unrendered_panels && this.unrendered_panels.length) {
35902             for (var i =0;i< this.unrendered_panels.length; i++) {
35903                 this.add(this.unrendered_panels[i]);
35904             }
35905             this.unrendered_panels = null;
35906             
35907         }
35908         
35909     },
35910     
35911     applyConfig : function(c)
35912     {
35913         /*
35914          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35915             var dh = Roo.DomHelper;
35916             if(c.titlebar !== false){
35917                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35918                 this.collapseBtn.on("click", this.collapse, this);
35919                 this.collapseBtn.enableDisplayMode();
35920                 /*
35921                 if(c.showPin === true || this.showPin){
35922                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35923                     this.stickBtn.enableDisplayMode();
35924                     this.stickBtn.on("click", this.expand, this);
35925                     this.stickBtn.hide();
35926                 }
35927                 
35928             }
35929             */
35930             /** This region's collapsed element
35931             * @type Roo.Element */
35932             /*
35933              *
35934             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35935                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35936             ]}, true);
35937             
35938             if(c.floatable !== false){
35939                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35940                this.collapsedEl.on("click", this.collapseClick, this);
35941             }
35942
35943             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35944                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35945                    id: "message", unselectable: "on", style:{"float":"left"}});
35946                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35947              }
35948             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35949             this.expandBtn.on("click", this.expand, this);
35950             
35951         }
35952         
35953         if(this.collapseBtn){
35954             this.collapseBtn.setVisible(c.collapsible == true);
35955         }
35956         
35957         this.cmargins = c.cmargins || this.cmargins ||
35958                          (this.position == "west" || this.position == "east" ?
35959                              {top: 0, left: 2, right:2, bottom: 0} :
35960                              {top: 2, left: 0, right:0, bottom: 2});
35961         */
35962         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35963         
35964         
35965         this.bottomTabs = c.tabPosition != "top";
35966         
35967         this.autoScroll = c.autoScroll || false;
35968         
35969         
35970        
35971         
35972         this.duration = c.duration || .30;
35973         this.slideDuration = c.slideDuration || .45;
35974         this.config = c;
35975        
35976     },
35977     /**
35978      * Returns true if this region is currently visible.
35979      * @return {Boolean}
35980      */
35981     isVisible : function(){
35982         return this.visible;
35983     },
35984
35985     /**
35986      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35987      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35988      */
35989     //setCollapsedTitle : function(title){
35990     //    title = title || "&#160;";
35991      //   if(this.collapsedTitleTextEl){
35992       //      this.collapsedTitleTextEl.innerHTML = title;
35993        // }
35994     //},
35995
35996     getBox : function(){
35997         var b;
35998       //  if(!this.collapsed){
35999             b = this.el.getBox(false, true);
36000        // }else{
36001           //  b = this.collapsedEl.getBox(false, true);
36002         //}
36003         return b;
36004     },
36005
36006     getMargins : function(){
36007         return this.margins;
36008         //return this.collapsed ? this.cmargins : this.margins;
36009     },
36010 /*
36011     highlight : function(){
36012         this.el.addClass("x-layout-panel-dragover");
36013     },
36014
36015     unhighlight : function(){
36016         this.el.removeClass("x-layout-panel-dragover");
36017     },
36018 */
36019     updateBox : function(box)
36020     {
36021         if (!this.bodyEl) {
36022             return; // not rendered yet..
36023         }
36024         
36025         this.box = box;
36026         if(!this.collapsed){
36027             this.el.dom.style.left = box.x + "px";
36028             this.el.dom.style.top = box.y + "px";
36029             this.updateBody(box.width, box.height);
36030         }else{
36031             this.collapsedEl.dom.style.left = box.x + "px";
36032             this.collapsedEl.dom.style.top = box.y + "px";
36033             this.collapsedEl.setSize(box.width, box.height);
36034         }
36035         if(this.tabs){
36036             this.tabs.autoSizeTabs();
36037         }
36038     },
36039
36040     updateBody : function(w, h)
36041     {
36042         if(w !== null){
36043             this.el.setWidth(w);
36044             w -= this.el.getBorderWidth("rl");
36045             if(this.config.adjustments){
36046                 w += this.config.adjustments[0];
36047             }
36048         }
36049         if(h !== null && h > 0){
36050             this.el.setHeight(h);
36051             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36052             h -= this.el.getBorderWidth("tb");
36053             if(this.config.adjustments){
36054                 h += this.config.adjustments[1];
36055             }
36056             this.bodyEl.setHeight(h);
36057             if(this.tabs){
36058                 h = this.tabs.syncHeight(h);
36059             }
36060         }
36061         if(this.panelSize){
36062             w = w !== null ? w : this.panelSize.width;
36063             h = h !== null ? h : this.panelSize.height;
36064         }
36065         if(this.activePanel){
36066             var el = this.activePanel.getEl();
36067             w = w !== null ? w : el.getWidth();
36068             h = h !== null ? h : el.getHeight();
36069             this.panelSize = {width: w, height: h};
36070             this.activePanel.setSize(w, h);
36071         }
36072         if(Roo.isIE && this.tabs){
36073             this.tabs.el.repaint();
36074         }
36075     },
36076
36077     /**
36078      * Returns the container element for this region.
36079      * @return {Roo.Element}
36080      */
36081     getEl : function(){
36082         return this.el;
36083     },
36084
36085     /**
36086      * Hides this region.
36087      */
36088     hide : function(){
36089         //if(!this.collapsed){
36090             this.el.dom.style.left = "-2000px";
36091             this.el.hide();
36092         //}else{
36093          //   this.collapsedEl.dom.style.left = "-2000px";
36094          //   this.collapsedEl.hide();
36095        // }
36096         this.visible = false;
36097         this.fireEvent("visibilitychange", this, false);
36098     },
36099
36100     /**
36101      * Shows this region if it was previously hidden.
36102      */
36103     show : function(){
36104         //if(!this.collapsed){
36105             this.el.show();
36106         //}else{
36107         //    this.collapsedEl.show();
36108        // }
36109         this.visible = true;
36110         this.fireEvent("visibilitychange", this, true);
36111     },
36112 /*
36113     closeClicked : function(){
36114         if(this.activePanel){
36115             this.remove(this.activePanel);
36116         }
36117     },
36118
36119     collapseClick : function(e){
36120         if(this.isSlid){
36121            e.stopPropagation();
36122            this.slideIn();
36123         }else{
36124            e.stopPropagation();
36125            this.slideOut();
36126         }
36127     },
36128 */
36129     /**
36130      * Collapses this region.
36131      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36132      */
36133     /*
36134     collapse : function(skipAnim, skipCheck = false){
36135         if(this.collapsed) {
36136             return;
36137         }
36138         
36139         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36140             
36141             this.collapsed = true;
36142             if(this.split){
36143                 this.split.el.hide();
36144             }
36145             if(this.config.animate && skipAnim !== true){
36146                 this.fireEvent("invalidated", this);
36147                 this.animateCollapse();
36148             }else{
36149                 this.el.setLocation(-20000,-20000);
36150                 this.el.hide();
36151                 this.collapsedEl.show();
36152                 this.fireEvent("collapsed", this);
36153                 this.fireEvent("invalidated", this);
36154             }
36155         }
36156         
36157     },
36158 */
36159     animateCollapse : function(){
36160         // overridden
36161     },
36162
36163     /**
36164      * Expands this region if it was previously collapsed.
36165      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36166      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36167      */
36168     /*
36169     expand : function(e, skipAnim){
36170         if(e) {
36171             e.stopPropagation();
36172         }
36173         if(!this.collapsed || this.el.hasActiveFx()) {
36174             return;
36175         }
36176         if(this.isSlid){
36177             this.afterSlideIn();
36178             skipAnim = true;
36179         }
36180         this.collapsed = false;
36181         if(this.config.animate && skipAnim !== true){
36182             this.animateExpand();
36183         }else{
36184             this.el.show();
36185             if(this.split){
36186                 this.split.el.show();
36187             }
36188             this.collapsedEl.setLocation(-2000,-2000);
36189             this.collapsedEl.hide();
36190             this.fireEvent("invalidated", this);
36191             this.fireEvent("expanded", this);
36192         }
36193     },
36194 */
36195     animateExpand : function(){
36196         // overridden
36197     },
36198
36199     initTabs : function()
36200     {
36201         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36202         
36203         var ts = new Roo.bootstrap.panel.Tabs({
36204                 el: this.bodyEl.dom,
36205                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36206                 disableTooltips: this.config.disableTabTips,
36207                 toolbar : this.config.toolbar
36208             });
36209         
36210         if(this.config.hideTabs){
36211             ts.stripWrap.setDisplayed(false);
36212         }
36213         this.tabs = ts;
36214         ts.resizeTabs = this.config.resizeTabs === true;
36215         ts.minTabWidth = this.config.minTabWidth || 40;
36216         ts.maxTabWidth = this.config.maxTabWidth || 250;
36217         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36218         ts.monitorResize = false;
36219         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36220         ts.bodyEl.addClass('roo-layout-tabs-body');
36221         this.panels.each(this.initPanelAsTab, this);
36222     },
36223
36224     initPanelAsTab : function(panel){
36225         var ti = this.tabs.addTab(
36226             panel.getEl().id,
36227             panel.getTitle(),
36228             null,
36229             this.config.closeOnTab && panel.isClosable(),
36230             panel.tpl
36231         );
36232         if(panel.tabTip !== undefined){
36233             ti.setTooltip(panel.tabTip);
36234         }
36235         ti.on("activate", function(){
36236               this.setActivePanel(panel);
36237         }, this);
36238         
36239         if(this.config.closeOnTab){
36240             ti.on("beforeclose", function(t, e){
36241                 e.cancel = true;
36242                 this.remove(panel);
36243             }, this);
36244         }
36245         
36246         panel.tabItem = ti;
36247         
36248         return ti;
36249     },
36250
36251     updatePanelTitle : function(panel, title)
36252     {
36253         if(this.activePanel == panel){
36254             this.updateTitle(title);
36255         }
36256         if(this.tabs){
36257             var ti = this.tabs.getTab(panel.getEl().id);
36258             ti.setText(title);
36259             if(panel.tabTip !== undefined){
36260                 ti.setTooltip(panel.tabTip);
36261             }
36262         }
36263     },
36264
36265     updateTitle : function(title){
36266         if(this.titleTextEl && !this.config.title){
36267             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36268         }
36269     },
36270
36271     setActivePanel : function(panel)
36272     {
36273         panel = this.getPanel(panel);
36274         if(this.activePanel && this.activePanel != panel){
36275             if(this.activePanel.setActiveState(false) === false){
36276                 return;
36277             }
36278         }
36279         this.activePanel = panel;
36280         panel.setActiveState(true);
36281         if(this.panelSize){
36282             panel.setSize(this.panelSize.width, this.panelSize.height);
36283         }
36284         if(this.closeBtn){
36285             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36286         }
36287         this.updateTitle(panel.getTitle());
36288         if(this.tabs){
36289             this.fireEvent("invalidated", this);
36290         }
36291         this.fireEvent("panelactivated", this, panel);
36292     },
36293
36294     /**
36295      * Shows the specified panel.
36296      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36297      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36298      */
36299     showPanel : function(panel)
36300     {
36301         panel = this.getPanel(panel);
36302         if(panel){
36303             if(this.tabs){
36304                 var tab = this.tabs.getTab(panel.getEl().id);
36305                 if(tab.isHidden()){
36306                     this.tabs.unhideTab(tab.id);
36307                 }
36308                 tab.activate();
36309             }else{
36310                 this.setActivePanel(panel);
36311             }
36312         }
36313         return panel;
36314     },
36315
36316     /**
36317      * Get the active panel for this region.
36318      * @return {Roo.ContentPanel} The active panel or null
36319      */
36320     getActivePanel : function(){
36321         return this.activePanel;
36322     },
36323
36324     validateVisibility : function(){
36325         if(this.panels.getCount() < 1){
36326             this.updateTitle("&#160;");
36327             this.closeBtn.hide();
36328             this.hide();
36329         }else{
36330             if(!this.isVisible()){
36331                 this.show();
36332             }
36333         }
36334     },
36335
36336     /**
36337      * Adds the passed ContentPanel(s) to this region.
36338      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36339      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36340      */
36341     add : function(panel)
36342     {
36343         if(arguments.length > 1){
36344             for(var i = 0, len = arguments.length; i < len; i++) {
36345                 this.add(arguments[i]);
36346             }
36347             return null;
36348         }
36349         
36350         // if we have not been rendered yet, then we can not really do much of this..
36351         if (!this.bodyEl) {
36352             this.unrendered_panels.push(panel);
36353             return panel;
36354         }
36355         
36356         
36357         
36358         
36359         if(this.hasPanel(panel)){
36360             this.showPanel(panel);
36361             return panel;
36362         }
36363         panel.setRegion(this);
36364         this.panels.add(panel);
36365        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36366             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36367             // and hide them... ???
36368             this.bodyEl.dom.appendChild(panel.getEl().dom);
36369             if(panel.background !== true){
36370                 this.setActivePanel(panel);
36371             }
36372             this.fireEvent("paneladded", this, panel);
36373             return panel;
36374         }
36375         */
36376         if(!this.tabs){
36377             this.initTabs();
36378         }else{
36379             this.initPanelAsTab(panel);
36380         }
36381         
36382         
36383         if(panel.background !== true){
36384             this.tabs.activate(panel.getEl().id);
36385         }
36386         this.fireEvent("paneladded", this, panel);
36387         return panel;
36388     },
36389
36390     /**
36391      * Hides the tab for the specified panel.
36392      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36393      */
36394     hidePanel : function(panel){
36395         if(this.tabs && (panel = this.getPanel(panel))){
36396             this.tabs.hideTab(panel.getEl().id);
36397         }
36398     },
36399
36400     /**
36401      * Unhides the tab for a previously hidden panel.
36402      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36403      */
36404     unhidePanel : function(panel){
36405         if(this.tabs && (panel = this.getPanel(panel))){
36406             this.tabs.unhideTab(panel.getEl().id);
36407         }
36408     },
36409
36410     clearPanels : function(){
36411         while(this.panels.getCount() > 0){
36412              this.remove(this.panels.first());
36413         }
36414     },
36415
36416     /**
36417      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36418      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36419      * @param {Boolean} preservePanel Overrides the config preservePanel option
36420      * @return {Roo.ContentPanel} The panel that was removed
36421      */
36422     remove : function(panel, preservePanel)
36423     {
36424         panel = this.getPanel(panel);
36425         if(!panel){
36426             return null;
36427         }
36428         var e = {};
36429         this.fireEvent("beforeremove", this, panel, e);
36430         if(e.cancel === true){
36431             return null;
36432         }
36433         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36434         var panelId = panel.getId();
36435         this.panels.removeKey(panelId);
36436         if(preservePanel){
36437             document.body.appendChild(panel.getEl().dom);
36438         }
36439         if(this.tabs){
36440             this.tabs.removeTab(panel.getEl().id);
36441         }else if (!preservePanel){
36442             this.bodyEl.dom.removeChild(panel.getEl().dom);
36443         }
36444         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36445             var p = this.panels.first();
36446             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36447             tempEl.appendChild(p.getEl().dom);
36448             this.bodyEl.update("");
36449             this.bodyEl.dom.appendChild(p.getEl().dom);
36450             tempEl = null;
36451             this.updateTitle(p.getTitle());
36452             this.tabs = null;
36453             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36454             this.setActivePanel(p);
36455         }
36456         panel.setRegion(null);
36457         if(this.activePanel == panel){
36458             this.activePanel = null;
36459         }
36460         if(this.config.autoDestroy !== false && preservePanel !== true){
36461             try{panel.destroy();}catch(e){}
36462         }
36463         this.fireEvent("panelremoved", this, panel);
36464         return panel;
36465     },
36466
36467     /**
36468      * Returns the TabPanel component used by this region
36469      * @return {Roo.TabPanel}
36470      */
36471     getTabs : function(){
36472         return this.tabs;
36473     },
36474
36475     createTool : function(parentEl, className){
36476         var btn = Roo.DomHelper.append(parentEl, {
36477             tag: "div",
36478             cls: "x-layout-tools-button",
36479             children: [ {
36480                 tag: "div",
36481                 cls: "roo-layout-tools-button-inner " + className,
36482                 html: "&#160;"
36483             }]
36484         }, true);
36485         btn.addClassOnOver("roo-layout-tools-button-over");
36486         return btn;
36487     }
36488 });/*
36489  * Based on:
36490  * Ext JS Library 1.1.1
36491  * Copyright(c) 2006-2007, Ext JS, LLC.
36492  *
36493  * Originally Released Under LGPL - original licence link has changed is not relivant.
36494  *
36495  * Fork - LGPL
36496  * <script type="text/javascript">
36497  */
36498  
36499
36500
36501 /**
36502  * @class Roo.SplitLayoutRegion
36503  * @extends Roo.LayoutRegion
36504  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36505  */
36506 Roo.bootstrap.layout.Split = function(config){
36507     this.cursor = config.cursor;
36508     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36509 };
36510
36511 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36512 {
36513     splitTip : "Drag to resize.",
36514     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36515     useSplitTips : false,
36516
36517     applyConfig : function(config){
36518         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36519     },
36520     
36521     onRender : function(ctr,pos) {
36522         
36523         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36524         if(!this.config.split){
36525             return;
36526         }
36527         if(!this.split){
36528             
36529             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36530                             tag: "div",
36531                             id: this.el.id + "-split",
36532                             cls: "roo-layout-split roo-layout-split-"+this.position,
36533                             html: "&#160;"
36534             });
36535             /** The SplitBar for this region 
36536             * @type Roo.SplitBar */
36537             // does not exist yet...
36538             Roo.log([this.position, this.orientation]);
36539             
36540             this.split = new Roo.bootstrap.SplitBar({
36541                 dragElement : splitEl,
36542                 resizingElement: this.el,
36543                 orientation : this.orientation
36544             });
36545             
36546             this.split.on("moved", this.onSplitMove, this);
36547             this.split.useShim = this.config.useShim === true;
36548             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36549             if(this.useSplitTips){
36550                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36551             }
36552             //if(config.collapsible){
36553             //    this.split.el.on("dblclick", this.collapse,  this);
36554             //}
36555         }
36556         if(typeof this.config.minSize != "undefined"){
36557             this.split.minSize = this.config.minSize;
36558         }
36559         if(typeof this.config.maxSize != "undefined"){
36560             this.split.maxSize = this.config.maxSize;
36561         }
36562         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36563             this.hideSplitter();
36564         }
36565         
36566     },
36567
36568     getHMaxSize : function(){
36569          var cmax = this.config.maxSize || 10000;
36570          var center = this.mgr.getRegion("center");
36571          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36572     },
36573
36574     getVMaxSize : function(){
36575          var cmax = this.config.maxSize || 10000;
36576          var center = this.mgr.getRegion("center");
36577          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36578     },
36579
36580     onSplitMove : function(split, newSize){
36581         this.fireEvent("resized", this, newSize);
36582     },
36583     
36584     /** 
36585      * Returns the {@link Roo.SplitBar} for this region.
36586      * @return {Roo.SplitBar}
36587      */
36588     getSplitBar : function(){
36589         return this.split;
36590     },
36591     
36592     hide : function(){
36593         this.hideSplitter();
36594         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36595     },
36596
36597     hideSplitter : function(){
36598         if(this.split){
36599             this.split.el.setLocation(-2000,-2000);
36600             this.split.el.hide();
36601         }
36602     },
36603
36604     show : function(){
36605         if(this.split){
36606             this.split.el.show();
36607         }
36608         Roo.bootstrap.layout.Split.superclass.show.call(this);
36609     },
36610     
36611     beforeSlide: function(){
36612         if(Roo.isGecko){// firefox overflow auto bug workaround
36613             this.bodyEl.clip();
36614             if(this.tabs) {
36615                 this.tabs.bodyEl.clip();
36616             }
36617             if(this.activePanel){
36618                 this.activePanel.getEl().clip();
36619                 
36620                 if(this.activePanel.beforeSlide){
36621                     this.activePanel.beforeSlide();
36622                 }
36623             }
36624         }
36625     },
36626     
36627     afterSlide : function(){
36628         if(Roo.isGecko){// firefox overflow auto bug workaround
36629             this.bodyEl.unclip();
36630             if(this.tabs) {
36631                 this.tabs.bodyEl.unclip();
36632             }
36633             if(this.activePanel){
36634                 this.activePanel.getEl().unclip();
36635                 if(this.activePanel.afterSlide){
36636                     this.activePanel.afterSlide();
36637                 }
36638             }
36639         }
36640     },
36641
36642     initAutoHide : function(){
36643         if(this.autoHide !== false){
36644             if(!this.autoHideHd){
36645                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36646                 this.autoHideHd = {
36647                     "mouseout": function(e){
36648                         if(!e.within(this.el, true)){
36649                             st.delay(500);
36650                         }
36651                     },
36652                     "mouseover" : function(e){
36653                         st.cancel();
36654                     },
36655                     scope : this
36656                 };
36657             }
36658             this.el.on(this.autoHideHd);
36659         }
36660     },
36661
36662     clearAutoHide : function(){
36663         if(this.autoHide !== false){
36664             this.el.un("mouseout", this.autoHideHd.mouseout);
36665             this.el.un("mouseover", this.autoHideHd.mouseover);
36666         }
36667     },
36668
36669     clearMonitor : function(){
36670         Roo.get(document).un("click", this.slideInIf, this);
36671     },
36672
36673     // these names are backwards but not changed for compat
36674     slideOut : function(){
36675         if(this.isSlid || this.el.hasActiveFx()){
36676             return;
36677         }
36678         this.isSlid = true;
36679         if(this.collapseBtn){
36680             this.collapseBtn.hide();
36681         }
36682         this.closeBtnState = this.closeBtn.getStyle('display');
36683         this.closeBtn.hide();
36684         if(this.stickBtn){
36685             this.stickBtn.show();
36686         }
36687         this.el.show();
36688         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36689         this.beforeSlide();
36690         this.el.setStyle("z-index", 10001);
36691         this.el.slideIn(this.getSlideAnchor(), {
36692             callback: function(){
36693                 this.afterSlide();
36694                 this.initAutoHide();
36695                 Roo.get(document).on("click", this.slideInIf, this);
36696                 this.fireEvent("slideshow", this);
36697             },
36698             scope: this,
36699             block: true
36700         });
36701     },
36702
36703     afterSlideIn : function(){
36704         this.clearAutoHide();
36705         this.isSlid = false;
36706         this.clearMonitor();
36707         this.el.setStyle("z-index", "");
36708         if(this.collapseBtn){
36709             this.collapseBtn.show();
36710         }
36711         this.closeBtn.setStyle('display', this.closeBtnState);
36712         if(this.stickBtn){
36713             this.stickBtn.hide();
36714         }
36715         this.fireEvent("slidehide", this);
36716     },
36717
36718     slideIn : function(cb){
36719         if(!this.isSlid || this.el.hasActiveFx()){
36720             Roo.callback(cb);
36721             return;
36722         }
36723         this.isSlid = false;
36724         this.beforeSlide();
36725         this.el.slideOut(this.getSlideAnchor(), {
36726             callback: function(){
36727                 this.el.setLeftTop(-10000, -10000);
36728                 this.afterSlide();
36729                 this.afterSlideIn();
36730                 Roo.callback(cb);
36731             },
36732             scope: this,
36733             block: true
36734         });
36735     },
36736     
36737     slideInIf : function(e){
36738         if(!e.within(this.el)){
36739             this.slideIn();
36740         }
36741     },
36742
36743     animateCollapse : function(){
36744         this.beforeSlide();
36745         this.el.setStyle("z-index", 20000);
36746         var anchor = this.getSlideAnchor();
36747         this.el.slideOut(anchor, {
36748             callback : function(){
36749                 this.el.setStyle("z-index", "");
36750                 this.collapsedEl.slideIn(anchor, {duration:.3});
36751                 this.afterSlide();
36752                 this.el.setLocation(-10000,-10000);
36753                 this.el.hide();
36754                 this.fireEvent("collapsed", this);
36755             },
36756             scope: this,
36757             block: true
36758         });
36759     },
36760
36761     animateExpand : function(){
36762         this.beforeSlide();
36763         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36764         this.el.setStyle("z-index", 20000);
36765         this.collapsedEl.hide({
36766             duration:.1
36767         });
36768         this.el.slideIn(this.getSlideAnchor(), {
36769             callback : function(){
36770                 this.el.setStyle("z-index", "");
36771                 this.afterSlide();
36772                 if(this.split){
36773                     this.split.el.show();
36774                 }
36775                 this.fireEvent("invalidated", this);
36776                 this.fireEvent("expanded", this);
36777             },
36778             scope: this,
36779             block: true
36780         });
36781     },
36782
36783     anchors : {
36784         "west" : "left",
36785         "east" : "right",
36786         "north" : "top",
36787         "south" : "bottom"
36788     },
36789
36790     sanchors : {
36791         "west" : "l",
36792         "east" : "r",
36793         "north" : "t",
36794         "south" : "b"
36795     },
36796
36797     canchors : {
36798         "west" : "tl-tr",
36799         "east" : "tr-tl",
36800         "north" : "tl-bl",
36801         "south" : "bl-tl"
36802     },
36803
36804     getAnchor : function(){
36805         return this.anchors[this.position];
36806     },
36807
36808     getCollapseAnchor : function(){
36809         return this.canchors[this.position];
36810     },
36811
36812     getSlideAnchor : function(){
36813         return this.sanchors[this.position];
36814     },
36815
36816     getAlignAdj : function(){
36817         var cm = this.cmargins;
36818         switch(this.position){
36819             case "west":
36820                 return [0, 0];
36821             break;
36822             case "east":
36823                 return [0, 0];
36824             break;
36825             case "north":
36826                 return [0, 0];
36827             break;
36828             case "south":
36829                 return [0, 0];
36830             break;
36831         }
36832     },
36833
36834     getExpandAdj : function(){
36835         var c = this.collapsedEl, cm = this.cmargins;
36836         switch(this.position){
36837             case "west":
36838                 return [-(cm.right+c.getWidth()+cm.left), 0];
36839             break;
36840             case "east":
36841                 return [cm.right+c.getWidth()+cm.left, 0];
36842             break;
36843             case "north":
36844                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36845             break;
36846             case "south":
36847                 return [0, cm.top+cm.bottom+c.getHeight()];
36848             break;
36849         }
36850     }
36851 });/*
36852  * Based on:
36853  * Ext JS Library 1.1.1
36854  * Copyright(c) 2006-2007, Ext JS, LLC.
36855  *
36856  * Originally Released Under LGPL - original licence link has changed is not relivant.
36857  *
36858  * Fork - LGPL
36859  * <script type="text/javascript">
36860  */
36861 /*
36862  * These classes are private internal classes
36863  */
36864 Roo.bootstrap.layout.Center = function(config){
36865     config.region = "center";
36866     Roo.bootstrap.layout.Region.call(this, config);
36867     this.visible = true;
36868     this.minWidth = config.minWidth || 20;
36869     this.minHeight = config.minHeight || 20;
36870 };
36871
36872 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36873     hide : function(){
36874         // center panel can't be hidden
36875     },
36876     
36877     show : function(){
36878         // center panel can't be hidden
36879     },
36880     
36881     getMinWidth: function(){
36882         return this.minWidth;
36883     },
36884     
36885     getMinHeight: function(){
36886         return this.minHeight;
36887     }
36888 });
36889
36890
36891
36892
36893  
36894
36895
36896
36897
36898
36899 Roo.bootstrap.layout.North = function(config)
36900 {
36901     config.region = 'north';
36902     config.cursor = 'n-resize';
36903     
36904     Roo.bootstrap.layout.Split.call(this, config);
36905     
36906     
36907     if(this.split){
36908         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36909         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36910         this.split.el.addClass("roo-layout-split-v");
36911     }
36912     var size = config.initialSize || config.height;
36913     if(typeof size != "undefined"){
36914         this.el.setHeight(size);
36915     }
36916 };
36917 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36918 {
36919     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36920     
36921     
36922     
36923     getBox : function(){
36924         if(this.collapsed){
36925             return this.collapsedEl.getBox();
36926         }
36927         var box = this.el.getBox();
36928         if(this.split){
36929             box.height += this.split.el.getHeight();
36930         }
36931         return box;
36932     },
36933     
36934     updateBox : function(box){
36935         if(this.split && !this.collapsed){
36936             box.height -= this.split.el.getHeight();
36937             this.split.el.setLeft(box.x);
36938             this.split.el.setTop(box.y+box.height);
36939             this.split.el.setWidth(box.width);
36940         }
36941         if(this.collapsed){
36942             this.updateBody(box.width, null);
36943         }
36944         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36945     }
36946 });
36947
36948
36949
36950
36951
36952 Roo.bootstrap.layout.South = function(config){
36953     config.region = 'south';
36954     config.cursor = 's-resize';
36955     Roo.bootstrap.layout.Split.call(this, config);
36956     if(this.split){
36957         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36958         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36959         this.split.el.addClass("roo-layout-split-v");
36960     }
36961     var size = config.initialSize || config.height;
36962     if(typeof size != "undefined"){
36963         this.el.setHeight(size);
36964     }
36965 };
36966
36967 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36968     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36969     getBox : function(){
36970         if(this.collapsed){
36971             return this.collapsedEl.getBox();
36972         }
36973         var box = this.el.getBox();
36974         if(this.split){
36975             var sh = this.split.el.getHeight();
36976             box.height += sh;
36977             box.y -= sh;
36978         }
36979         return box;
36980     },
36981     
36982     updateBox : function(box){
36983         if(this.split && !this.collapsed){
36984             var sh = this.split.el.getHeight();
36985             box.height -= sh;
36986             box.y += sh;
36987             this.split.el.setLeft(box.x);
36988             this.split.el.setTop(box.y-sh);
36989             this.split.el.setWidth(box.width);
36990         }
36991         if(this.collapsed){
36992             this.updateBody(box.width, null);
36993         }
36994         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36995     }
36996 });
36997
36998 Roo.bootstrap.layout.East = function(config){
36999     config.region = "east";
37000     config.cursor = "e-resize";
37001     Roo.bootstrap.layout.Split.call(this, config);
37002     if(this.split){
37003         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37004         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37005         this.split.el.addClass("roo-layout-split-h");
37006     }
37007     var size = config.initialSize || config.width;
37008     if(typeof size != "undefined"){
37009         this.el.setWidth(size);
37010     }
37011 };
37012 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37013     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37014     getBox : function(){
37015         if(this.collapsed){
37016             return this.collapsedEl.getBox();
37017         }
37018         var box = this.el.getBox();
37019         if(this.split){
37020             var sw = this.split.el.getWidth();
37021             box.width += sw;
37022             box.x -= sw;
37023         }
37024         return box;
37025     },
37026
37027     updateBox : function(box){
37028         if(this.split && !this.collapsed){
37029             var sw = this.split.el.getWidth();
37030             box.width -= sw;
37031             this.split.el.setLeft(box.x);
37032             this.split.el.setTop(box.y);
37033             this.split.el.setHeight(box.height);
37034             box.x += sw;
37035         }
37036         if(this.collapsed){
37037             this.updateBody(null, box.height);
37038         }
37039         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37040     }
37041 });
37042
37043 Roo.bootstrap.layout.West = function(config){
37044     config.region = "west";
37045     config.cursor = "w-resize";
37046     
37047     Roo.bootstrap.layout.Split.call(this, config);
37048     if(this.split){
37049         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37050         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37051         this.split.el.addClass("roo-layout-split-h");
37052     }
37053     
37054 };
37055 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37056     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37057     
37058     onRender: function(ctr, pos)
37059     {
37060         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37061         var size = this.config.initialSize || this.config.width;
37062         if(typeof size != "undefined"){
37063             this.el.setWidth(size);
37064         }
37065     },
37066     
37067     getBox : function(){
37068         if(this.collapsed){
37069             return this.collapsedEl.getBox();
37070         }
37071         var box = this.el.getBox();
37072         if(this.split){
37073             box.width += this.split.el.getWidth();
37074         }
37075         return box;
37076     },
37077     
37078     updateBox : function(box){
37079         if(this.split && !this.collapsed){
37080             var sw = this.split.el.getWidth();
37081             box.width -= sw;
37082             this.split.el.setLeft(box.x+box.width);
37083             this.split.el.setTop(box.y);
37084             this.split.el.setHeight(box.height);
37085         }
37086         if(this.collapsed){
37087             this.updateBody(null, box.height);
37088         }
37089         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37090     }
37091 });
37092 Roo.namespace("Roo.bootstrap.panel");/*
37093  * Based on:
37094  * Ext JS Library 1.1.1
37095  * Copyright(c) 2006-2007, Ext JS, LLC.
37096  *
37097  * Originally Released Under LGPL - original licence link has changed is not relivant.
37098  *
37099  * Fork - LGPL
37100  * <script type="text/javascript">
37101  */
37102 /**
37103  * @class Roo.ContentPanel
37104  * @extends Roo.util.Observable
37105  * A basic ContentPanel element.
37106  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37107  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37108  * @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
37109  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37110  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37111  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37112  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37113  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37114  * @cfg {String} title          The title for this panel
37115  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37116  * @cfg {String} url            Calls {@link #setUrl} with this value
37117  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37118  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37119  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37120  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37121  * @cfg {Boolean} badges render the badges
37122
37123  * @constructor
37124  * Create a new ContentPanel.
37125  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37126  * @param {String/Object} config A string to set only the title or a config object
37127  * @param {String} content (optional) Set the HTML content for this panel
37128  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37129  */
37130 Roo.bootstrap.panel.Content = function( config){
37131     
37132     this.tpl = config.tpl || false;
37133     
37134     var el = config.el;
37135     var content = config.content;
37136
37137     if(config.autoCreate){ // xtype is available if this is called from factory
37138         el = Roo.id();
37139     }
37140     this.el = Roo.get(el);
37141     if(!this.el && config && config.autoCreate){
37142         if(typeof config.autoCreate == "object"){
37143             if(!config.autoCreate.id){
37144                 config.autoCreate.id = config.id||el;
37145             }
37146             this.el = Roo.DomHelper.append(document.body,
37147                         config.autoCreate, true);
37148         }else{
37149             var elcfg =  {   tag: "div",
37150                             cls: "roo-layout-inactive-content",
37151                             id: config.id||el
37152                             };
37153             if (config.html) {
37154                 elcfg.html = config.html;
37155                 
37156             }
37157                         
37158             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37159         }
37160     } 
37161     this.closable = false;
37162     this.loaded = false;
37163     this.active = false;
37164    
37165       
37166     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37167         
37168         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37169         
37170         this.wrapEl = this.el; //this.el.wrap();
37171         var ti = [];
37172         if (config.toolbar.items) {
37173             ti = config.toolbar.items ;
37174             delete config.toolbar.items ;
37175         }
37176         
37177         var nitems = [];
37178         this.toolbar.render(this.wrapEl, 'before');
37179         for(var i =0;i < ti.length;i++) {
37180           //  Roo.log(['add child', items[i]]);
37181             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37182         }
37183         this.toolbar.items = nitems;
37184         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37185         delete config.toolbar;
37186         
37187     }
37188     /*
37189     // xtype created footer. - not sure if will work as we normally have to render first..
37190     if (this.footer && !this.footer.el && this.footer.xtype) {
37191         if (!this.wrapEl) {
37192             this.wrapEl = this.el.wrap();
37193         }
37194     
37195         this.footer.container = this.wrapEl.createChild();
37196          
37197         this.footer = Roo.factory(this.footer, Roo);
37198         
37199     }
37200     */
37201     
37202      if(typeof config == "string"){
37203         this.title = config;
37204     }else{
37205         Roo.apply(this, config);
37206     }
37207     
37208     if(this.resizeEl){
37209         this.resizeEl = Roo.get(this.resizeEl, true);
37210     }else{
37211         this.resizeEl = this.el;
37212     }
37213     // handle view.xtype
37214     
37215  
37216     
37217     
37218     this.addEvents({
37219         /**
37220          * @event activate
37221          * Fires when this panel is activated. 
37222          * @param {Roo.ContentPanel} this
37223          */
37224         "activate" : true,
37225         /**
37226          * @event deactivate
37227          * Fires when this panel is activated. 
37228          * @param {Roo.ContentPanel} this
37229          */
37230         "deactivate" : true,
37231
37232         /**
37233          * @event resize
37234          * Fires when this panel is resized if fitToFrame is true.
37235          * @param {Roo.ContentPanel} this
37236          * @param {Number} width The width after any component adjustments
37237          * @param {Number} height The height after any component adjustments
37238          */
37239         "resize" : true,
37240         
37241          /**
37242          * @event render
37243          * Fires when this tab is created
37244          * @param {Roo.ContentPanel} this
37245          */
37246         "render" : true
37247         
37248         
37249         
37250     });
37251     
37252
37253     
37254     
37255     if(this.autoScroll){
37256         this.resizeEl.setStyle("overflow", "auto");
37257     } else {
37258         // fix randome scrolling
37259         //this.el.on('scroll', function() {
37260         //    Roo.log('fix random scolling');
37261         //    this.scrollTo('top',0); 
37262         //});
37263     }
37264     content = content || this.content;
37265     if(content){
37266         this.setContent(content);
37267     }
37268     if(config && config.url){
37269         this.setUrl(this.url, this.params, this.loadOnce);
37270     }
37271     
37272     
37273     
37274     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37275     
37276     if (this.view && typeof(this.view.xtype) != 'undefined') {
37277         this.view.el = this.el.appendChild(document.createElement("div"));
37278         this.view = Roo.factory(this.view); 
37279         this.view.render  &&  this.view.render(false, '');  
37280     }
37281     
37282     
37283     this.fireEvent('render', this);
37284 };
37285
37286 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37287     
37288     tabTip : '',
37289     
37290     setRegion : function(region){
37291         this.region = region;
37292         this.setActiveClass(region && !this.background);
37293     },
37294     
37295     
37296     setActiveClass: function(state)
37297     {
37298         if(state){
37299            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37300            this.el.setStyle('position','relative');
37301         }else{
37302            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37303            this.el.setStyle('position', 'absolute');
37304         } 
37305     },
37306     
37307     /**
37308      * Returns the toolbar for this Panel if one was configured. 
37309      * @return {Roo.Toolbar} 
37310      */
37311     getToolbar : function(){
37312         return this.toolbar;
37313     },
37314     
37315     setActiveState : function(active)
37316     {
37317         this.active = active;
37318         this.setActiveClass(active);
37319         if(!active){
37320             if(this.fireEvent("deactivate", this) === false){
37321                 return false;
37322             }
37323             return true;
37324         }
37325         this.fireEvent("activate", this);
37326         return true;
37327     },
37328     /**
37329      * Updates this panel's element
37330      * @param {String} content The new content
37331      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37332     */
37333     setContent : function(content, loadScripts){
37334         this.el.update(content, loadScripts);
37335     },
37336
37337     ignoreResize : function(w, h){
37338         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37339             return true;
37340         }else{
37341             this.lastSize = {width: w, height: h};
37342             return false;
37343         }
37344     },
37345     /**
37346      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37347      * @return {Roo.UpdateManager} The UpdateManager
37348      */
37349     getUpdateManager : function(){
37350         return this.el.getUpdateManager();
37351     },
37352      /**
37353      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37354      * @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:
37355 <pre><code>
37356 panel.load({
37357     url: "your-url.php",
37358     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37359     callback: yourFunction,
37360     scope: yourObject, //(optional scope)
37361     discardUrl: false,
37362     nocache: false,
37363     text: "Loading...",
37364     timeout: 30,
37365     scripts: false
37366 });
37367 </code></pre>
37368      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37369      * 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.
37370      * @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}
37371      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37372      * @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.
37373      * @return {Roo.ContentPanel} this
37374      */
37375     load : function(){
37376         var um = this.el.getUpdateManager();
37377         um.update.apply(um, arguments);
37378         return this;
37379     },
37380
37381
37382     /**
37383      * 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.
37384      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37385      * @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)
37386      * @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)
37387      * @return {Roo.UpdateManager} The UpdateManager
37388      */
37389     setUrl : function(url, params, loadOnce){
37390         if(this.refreshDelegate){
37391             this.removeListener("activate", this.refreshDelegate);
37392         }
37393         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37394         this.on("activate", this.refreshDelegate);
37395         return this.el.getUpdateManager();
37396     },
37397     
37398     _handleRefresh : function(url, params, loadOnce){
37399         if(!loadOnce || !this.loaded){
37400             var updater = this.el.getUpdateManager();
37401             updater.update(url, params, this._setLoaded.createDelegate(this));
37402         }
37403     },
37404     
37405     _setLoaded : function(){
37406         this.loaded = true;
37407     }, 
37408     
37409     /**
37410      * Returns this panel's id
37411      * @return {String} 
37412      */
37413     getId : function(){
37414         return this.el.id;
37415     },
37416     
37417     /** 
37418      * Returns this panel's element - used by regiosn to add.
37419      * @return {Roo.Element} 
37420      */
37421     getEl : function(){
37422         return this.wrapEl || this.el;
37423     },
37424     
37425    
37426     
37427     adjustForComponents : function(width, height)
37428     {
37429         //Roo.log('adjustForComponents ');
37430         if(this.resizeEl != this.el){
37431             width -= this.el.getFrameWidth('lr');
37432             height -= this.el.getFrameWidth('tb');
37433         }
37434         if(this.toolbar){
37435             var te = this.toolbar.getEl();
37436             te.setWidth(width);
37437             height -= te.getHeight();
37438         }
37439         if(this.footer){
37440             var te = this.footer.getEl();
37441             te.setWidth(width);
37442             height -= te.getHeight();
37443         }
37444         
37445         
37446         if(this.adjustments){
37447             width += this.adjustments[0];
37448             height += this.adjustments[1];
37449         }
37450         return {"width": width, "height": height};
37451     },
37452     
37453     setSize : function(width, height){
37454         if(this.fitToFrame && !this.ignoreResize(width, height)){
37455             if(this.fitContainer && this.resizeEl != this.el){
37456                 this.el.setSize(width, height);
37457             }
37458             var size = this.adjustForComponents(width, height);
37459             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37460             this.fireEvent('resize', this, size.width, size.height);
37461         }
37462     },
37463     
37464     /**
37465      * Returns this panel's title
37466      * @return {String} 
37467      */
37468     getTitle : function(){
37469         
37470         if (typeof(this.title) != 'object') {
37471             return this.title;
37472         }
37473         
37474         var t = '';
37475         for (var k in this.title) {
37476             if (!this.title.hasOwnProperty(k)) {
37477                 continue;
37478             }
37479             
37480             if (k.indexOf('-') >= 0) {
37481                 var s = k.split('-');
37482                 for (var i = 0; i<s.length; i++) {
37483                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37484                 }
37485             } else {
37486                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37487             }
37488         }
37489         return t;
37490     },
37491     
37492     /**
37493      * Set this panel's title
37494      * @param {String} title
37495      */
37496     setTitle : function(title){
37497         this.title = title;
37498         if(this.region){
37499             this.region.updatePanelTitle(this, title);
37500         }
37501     },
37502     
37503     /**
37504      * Returns true is this panel was configured to be closable
37505      * @return {Boolean} 
37506      */
37507     isClosable : function(){
37508         return this.closable;
37509     },
37510     
37511     beforeSlide : function(){
37512         this.el.clip();
37513         this.resizeEl.clip();
37514     },
37515     
37516     afterSlide : function(){
37517         this.el.unclip();
37518         this.resizeEl.unclip();
37519     },
37520     
37521     /**
37522      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37523      *   Will fail silently if the {@link #setUrl} method has not been called.
37524      *   This does not activate the panel, just updates its content.
37525      */
37526     refresh : function(){
37527         if(this.refreshDelegate){
37528            this.loaded = false;
37529            this.refreshDelegate();
37530         }
37531     },
37532     
37533     /**
37534      * Destroys this panel
37535      */
37536     destroy : function(){
37537         this.el.removeAllListeners();
37538         var tempEl = document.createElement("span");
37539         tempEl.appendChild(this.el.dom);
37540         tempEl.innerHTML = "";
37541         this.el.remove();
37542         this.el = null;
37543     },
37544     
37545     /**
37546      * form - if the content panel contains a form - this is a reference to it.
37547      * @type {Roo.form.Form}
37548      */
37549     form : false,
37550     /**
37551      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37552      *    This contains a reference to it.
37553      * @type {Roo.View}
37554      */
37555     view : false,
37556     
37557       /**
37558      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37559      * <pre><code>
37560
37561 layout.addxtype({
37562        xtype : 'Form',
37563        items: [ .... ]
37564    }
37565 );
37566
37567 </code></pre>
37568      * @param {Object} cfg Xtype definition of item to add.
37569      */
37570     
37571     
37572     getChildContainer: function () {
37573         return this.getEl();
37574     }
37575     
37576     
37577     /*
37578         var  ret = new Roo.factory(cfg);
37579         return ret;
37580         
37581         
37582         // add form..
37583         if (cfg.xtype.match(/^Form$/)) {
37584             
37585             var el;
37586             //if (this.footer) {
37587             //    el = this.footer.container.insertSibling(false, 'before');
37588             //} else {
37589                 el = this.el.createChild();
37590             //}
37591
37592             this.form = new  Roo.form.Form(cfg);
37593             
37594             
37595             if ( this.form.allItems.length) {
37596                 this.form.render(el.dom);
37597             }
37598             return this.form;
37599         }
37600         // should only have one of theses..
37601         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37602             // views.. should not be just added - used named prop 'view''
37603             
37604             cfg.el = this.el.appendChild(document.createElement("div"));
37605             // factory?
37606             
37607             var ret = new Roo.factory(cfg);
37608              
37609              ret.render && ret.render(false, ''); // render blank..
37610             this.view = ret;
37611             return ret;
37612         }
37613         return false;
37614     }
37615     \*/
37616 });
37617  
37618 /**
37619  * @class Roo.bootstrap.panel.Grid
37620  * @extends Roo.bootstrap.panel.Content
37621  * @constructor
37622  * Create a new GridPanel.
37623  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37624  * @param {Object} config A the config object
37625   
37626  */
37627
37628
37629
37630 Roo.bootstrap.panel.Grid = function(config)
37631 {
37632     
37633       
37634     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37635         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37636
37637     config.el = this.wrapper;
37638     //this.el = this.wrapper;
37639     
37640       if (config.container) {
37641         // ctor'ed from a Border/panel.grid
37642         
37643         
37644         this.wrapper.setStyle("overflow", "hidden");
37645         this.wrapper.addClass('roo-grid-container');
37646
37647     }
37648     
37649     
37650     if(config.toolbar){
37651         var tool_el = this.wrapper.createChild();    
37652         this.toolbar = Roo.factory(config.toolbar);
37653         var ti = [];
37654         if (config.toolbar.items) {
37655             ti = config.toolbar.items ;
37656             delete config.toolbar.items ;
37657         }
37658         
37659         var nitems = [];
37660         this.toolbar.render(tool_el);
37661         for(var i =0;i < ti.length;i++) {
37662           //  Roo.log(['add child', items[i]]);
37663             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37664         }
37665         this.toolbar.items = nitems;
37666         
37667         delete config.toolbar;
37668     }
37669     
37670     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37671     config.grid.scrollBody = true;;
37672     config.grid.monitorWindowResize = false; // turn off autosizing
37673     config.grid.autoHeight = false;
37674     config.grid.autoWidth = false;
37675     
37676     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37677     
37678     if (config.background) {
37679         // render grid on panel activation (if panel background)
37680         this.on('activate', function(gp) {
37681             if (!gp.grid.rendered) {
37682                 gp.grid.render(this.wrapper);
37683                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37684             }
37685         });
37686             
37687     } else {
37688         this.grid.render(this.wrapper);
37689         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37690
37691     }
37692     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37693     // ??? needed ??? config.el = this.wrapper;
37694     
37695     
37696     
37697   
37698     // xtype created footer. - not sure if will work as we normally have to render first..
37699     if (this.footer && !this.footer.el && this.footer.xtype) {
37700         
37701         var ctr = this.grid.getView().getFooterPanel(true);
37702         this.footer.dataSource = this.grid.dataSource;
37703         this.footer = Roo.factory(this.footer, Roo);
37704         this.footer.render(ctr);
37705         
37706     }
37707     
37708     
37709     
37710     
37711      
37712 };
37713
37714 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37715     getId : function(){
37716         return this.grid.id;
37717     },
37718     
37719     /**
37720      * Returns the grid for this panel
37721      * @return {Roo.bootstrap.Table} 
37722      */
37723     getGrid : function(){
37724         return this.grid;    
37725     },
37726     
37727     setSize : function(width, height){
37728         if(!this.ignoreResize(width, height)){
37729             var grid = this.grid;
37730             var size = this.adjustForComponents(width, height);
37731             var gridel = grid.getGridEl();
37732             gridel.setSize(size.width, size.height);
37733             /*
37734             var thd = grid.getGridEl().select('thead',true).first();
37735             var tbd = grid.getGridEl().select('tbody', true).first();
37736             if (tbd) {
37737                 tbd.setSize(width, height - thd.getHeight());
37738             }
37739             */
37740             grid.autoSize();
37741         }
37742     },
37743      
37744     
37745     
37746     beforeSlide : function(){
37747         this.grid.getView().scroller.clip();
37748     },
37749     
37750     afterSlide : function(){
37751         this.grid.getView().scroller.unclip();
37752     },
37753     
37754     destroy : function(){
37755         this.grid.destroy();
37756         delete this.grid;
37757         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37758     }
37759 });
37760
37761 /**
37762  * @class Roo.bootstrap.panel.Nest
37763  * @extends Roo.bootstrap.panel.Content
37764  * @constructor
37765  * Create a new Panel, that can contain a layout.Border.
37766  * 
37767  * 
37768  * @param {Roo.BorderLayout} layout The layout for this panel
37769  * @param {String/Object} config A string to set only the title or a config object
37770  */
37771 Roo.bootstrap.panel.Nest = function(config)
37772 {
37773     // construct with only one argument..
37774     /* FIXME - implement nicer consturctors
37775     if (layout.layout) {
37776         config = layout;
37777         layout = config.layout;
37778         delete config.layout;
37779     }
37780     if (layout.xtype && !layout.getEl) {
37781         // then layout needs constructing..
37782         layout = Roo.factory(layout, Roo);
37783     }
37784     */
37785     
37786     config.el =  config.layout.getEl();
37787     
37788     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37789     
37790     config.layout.monitorWindowResize = false; // turn off autosizing
37791     this.layout = config.layout;
37792     this.layout.getEl().addClass("roo-layout-nested-layout");
37793     
37794     
37795     
37796     
37797 };
37798
37799 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37800
37801     setSize : function(width, height){
37802         if(!this.ignoreResize(width, height)){
37803             var size = this.adjustForComponents(width, height);
37804             var el = this.layout.getEl();
37805             if (size.height < 1) {
37806                 el.setWidth(size.width);   
37807             } else {
37808                 el.setSize(size.width, size.height);
37809             }
37810             var touch = el.dom.offsetWidth;
37811             this.layout.layout();
37812             // ie requires a double layout on the first pass
37813             if(Roo.isIE && !this.initialized){
37814                 this.initialized = true;
37815                 this.layout.layout();
37816             }
37817         }
37818     },
37819     
37820     // activate all subpanels if not currently active..
37821     
37822     setActiveState : function(active){
37823         this.active = active;
37824         this.setActiveClass(active);
37825         
37826         if(!active){
37827             this.fireEvent("deactivate", this);
37828             return;
37829         }
37830         
37831         this.fireEvent("activate", this);
37832         // not sure if this should happen before or after..
37833         if (!this.layout) {
37834             return; // should not happen..
37835         }
37836         var reg = false;
37837         for (var r in this.layout.regions) {
37838             reg = this.layout.getRegion(r);
37839             if (reg.getActivePanel()) {
37840                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37841                 reg.setActivePanel(reg.getActivePanel());
37842                 continue;
37843             }
37844             if (!reg.panels.length) {
37845                 continue;
37846             }
37847             reg.showPanel(reg.getPanel(0));
37848         }
37849         
37850         
37851         
37852         
37853     },
37854     
37855     /**
37856      * Returns the nested BorderLayout for this panel
37857      * @return {Roo.BorderLayout} 
37858      */
37859     getLayout : function(){
37860         return this.layout;
37861     },
37862     
37863      /**
37864      * Adds a xtype elements to the layout of the nested panel
37865      * <pre><code>
37866
37867 panel.addxtype({
37868        xtype : 'ContentPanel',
37869        region: 'west',
37870        items: [ .... ]
37871    }
37872 );
37873
37874 panel.addxtype({
37875         xtype : 'NestedLayoutPanel',
37876         region: 'west',
37877         layout: {
37878            center: { },
37879            west: { }   
37880         },
37881         items : [ ... list of content panels or nested layout panels.. ]
37882    }
37883 );
37884 </code></pre>
37885      * @param {Object} cfg Xtype definition of item to add.
37886      */
37887     addxtype : function(cfg) {
37888         return this.layout.addxtype(cfg);
37889     
37890     }
37891 });        /*
37892  * Based on:
37893  * Ext JS Library 1.1.1
37894  * Copyright(c) 2006-2007, Ext JS, LLC.
37895  *
37896  * Originally Released Under LGPL - original licence link has changed is not relivant.
37897  *
37898  * Fork - LGPL
37899  * <script type="text/javascript">
37900  */
37901 /**
37902  * @class Roo.TabPanel
37903  * @extends Roo.util.Observable
37904  * A lightweight tab container.
37905  * <br><br>
37906  * Usage:
37907  * <pre><code>
37908 // basic tabs 1, built from existing content
37909 var tabs = new Roo.TabPanel("tabs1");
37910 tabs.addTab("script", "View Script");
37911 tabs.addTab("markup", "View Markup");
37912 tabs.activate("script");
37913
37914 // more advanced tabs, built from javascript
37915 var jtabs = new Roo.TabPanel("jtabs");
37916 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37917
37918 // set up the UpdateManager
37919 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37920 var updater = tab2.getUpdateManager();
37921 updater.setDefaultUrl("ajax1.htm");
37922 tab2.on('activate', updater.refresh, updater, true);
37923
37924 // Use setUrl for Ajax loading
37925 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37926 tab3.setUrl("ajax2.htm", null, true);
37927
37928 // Disabled tab
37929 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37930 tab4.disable();
37931
37932 jtabs.activate("jtabs-1");
37933  * </code></pre>
37934  * @constructor
37935  * Create a new TabPanel.
37936  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37937  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37938  */
37939 Roo.bootstrap.panel.Tabs = function(config){
37940     /**
37941     * The container element for this TabPanel.
37942     * @type Roo.Element
37943     */
37944     this.el = Roo.get(config.el);
37945     delete config.el;
37946     if(config){
37947         if(typeof config == "boolean"){
37948             this.tabPosition = config ? "bottom" : "top";
37949         }else{
37950             Roo.apply(this, config);
37951         }
37952     }
37953     
37954     if(this.tabPosition == "bottom"){
37955         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37956         this.el.addClass("roo-tabs-bottom");
37957     }
37958     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37959     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37960     this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
37961     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37962     if(Roo.isIE){
37963         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37964     }
37965     if(this.tabPosition != "bottom"){
37966         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37967          * @type Roo.Element
37968          */
37969         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37970         this.el.addClass("roo-tabs-top");
37971     }
37972     this.items = [];
37973
37974     this.bodyEl.setStyle("position", "relative");
37975
37976     this.active = null;
37977     this.activateDelegate = this.activate.createDelegate(this);
37978
37979     this.addEvents({
37980         /**
37981          * @event tabchange
37982          * Fires when the active tab changes
37983          * @param {Roo.TabPanel} this
37984          * @param {Roo.TabPanelItem} activePanel The new active tab
37985          */
37986         "tabchange": true,
37987         /**
37988          * @event beforetabchange
37989          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37990          * @param {Roo.TabPanel} this
37991          * @param {Object} e Set cancel to true on this object to cancel the tab change
37992          * @param {Roo.TabPanelItem} tab The tab being changed to
37993          */
37994         "beforetabchange" : true
37995     });
37996
37997     Roo.EventManager.onWindowResize(this.onResize, this);
37998     this.cpad = this.el.getPadding("lr");
37999     this.hiddenCount = 0;
38000
38001
38002     // toolbar on the tabbar support...
38003     if (this.toolbar) {
38004         alert("no toolbar support yet");
38005         this.toolbar  = false;
38006         /*
38007         var tcfg = this.toolbar;
38008         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38009         this.toolbar = new Roo.Toolbar(tcfg);
38010         if (Roo.isSafari) {
38011             var tbl = tcfg.container.child('table', true);
38012             tbl.setAttribute('width', '100%');
38013         }
38014         */
38015         
38016     }
38017    
38018
38019
38020     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38021 };
38022
38023 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38024     /*
38025      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38026      */
38027     tabPosition : "top",
38028     /*
38029      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38030      */
38031     currentTabWidth : 0,
38032     /*
38033      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38034      */
38035     minTabWidth : 40,
38036     /*
38037      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38038      */
38039     maxTabWidth : 250,
38040     /*
38041      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38042      */
38043     preferredTabWidth : 175,
38044     /*
38045      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38046      */
38047     resizeTabs : false,
38048     /*
38049      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38050      */
38051     monitorResize : true,
38052     /*
38053      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38054      */
38055     toolbar : false,
38056
38057     /**
38058      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38059      * @param {String} id The id of the div to use <b>or create</b>
38060      * @param {String} text The text for the tab
38061      * @param {String} content (optional) Content to put in the TabPanelItem body
38062      * @param {Boolean} closable (optional) True to create a close icon on the tab
38063      * @return {Roo.TabPanelItem} The created TabPanelItem
38064      */
38065     addTab : function(id, text, content, closable, tpl)
38066     {
38067         var item = new Roo.bootstrap.panel.TabItem({
38068             panel: this,
38069             id : id,
38070             text : text,
38071             closable : closable,
38072             tpl : tpl
38073         });
38074         this.addTabItem(item);
38075         if(content){
38076             item.setContent(content);
38077         }
38078         return item;
38079     },
38080
38081     /**
38082      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38083      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38084      * @return {Roo.TabPanelItem}
38085      */
38086     getTab : function(id){
38087         return this.items[id];
38088     },
38089
38090     /**
38091      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38092      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38093      */
38094     hideTab : function(id){
38095         var t = this.items[id];
38096         if(!t.isHidden()){
38097            t.setHidden(true);
38098            this.hiddenCount++;
38099            this.autoSizeTabs();
38100         }
38101     },
38102
38103     /**
38104      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38105      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38106      */
38107     unhideTab : function(id){
38108         var t = this.items[id];
38109         if(t.isHidden()){
38110            t.setHidden(false);
38111            this.hiddenCount--;
38112            this.autoSizeTabs();
38113         }
38114     },
38115
38116     /**
38117      * Adds an existing {@link Roo.TabPanelItem}.
38118      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38119      */
38120     addTabItem : function(item)
38121     {
38122         this.items[item.id] = item;
38123         this.items.push(item);
38124         this.autoSizeTabs();
38125       //  if(this.resizeTabs){
38126     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38127   //         this.autoSizeTabs();
38128 //        }else{
38129 //            item.autoSize();
38130        // }
38131     },
38132
38133     /**
38134      * Removes a {@link Roo.TabPanelItem}.
38135      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38136      */
38137     removeTab : function(id){
38138         var items = this.items;
38139         var tab = items[id];
38140         if(!tab) { return; }
38141         var index = items.indexOf(tab);
38142         if(this.active == tab && items.length > 1){
38143             var newTab = this.getNextAvailable(index);
38144             if(newTab) {
38145                 newTab.activate();
38146             }
38147         }
38148         this.stripEl.dom.removeChild(tab.pnode.dom);
38149         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38150             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38151         }
38152         items.splice(index, 1);
38153         delete this.items[tab.id];
38154         tab.fireEvent("close", tab);
38155         tab.purgeListeners();
38156         this.autoSizeTabs();
38157     },
38158
38159     getNextAvailable : function(start){
38160         var items = this.items;
38161         var index = start;
38162         // look for a next tab that will slide over to
38163         // replace the one being removed
38164         while(index < items.length){
38165             var item = items[++index];
38166             if(item && !item.isHidden()){
38167                 return item;
38168             }
38169         }
38170         // if one isn't found select the previous tab (on the left)
38171         index = start;
38172         while(index >= 0){
38173             var item = items[--index];
38174             if(item && !item.isHidden()){
38175                 return item;
38176             }
38177         }
38178         return null;
38179     },
38180
38181     /**
38182      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38183      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38184      */
38185     disableTab : function(id){
38186         var tab = this.items[id];
38187         if(tab && this.active != tab){
38188             tab.disable();
38189         }
38190     },
38191
38192     /**
38193      * Enables a {@link Roo.TabPanelItem} that is disabled.
38194      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38195      */
38196     enableTab : function(id){
38197         var tab = this.items[id];
38198         tab.enable();
38199     },
38200
38201     /**
38202      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38203      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38204      * @return {Roo.TabPanelItem} The TabPanelItem.
38205      */
38206     activate : function(id)
38207     {
38208         var tab = this.items[id];
38209         if(!tab){
38210             return null;
38211         }
38212         if(tab == this.active || tab.disabled){
38213             return tab;
38214         }
38215         var e = {};
38216         this.fireEvent("beforetabchange", this, e, tab);
38217         if(e.cancel !== true && !tab.disabled){
38218             if(this.active){
38219                 this.active.hide();
38220             }
38221             this.active = this.items[id];
38222             this.active.show();
38223             this.fireEvent("tabchange", this, this.active);
38224         }
38225         return tab;
38226     },
38227
38228     /**
38229      * Gets the active {@link Roo.TabPanelItem}.
38230      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38231      */
38232     getActiveTab : function(){
38233         return this.active;
38234     },
38235
38236     /**
38237      * Updates the tab body element to fit the height of the container element
38238      * for overflow scrolling
38239      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38240      */
38241     syncHeight : function(targetHeight){
38242         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38243         var bm = this.bodyEl.getMargins();
38244         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38245         this.bodyEl.setHeight(newHeight);
38246         return newHeight;
38247     },
38248
38249     onResize : function(){
38250         if(this.monitorResize){
38251             this.autoSizeTabs();
38252         }
38253     },
38254
38255     /**
38256      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38257      */
38258     beginUpdate : function(){
38259         this.updating = true;
38260     },
38261
38262     /**
38263      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38264      */
38265     endUpdate : function(){
38266         this.updating = false;
38267         this.autoSizeTabs();
38268     },
38269
38270     /**
38271      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38272      */
38273     autoSizeTabs : function()
38274     {
38275         var count = this.items.length;
38276         var vcount = count - this.hiddenCount;
38277         
38278         if (vcount < 2) {
38279             this.stripEl.hide();
38280         } else {
38281             this.stripEl.show();
38282         }
38283         
38284         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38285             return;
38286         }
38287         
38288         
38289         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38290         var availWidth = Math.floor(w / vcount);
38291         var b = this.stripBody;
38292         if(b.getWidth() > w){
38293             var tabs = this.items;
38294             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38295             if(availWidth < this.minTabWidth){
38296                 /*if(!this.sleft){    // incomplete scrolling code
38297                     this.createScrollButtons();
38298                 }
38299                 this.showScroll();
38300                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38301             }
38302         }else{
38303             if(this.currentTabWidth < this.preferredTabWidth){
38304                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38305             }
38306         }
38307     },
38308
38309     /**
38310      * Returns the number of tabs in this TabPanel.
38311      * @return {Number}
38312      */
38313      getCount : function(){
38314          return this.items.length;
38315      },
38316
38317     /**
38318      * Resizes all the tabs to the passed width
38319      * @param {Number} The new width
38320      */
38321     setTabWidth : function(width){
38322         this.currentTabWidth = width;
38323         for(var i = 0, len = this.items.length; i < len; i++) {
38324                 if(!this.items[i].isHidden()) {
38325                 this.items[i].setWidth(width);
38326             }
38327         }
38328     },
38329
38330     /**
38331      * Destroys this TabPanel
38332      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38333      */
38334     destroy : function(removeEl){
38335         Roo.EventManager.removeResizeListener(this.onResize, this);
38336         for(var i = 0, len = this.items.length; i < len; i++){
38337             this.items[i].purgeListeners();
38338         }
38339         if(removeEl === true){
38340             this.el.update("");
38341             this.el.remove();
38342         }
38343     },
38344     
38345     createStrip : function(container)
38346     {
38347         var strip = document.createElement("nav");
38348         strip.className = Roo.bootstrap.version == 4 ?
38349             "navbar-light bg-light" : 
38350             "navbar navbar-default"; //"x-tabs-wrap";
38351         container.appendChild(strip);
38352         return strip;
38353     },
38354     
38355     createStripList : function(strip)
38356     {
38357         // div wrapper for retard IE
38358         // returns the "tr" element.
38359         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38360         //'<div class="x-tabs-strip-wrap">'+
38361           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38362           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38363         return strip.firstChild; //.firstChild.firstChild.firstChild;
38364     },
38365     createBody : function(container)
38366     {
38367         var body = document.createElement("div");
38368         Roo.id(body, "tab-body");
38369         //Roo.fly(body).addClass("x-tabs-body");
38370         Roo.fly(body).addClass("tab-content");
38371         container.appendChild(body);
38372         return body;
38373     },
38374     createItemBody :function(bodyEl, id){
38375         var body = Roo.getDom(id);
38376         if(!body){
38377             body = document.createElement("div");
38378             body.id = id;
38379         }
38380         //Roo.fly(body).addClass("x-tabs-item-body");
38381         Roo.fly(body).addClass("tab-pane");
38382          bodyEl.insertBefore(body, bodyEl.firstChild);
38383         return body;
38384     },
38385     /** @private */
38386     createStripElements :  function(stripEl, text, closable, tpl)
38387     {
38388         var td = document.createElement("li"); // was td..
38389         td.className = 'nav-item';
38390         
38391         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38392         
38393         
38394         stripEl.appendChild(td);
38395         /*if(closable){
38396             td.className = "x-tabs-closable";
38397             if(!this.closeTpl){
38398                 this.closeTpl = new Roo.Template(
38399                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38400                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38401                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38402                 );
38403             }
38404             var el = this.closeTpl.overwrite(td, {"text": text});
38405             var close = el.getElementsByTagName("div")[0];
38406             var inner = el.getElementsByTagName("em")[0];
38407             return {"el": el, "close": close, "inner": inner};
38408         } else {
38409         */
38410         // not sure what this is..
38411 //            if(!this.tabTpl){
38412                 //this.tabTpl = new Roo.Template(
38413                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38414                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38415                 //);
38416 //                this.tabTpl = new Roo.Template(
38417 //                   '<a href="#">' +
38418 //                   '<span unselectable="on"' +
38419 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38420 //                            ' >{text}</span></a>'
38421 //                );
38422 //                
38423 //            }
38424
38425
38426             var template = tpl || this.tabTpl || false;
38427             
38428             if(!template){
38429                 template =  new Roo.Template(
38430                         Roo.bootstrap.version == 4 ? 
38431                             (
38432                                 '<a class="nav-link" href="#" unselectable="on"' +
38433                                      (this.disableTooltips ? '' : ' title="{text}"') +
38434                                      ' >{text}</a>'
38435                             ) : (
38436                                 '<a class="nav-link" href="#">' +
38437                                 '<span unselectable="on"' +
38438                                          (this.disableTooltips ? '' : ' title="{text}"') +
38439                                     ' >{text}</span></a>'
38440                             )
38441                 );
38442             }
38443             
38444             switch (typeof(template)) {
38445                 case 'object' :
38446                     break;
38447                 case 'string' :
38448                     template = new Roo.Template(template);
38449                     break;
38450                 default :
38451                     break;
38452             }
38453             
38454             var el = template.overwrite(td, {"text": text});
38455             
38456             var inner = el.getElementsByTagName("span")[0];
38457             
38458             return {"el": el, "inner": inner};
38459             
38460     }
38461         
38462     
38463 });
38464
38465 /**
38466  * @class Roo.TabPanelItem
38467  * @extends Roo.util.Observable
38468  * Represents an individual item (tab plus body) in a TabPanel.
38469  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38470  * @param {String} id The id of this TabPanelItem
38471  * @param {String} text The text for the tab of this TabPanelItem
38472  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38473  */
38474 Roo.bootstrap.panel.TabItem = function(config){
38475     /**
38476      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38477      * @type Roo.TabPanel
38478      */
38479     this.tabPanel = config.panel;
38480     /**
38481      * The id for this TabPanelItem
38482      * @type String
38483      */
38484     this.id = config.id;
38485     /** @private */
38486     this.disabled = false;
38487     /** @private */
38488     this.text = config.text;
38489     /** @private */
38490     this.loaded = false;
38491     this.closable = config.closable;
38492
38493     /**
38494      * The body element for this TabPanelItem.
38495      * @type Roo.Element
38496      */
38497     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38498     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38499     this.bodyEl.setStyle("display", "block");
38500     this.bodyEl.setStyle("zoom", "1");
38501     //this.hideAction();
38502
38503     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38504     /** @private */
38505     this.el = Roo.get(els.el);
38506     this.inner = Roo.get(els.inner, true);
38507      this.textEl = Roo.bootstrap.version == 4 ?
38508         this.el : Roo.get(this.el.dom.firstChild, true);
38509
38510     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38511     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38512
38513     
38514 //    this.el.on("mousedown", this.onTabMouseDown, this);
38515     this.el.on("click", this.onTabClick, this);
38516     /** @private */
38517     if(config.closable){
38518         var c = Roo.get(els.close, true);
38519         c.dom.title = this.closeText;
38520         c.addClassOnOver("close-over");
38521         c.on("click", this.closeClick, this);
38522      }
38523
38524     this.addEvents({
38525          /**
38526          * @event activate
38527          * Fires when this tab becomes the active tab.
38528          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38529          * @param {Roo.TabPanelItem} this
38530          */
38531         "activate": true,
38532         /**
38533          * @event beforeclose
38534          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38535          * @param {Roo.TabPanelItem} this
38536          * @param {Object} e Set cancel to true on this object to cancel the close.
38537          */
38538         "beforeclose": true,
38539         /**
38540          * @event close
38541          * Fires when this tab is closed.
38542          * @param {Roo.TabPanelItem} this
38543          */
38544          "close": true,
38545         /**
38546          * @event deactivate
38547          * Fires when this tab is no longer the active tab.
38548          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38549          * @param {Roo.TabPanelItem} this
38550          */
38551          "deactivate" : true
38552     });
38553     this.hidden = false;
38554
38555     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38556 };
38557
38558 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38559            {
38560     purgeListeners : function(){
38561        Roo.util.Observable.prototype.purgeListeners.call(this);
38562        this.el.removeAllListeners();
38563     },
38564     /**
38565      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38566      */
38567     show : function(){
38568         this.status_node.addClass("active");
38569         this.showAction();
38570         if(Roo.isOpera){
38571             this.tabPanel.stripWrap.repaint();
38572         }
38573         this.fireEvent("activate", this.tabPanel, this);
38574     },
38575
38576     /**
38577      * Returns true if this tab is the active tab.
38578      * @return {Boolean}
38579      */
38580     isActive : function(){
38581         return this.tabPanel.getActiveTab() == this;
38582     },
38583
38584     /**
38585      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38586      */
38587     hide : function(){
38588         this.status_node.removeClass("active");
38589         this.hideAction();
38590         this.fireEvent("deactivate", this.tabPanel, this);
38591     },
38592
38593     hideAction : function(){
38594         this.bodyEl.hide();
38595         this.bodyEl.setStyle("position", "absolute");
38596         this.bodyEl.setLeft("-20000px");
38597         this.bodyEl.setTop("-20000px");
38598     },
38599
38600     showAction : function(){
38601         this.bodyEl.setStyle("position", "relative");
38602         this.bodyEl.setTop("");
38603         this.bodyEl.setLeft("");
38604         this.bodyEl.show();
38605     },
38606
38607     /**
38608      * Set the tooltip for the tab.
38609      * @param {String} tooltip The tab's tooltip
38610      */
38611     setTooltip : function(text){
38612         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38613             this.textEl.dom.qtip = text;
38614             this.textEl.dom.removeAttribute('title');
38615         }else{
38616             this.textEl.dom.title = text;
38617         }
38618     },
38619
38620     onTabClick : function(e){
38621         e.preventDefault();
38622         this.tabPanel.activate(this.id);
38623     },
38624
38625     onTabMouseDown : function(e){
38626         e.preventDefault();
38627         this.tabPanel.activate(this.id);
38628     },
38629 /*
38630     getWidth : function(){
38631         return this.inner.getWidth();
38632     },
38633
38634     setWidth : function(width){
38635         var iwidth = width - this.linode.getPadding("lr");
38636         this.inner.setWidth(iwidth);
38637         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38638         this.linode.setWidth(width);
38639     },
38640 */
38641     /**
38642      * Show or hide the tab
38643      * @param {Boolean} hidden True to hide or false to show.
38644      */
38645     setHidden : function(hidden){
38646         this.hidden = hidden;
38647         this.linode.setStyle("display", hidden ? "none" : "");
38648     },
38649
38650     /**
38651      * Returns true if this tab is "hidden"
38652      * @return {Boolean}
38653      */
38654     isHidden : function(){
38655         return this.hidden;
38656     },
38657
38658     /**
38659      * Returns the text for this tab
38660      * @return {String}
38661      */
38662     getText : function(){
38663         return this.text;
38664     },
38665     /*
38666     autoSize : function(){
38667         //this.el.beginMeasure();
38668         this.textEl.setWidth(1);
38669         /*
38670          *  #2804 [new] Tabs in Roojs
38671          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38672          */
38673         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38674         //this.el.endMeasure();
38675     //},
38676
38677     /**
38678      * Sets the text for the tab (Note: this also sets the tooltip text)
38679      * @param {String} text The tab's text and tooltip
38680      */
38681     setText : function(text){
38682         this.text = text;
38683         this.textEl.update(text);
38684         this.setTooltip(text);
38685         //if(!this.tabPanel.resizeTabs){
38686         //    this.autoSize();
38687         //}
38688     },
38689     /**
38690      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38691      */
38692     activate : function(){
38693         this.tabPanel.activate(this.id);
38694     },
38695
38696     /**
38697      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38698      */
38699     disable : function(){
38700         if(this.tabPanel.active != this){
38701             this.disabled = true;
38702             this.status_node.addClass("disabled");
38703         }
38704     },
38705
38706     /**
38707      * Enables this TabPanelItem if it was previously disabled.
38708      */
38709     enable : function(){
38710         this.disabled = false;
38711         this.status_node.removeClass("disabled");
38712     },
38713
38714     /**
38715      * Sets the content for this TabPanelItem.
38716      * @param {String} content The content
38717      * @param {Boolean} loadScripts true to look for and load scripts
38718      */
38719     setContent : function(content, loadScripts){
38720         this.bodyEl.update(content, loadScripts);
38721     },
38722
38723     /**
38724      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38725      * @return {Roo.UpdateManager} The UpdateManager
38726      */
38727     getUpdateManager : function(){
38728         return this.bodyEl.getUpdateManager();
38729     },
38730
38731     /**
38732      * Set a URL to be used to load the content for this TabPanelItem.
38733      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38734      * @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)
38735      * @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)
38736      * @return {Roo.UpdateManager} The UpdateManager
38737      */
38738     setUrl : function(url, params, loadOnce){
38739         if(this.refreshDelegate){
38740             this.un('activate', this.refreshDelegate);
38741         }
38742         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38743         this.on("activate", this.refreshDelegate);
38744         return this.bodyEl.getUpdateManager();
38745     },
38746
38747     /** @private */
38748     _handleRefresh : function(url, params, loadOnce){
38749         if(!loadOnce || !this.loaded){
38750             var updater = this.bodyEl.getUpdateManager();
38751             updater.update(url, params, this._setLoaded.createDelegate(this));
38752         }
38753     },
38754
38755     /**
38756      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38757      *   Will fail silently if the setUrl method has not been called.
38758      *   This does not activate the panel, just updates its content.
38759      */
38760     refresh : function(){
38761         if(this.refreshDelegate){
38762            this.loaded = false;
38763            this.refreshDelegate();
38764         }
38765     },
38766
38767     /** @private */
38768     _setLoaded : function(){
38769         this.loaded = true;
38770     },
38771
38772     /** @private */
38773     closeClick : function(e){
38774         var o = {};
38775         e.stopEvent();
38776         this.fireEvent("beforeclose", this, o);
38777         if(o.cancel !== true){
38778             this.tabPanel.removeTab(this.id);
38779         }
38780     },
38781     /**
38782      * The text displayed in the tooltip for the close icon.
38783      * @type String
38784      */
38785     closeText : "Close this tab"
38786 });
38787 /**
38788 *    This script refer to:
38789 *    Title: International Telephone Input
38790 *    Author: Jack O'Connor
38791 *    Code version:  v12.1.12
38792 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38793 **/
38794
38795 Roo.bootstrap.PhoneInputData = function() {
38796     var d = [
38797       [
38798         "Afghanistan (‫افغانستان‬‎)",
38799         "af",
38800         "93"
38801       ],
38802       [
38803         "Albania (Shqipëri)",
38804         "al",
38805         "355"
38806       ],
38807       [
38808         "Algeria (‫الجزائر‬‎)",
38809         "dz",
38810         "213"
38811       ],
38812       [
38813         "American Samoa",
38814         "as",
38815         "1684"
38816       ],
38817       [
38818         "Andorra",
38819         "ad",
38820         "376"
38821       ],
38822       [
38823         "Angola",
38824         "ao",
38825         "244"
38826       ],
38827       [
38828         "Anguilla",
38829         "ai",
38830         "1264"
38831       ],
38832       [
38833         "Antigua and Barbuda",
38834         "ag",
38835         "1268"
38836       ],
38837       [
38838         "Argentina",
38839         "ar",
38840         "54"
38841       ],
38842       [
38843         "Armenia (Հայաստան)",
38844         "am",
38845         "374"
38846       ],
38847       [
38848         "Aruba",
38849         "aw",
38850         "297"
38851       ],
38852       [
38853         "Australia",
38854         "au",
38855         "61",
38856         0
38857       ],
38858       [
38859         "Austria (Österreich)",
38860         "at",
38861         "43"
38862       ],
38863       [
38864         "Azerbaijan (Azərbaycan)",
38865         "az",
38866         "994"
38867       ],
38868       [
38869         "Bahamas",
38870         "bs",
38871         "1242"
38872       ],
38873       [
38874         "Bahrain (‫البحرين‬‎)",
38875         "bh",
38876         "973"
38877       ],
38878       [
38879         "Bangladesh (বাংলাদেশ)",
38880         "bd",
38881         "880"
38882       ],
38883       [
38884         "Barbados",
38885         "bb",
38886         "1246"
38887       ],
38888       [
38889         "Belarus (Беларусь)",
38890         "by",
38891         "375"
38892       ],
38893       [
38894         "Belgium (België)",
38895         "be",
38896         "32"
38897       ],
38898       [
38899         "Belize",
38900         "bz",
38901         "501"
38902       ],
38903       [
38904         "Benin (Bénin)",
38905         "bj",
38906         "229"
38907       ],
38908       [
38909         "Bermuda",
38910         "bm",
38911         "1441"
38912       ],
38913       [
38914         "Bhutan (འབྲུག)",
38915         "bt",
38916         "975"
38917       ],
38918       [
38919         "Bolivia",
38920         "bo",
38921         "591"
38922       ],
38923       [
38924         "Bosnia and Herzegovina (Босна и Херцеговина)",
38925         "ba",
38926         "387"
38927       ],
38928       [
38929         "Botswana",
38930         "bw",
38931         "267"
38932       ],
38933       [
38934         "Brazil (Brasil)",
38935         "br",
38936         "55"
38937       ],
38938       [
38939         "British Indian Ocean Territory",
38940         "io",
38941         "246"
38942       ],
38943       [
38944         "British Virgin Islands",
38945         "vg",
38946         "1284"
38947       ],
38948       [
38949         "Brunei",
38950         "bn",
38951         "673"
38952       ],
38953       [
38954         "Bulgaria (България)",
38955         "bg",
38956         "359"
38957       ],
38958       [
38959         "Burkina Faso",
38960         "bf",
38961         "226"
38962       ],
38963       [
38964         "Burundi (Uburundi)",
38965         "bi",
38966         "257"
38967       ],
38968       [
38969         "Cambodia (កម្ពុជា)",
38970         "kh",
38971         "855"
38972       ],
38973       [
38974         "Cameroon (Cameroun)",
38975         "cm",
38976         "237"
38977       ],
38978       [
38979         "Canada",
38980         "ca",
38981         "1",
38982         1,
38983         ["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"]
38984       ],
38985       [
38986         "Cape Verde (Kabu Verdi)",
38987         "cv",
38988         "238"
38989       ],
38990       [
38991         "Caribbean Netherlands",
38992         "bq",
38993         "599",
38994         1
38995       ],
38996       [
38997         "Cayman Islands",
38998         "ky",
38999         "1345"
39000       ],
39001       [
39002         "Central African Republic (République centrafricaine)",
39003         "cf",
39004         "236"
39005       ],
39006       [
39007         "Chad (Tchad)",
39008         "td",
39009         "235"
39010       ],
39011       [
39012         "Chile",
39013         "cl",
39014         "56"
39015       ],
39016       [
39017         "China (中国)",
39018         "cn",
39019         "86"
39020       ],
39021       [
39022         "Christmas Island",
39023         "cx",
39024         "61",
39025         2
39026       ],
39027       [
39028         "Cocos (Keeling) Islands",
39029         "cc",
39030         "61",
39031         1
39032       ],
39033       [
39034         "Colombia",
39035         "co",
39036         "57"
39037       ],
39038       [
39039         "Comoros (‫جزر القمر‬‎)",
39040         "km",
39041         "269"
39042       ],
39043       [
39044         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39045         "cd",
39046         "243"
39047       ],
39048       [
39049         "Congo (Republic) (Congo-Brazzaville)",
39050         "cg",
39051         "242"
39052       ],
39053       [
39054         "Cook Islands",
39055         "ck",
39056         "682"
39057       ],
39058       [
39059         "Costa Rica",
39060         "cr",
39061         "506"
39062       ],
39063       [
39064         "Côte d’Ivoire",
39065         "ci",
39066         "225"
39067       ],
39068       [
39069         "Croatia (Hrvatska)",
39070         "hr",
39071         "385"
39072       ],
39073       [
39074         "Cuba",
39075         "cu",
39076         "53"
39077       ],
39078       [
39079         "Curaçao",
39080         "cw",
39081         "599",
39082         0
39083       ],
39084       [
39085         "Cyprus (Κύπρος)",
39086         "cy",
39087         "357"
39088       ],
39089       [
39090         "Czech Republic (Česká republika)",
39091         "cz",
39092         "420"
39093       ],
39094       [
39095         "Denmark (Danmark)",
39096         "dk",
39097         "45"
39098       ],
39099       [
39100         "Djibouti",
39101         "dj",
39102         "253"
39103       ],
39104       [
39105         "Dominica",
39106         "dm",
39107         "1767"
39108       ],
39109       [
39110         "Dominican Republic (República Dominicana)",
39111         "do",
39112         "1",
39113         2,
39114         ["809", "829", "849"]
39115       ],
39116       [
39117         "Ecuador",
39118         "ec",
39119         "593"
39120       ],
39121       [
39122         "Egypt (‫مصر‬‎)",
39123         "eg",
39124         "20"
39125       ],
39126       [
39127         "El Salvador",
39128         "sv",
39129         "503"
39130       ],
39131       [
39132         "Equatorial Guinea (Guinea Ecuatorial)",
39133         "gq",
39134         "240"
39135       ],
39136       [
39137         "Eritrea",
39138         "er",
39139         "291"
39140       ],
39141       [
39142         "Estonia (Eesti)",
39143         "ee",
39144         "372"
39145       ],
39146       [
39147         "Ethiopia",
39148         "et",
39149         "251"
39150       ],
39151       [
39152         "Falkland Islands (Islas Malvinas)",
39153         "fk",
39154         "500"
39155       ],
39156       [
39157         "Faroe Islands (Føroyar)",
39158         "fo",
39159         "298"
39160       ],
39161       [
39162         "Fiji",
39163         "fj",
39164         "679"
39165       ],
39166       [
39167         "Finland (Suomi)",
39168         "fi",
39169         "358",
39170         0
39171       ],
39172       [
39173         "France",
39174         "fr",
39175         "33"
39176       ],
39177       [
39178         "French Guiana (Guyane française)",
39179         "gf",
39180         "594"
39181       ],
39182       [
39183         "French Polynesia (Polynésie française)",
39184         "pf",
39185         "689"
39186       ],
39187       [
39188         "Gabon",
39189         "ga",
39190         "241"
39191       ],
39192       [
39193         "Gambia",
39194         "gm",
39195         "220"
39196       ],
39197       [
39198         "Georgia (საქართველო)",
39199         "ge",
39200         "995"
39201       ],
39202       [
39203         "Germany (Deutschland)",
39204         "de",
39205         "49"
39206       ],
39207       [
39208         "Ghana (Gaana)",
39209         "gh",
39210         "233"
39211       ],
39212       [
39213         "Gibraltar",
39214         "gi",
39215         "350"
39216       ],
39217       [
39218         "Greece (Ελλάδα)",
39219         "gr",
39220         "30"
39221       ],
39222       [
39223         "Greenland (Kalaallit Nunaat)",
39224         "gl",
39225         "299"
39226       ],
39227       [
39228         "Grenada",
39229         "gd",
39230         "1473"
39231       ],
39232       [
39233         "Guadeloupe",
39234         "gp",
39235         "590",
39236         0
39237       ],
39238       [
39239         "Guam",
39240         "gu",
39241         "1671"
39242       ],
39243       [
39244         "Guatemala",
39245         "gt",
39246         "502"
39247       ],
39248       [
39249         "Guernsey",
39250         "gg",
39251         "44",
39252         1
39253       ],
39254       [
39255         "Guinea (Guinée)",
39256         "gn",
39257         "224"
39258       ],
39259       [
39260         "Guinea-Bissau (Guiné Bissau)",
39261         "gw",
39262         "245"
39263       ],
39264       [
39265         "Guyana",
39266         "gy",
39267         "592"
39268       ],
39269       [
39270         "Haiti",
39271         "ht",
39272         "509"
39273       ],
39274       [
39275         "Honduras",
39276         "hn",
39277         "504"
39278       ],
39279       [
39280         "Hong Kong (香港)",
39281         "hk",
39282         "852"
39283       ],
39284       [
39285         "Hungary (Magyarország)",
39286         "hu",
39287         "36"
39288       ],
39289       [
39290         "Iceland (Ísland)",
39291         "is",
39292         "354"
39293       ],
39294       [
39295         "India (भारत)",
39296         "in",
39297         "91"
39298       ],
39299       [
39300         "Indonesia",
39301         "id",
39302         "62"
39303       ],
39304       [
39305         "Iran (‫ایران‬‎)",
39306         "ir",
39307         "98"
39308       ],
39309       [
39310         "Iraq (‫العراق‬‎)",
39311         "iq",
39312         "964"
39313       ],
39314       [
39315         "Ireland",
39316         "ie",
39317         "353"
39318       ],
39319       [
39320         "Isle of Man",
39321         "im",
39322         "44",
39323         2
39324       ],
39325       [
39326         "Israel (‫ישראל‬‎)",
39327         "il",
39328         "972"
39329       ],
39330       [
39331         "Italy (Italia)",
39332         "it",
39333         "39",
39334         0
39335       ],
39336       [
39337         "Jamaica",
39338         "jm",
39339         "1876"
39340       ],
39341       [
39342         "Japan (日本)",
39343         "jp",
39344         "81"
39345       ],
39346       [
39347         "Jersey",
39348         "je",
39349         "44",
39350         3
39351       ],
39352       [
39353         "Jordan (‫الأردن‬‎)",
39354         "jo",
39355         "962"
39356       ],
39357       [
39358         "Kazakhstan (Казахстан)",
39359         "kz",
39360         "7",
39361         1
39362       ],
39363       [
39364         "Kenya",
39365         "ke",
39366         "254"
39367       ],
39368       [
39369         "Kiribati",
39370         "ki",
39371         "686"
39372       ],
39373       [
39374         "Kosovo",
39375         "xk",
39376         "383"
39377       ],
39378       [
39379         "Kuwait (‫الكويت‬‎)",
39380         "kw",
39381         "965"
39382       ],
39383       [
39384         "Kyrgyzstan (Кыргызстан)",
39385         "kg",
39386         "996"
39387       ],
39388       [
39389         "Laos (ລາວ)",
39390         "la",
39391         "856"
39392       ],
39393       [
39394         "Latvia (Latvija)",
39395         "lv",
39396         "371"
39397       ],
39398       [
39399         "Lebanon (‫لبنان‬‎)",
39400         "lb",
39401         "961"
39402       ],
39403       [
39404         "Lesotho",
39405         "ls",
39406         "266"
39407       ],
39408       [
39409         "Liberia",
39410         "lr",
39411         "231"
39412       ],
39413       [
39414         "Libya (‫ليبيا‬‎)",
39415         "ly",
39416         "218"
39417       ],
39418       [
39419         "Liechtenstein",
39420         "li",
39421         "423"
39422       ],
39423       [
39424         "Lithuania (Lietuva)",
39425         "lt",
39426         "370"
39427       ],
39428       [
39429         "Luxembourg",
39430         "lu",
39431         "352"
39432       ],
39433       [
39434         "Macau (澳門)",
39435         "mo",
39436         "853"
39437       ],
39438       [
39439         "Macedonia (FYROM) (Македонија)",
39440         "mk",
39441         "389"
39442       ],
39443       [
39444         "Madagascar (Madagasikara)",
39445         "mg",
39446         "261"
39447       ],
39448       [
39449         "Malawi",
39450         "mw",
39451         "265"
39452       ],
39453       [
39454         "Malaysia",
39455         "my",
39456         "60"
39457       ],
39458       [
39459         "Maldives",
39460         "mv",
39461         "960"
39462       ],
39463       [
39464         "Mali",
39465         "ml",
39466         "223"
39467       ],
39468       [
39469         "Malta",
39470         "mt",
39471         "356"
39472       ],
39473       [
39474         "Marshall Islands",
39475         "mh",
39476         "692"
39477       ],
39478       [
39479         "Martinique",
39480         "mq",
39481         "596"
39482       ],
39483       [
39484         "Mauritania (‫موريتانيا‬‎)",
39485         "mr",
39486         "222"
39487       ],
39488       [
39489         "Mauritius (Moris)",
39490         "mu",
39491         "230"
39492       ],
39493       [
39494         "Mayotte",
39495         "yt",
39496         "262",
39497         1
39498       ],
39499       [
39500         "Mexico (México)",
39501         "mx",
39502         "52"
39503       ],
39504       [
39505         "Micronesia",
39506         "fm",
39507         "691"
39508       ],
39509       [
39510         "Moldova (Republica Moldova)",
39511         "md",
39512         "373"
39513       ],
39514       [
39515         "Monaco",
39516         "mc",
39517         "377"
39518       ],
39519       [
39520         "Mongolia (Монгол)",
39521         "mn",
39522         "976"
39523       ],
39524       [
39525         "Montenegro (Crna Gora)",
39526         "me",
39527         "382"
39528       ],
39529       [
39530         "Montserrat",
39531         "ms",
39532         "1664"
39533       ],
39534       [
39535         "Morocco (‫المغرب‬‎)",
39536         "ma",
39537         "212",
39538         0
39539       ],
39540       [
39541         "Mozambique (Moçambique)",
39542         "mz",
39543         "258"
39544       ],
39545       [
39546         "Myanmar (Burma) (မြန်မာ)",
39547         "mm",
39548         "95"
39549       ],
39550       [
39551         "Namibia (Namibië)",
39552         "na",
39553         "264"
39554       ],
39555       [
39556         "Nauru",
39557         "nr",
39558         "674"
39559       ],
39560       [
39561         "Nepal (नेपाल)",
39562         "np",
39563         "977"
39564       ],
39565       [
39566         "Netherlands (Nederland)",
39567         "nl",
39568         "31"
39569       ],
39570       [
39571         "New Caledonia (Nouvelle-Calédonie)",
39572         "nc",
39573         "687"
39574       ],
39575       [
39576         "New Zealand",
39577         "nz",
39578         "64"
39579       ],
39580       [
39581         "Nicaragua",
39582         "ni",
39583         "505"
39584       ],
39585       [
39586         "Niger (Nijar)",
39587         "ne",
39588         "227"
39589       ],
39590       [
39591         "Nigeria",
39592         "ng",
39593         "234"
39594       ],
39595       [
39596         "Niue",
39597         "nu",
39598         "683"
39599       ],
39600       [
39601         "Norfolk Island",
39602         "nf",
39603         "672"
39604       ],
39605       [
39606         "North Korea (조선 민주주의 인민 공화국)",
39607         "kp",
39608         "850"
39609       ],
39610       [
39611         "Northern Mariana Islands",
39612         "mp",
39613         "1670"
39614       ],
39615       [
39616         "Norway (Norge)",
39617         "no",
39618         "47",
39619         0
39620       ],
39621       [
39622         "Oman (‫عُمان‬‎)",
39623         "om",
39624         "968"
39625       ],
39626       [
39627         "Pakistan (‫پاکستان‬‎)",
39628         "pk",
39629         "92"
39630       ],
39631       [
39632         "Palau",
39633         "pw",
39634         "680"
39635       ],
39636       [
39637         "Palestine (‫فلسطين‬‎)",
39638         "ps",
39639         "970"
39640       ],
39641       [
39642         "Panama (Panamá)",
39643         "pa",
39644         "507"
39645       ],
39646       [
39647         "Papua New Guinea",
39648         "pg",
39649         "675"
39650       ],
39651       [
39652         "Paraguay",
39653         "py",
39654         "595"
39655       ],
39656       [
39657         "Peru (Perú)",
39658         "pe",
39659         "51"
39660       ],
39661       [
39662         "Philippines",
39663         "ph",
39664         "63"
39665       ],
39666       [
39667         "Poland (Polska)",
39668         "pl",
39669         "48"
39670       ],
39671       [
39672         "Portugal",
39673         "pt",
39674         "351"
39675       ],
39676       [
39677         "Puerto Rico",
39678         "pr",
39679         "1",
39680         3,
39681         ["787", "939"]
39682       ],
39683       [
39684         "Qatar (‫قطر‬‎)",
39685         "qa",
39686         "974"
39687       ],
39688       [
39689         "Réunion (La Réunion)",
39690         "re",
39691         "262",
39692         0
39693       ],
39694       [
39695         "Romania (România)",
39696         "ro",
39697         "40"
39698       ],
39699       [
39700         "Russia (Россия)",
39701         "ru",
39702         "7",
39703         0
39704       ],
39705       [
39706         "Rwanda",
39707         "rw",
39708         "250"
39709       ],
39710       [
39711         "Saint Barthélemy",
39712         "bl",
39713         "590",
39714         1
39715       ],
39716       [
39717         "Saint Helena",
39718         "sh",
39719         "290"
39720       ],
39721       [
39722         "Saint Kitts and Nevis",
39723         "kn",
39724         "1869"
39725       ],
39726       [
39727         "Saint Lucia",
39728         "lc",
39729         "1758"
39730       ],
39731       [
39732         "Saint Martin (Saint-Martin (partie française))",
39733         "mf",
39734         "590",
39735         2
39736       ],
39737       [
39738         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39739         "pm",
39740         "508"
39741       ],
39742       [
39743         "Saint Vincent and the Grenadines",
39744         "vc",
39745         "1784"
39746       ],
39747       [
39748         "Samoa",
39749         "ws",
39750         "685"
39751       ],
39752       [
39753         "San Marino",
39754         "sm",
39755         "378"
39756       ],
39757       [
39758         "São Tomé and Príncipe (São Tomé e Príncipe)",
39759         "st",
39760         "239"
39761       ],
39762       [
39763         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39764         "sa",
39765         "966"
39766       ],
39767       [
39768         "Senegal (Sénégal)",
39769         "sn",
39770         "221"
39771       ],
39772       [
39773         "Serbia (Србија)",
39774         "rs",
39775         "381"
39776       ],
39777       [
39778         "Seychelles",
39779         "sc",
39780         "248"
39781       ],
39782       [
39783         "Sierra Leone",
39784         "sl",
39785         "232"
39786       ],
39787       [
39788         "Singapore",
39789         "sg",
39790         "65"
39791       ],
39792       [
39793         "Sint Maarten",
39794         "sx",
39795         "1721"
39796       ],
39797       [
39798         "Slovakia (Slovensko)",
39799         "sk",
39800         "421"
39801       ],
39802       [
39803         "Slovenia (Slovenija)",
39804         "si",
39805         "386"
39806       ],
39807       [
39808         "Solomon Islands",
39809         "sb",
39810         "677"
39811       ],
39812       [
39813         "Somalia (Soomaaliya)",
39814         "so",
39815         "252"
39816       ],
39817       [
39818         "South Africa",
39819         "za",
39820         "27"
39821       ],
39822       [
39823         "South Korea (대한민국)",
39824         "kr",
39825         "82"
39826       ],
39827       [
39828         "South Sudan (‫جنوب السودان‬‎)",
39829         "ss",
39830         "211"
39831       ],
39832       [
39833         "Spain (España)",
39834         "es",
39835         "34"
39836       ],
39837       [
39838         "Sri Lanka (ශ්‍රී ලංකාව)",
39839         "lk",
39840         "94"
39841       ],
39842       [
39843         "Sudan (‫السودان‬‎)",
39844         "sd",
39845         "249"
39846       ],
39847       [
39848         "Suriname",
39849         "sr",
39850         "597"
39851       ],
39852       [
39853         "Svalbard and Jan Mayen",
39854         "sj",
39855         "47",
39856         1
39857       ],
39858       [
39859         "Swaziland",
39860         "sz",
39861         "268"
39862       ],
39863       [
39864         "Sweden (Sverige)",
39865         "se",
39866         "46"
39867       ],
39868       [
39869         "Switzerland (Schweiz)",
39870         "ch",
39871         "41"
39872       ],
39873       [
39874         "Syria (‫سوريا‬‎)",
39875         "sy",
39876         "963"
39877       ],
39878       [
39879         "Taiwan (台灣)",
39880         "tw",
39881         "886"
39882       ],
39883       [
39884         "Tajikistan",
39885         "tj",
39886         "992"
39887       ],
39888       [
39889         "Tanzania",
39890         "tz",
39891         "255"
39892       ],
39893       [
39894         "Thailand (ไทย)",
39895         "th",
39896         "66"
39897       ],
39898       [
39899         "Timor-Leste",
39900         "tl",
39901         "670"
39902       ],
39903       [
39904         "Togo",
39905         "tg",
39906         "228"
39907       ],
39908       [
39909         "Tokelau",
39910         "tk",
39911         "690"
39912       ],
39913       [
39914         "Tonga",
39915         "to",
39916         "676"
39917       ],
39918       [
39919         "Trinidad and Tobago",
39920         "tt",
39921         "1868"
39922       ],
39923       [
39924         "Tunisia (‫تونس‬‎)",
39925         "tn",
39926         "216"
39927       ],
39928       [
39929         "Turkey (Türkiye)",
39930         "tr",
39931         "90"
39932       ],
39933       [
39934         "Turkmenistan",
39935         "tm",
39936         "993"
39937       ],
39938       [
39939         "Turks and Caicos Islands",
39940         "tc",
39941         "1649"
39942       ],
39943       [
39944         "Tuvalu",
39945         "tv",
39946         "688"
39947       ],
39948       [
39949         "U.S. Virgin Islands",
39950         "vi",
39951         "1340"
39952       ],
39953       [
39954         "Uganda",
39955         "ug",
39956         "256"
39957       ],
39958       [
39959         "Ukraine (Україна)",
39960         "ua",
39961         "380"
39962       ],
39963       [
39964         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39965         "ae",
39966         "971"
39967       ],
39968       [
39969         "United Kingdom",
39970         "gb",
39971         "44",
39972         0
39973       ],
39974       [
39975         "United States",
39976         "us",
39977         "1",
39978         0
39979       ],
39980       [
39981         "Uruguay",
39982         "uy",
39983         "598"
39984       ],
39985       [
39986         "Uzbekistan (Oʻzbekiston)",
39987         "uz",
39988         "998"
39989       ],
39990       [
39991         "Vanuatu",
39992         "vu",
39993         "678"
39994       ],
39995       [
39996         "Vatican City (Città del Vaticano)",
39997         "va",
39998         "39",
39999         1
40000       ],
40001       [
40002         "Venezuela",
40003         "ve",
40004         "58"
40005       ],
40006       [
40007         "Vietnam (Việt Nam)",
40008         "vn",
40009         "84"
40010       ],
40011       [
40012         "Wallis and Futuna (Wallis-et-Futuna)",
40013         "wf",
40014         "681"
40015       ],
40016       [
40017         "Western Sahara (‫الصحراء الغربية‬‎)",
40018         "eh",
40019         "212",
40020         1
40021       ],
40022       [
40023         "Yemen (‫اليمن‬‎)",
40024         "ye",
40025         "967"
40026       ],
40027       [
40028         "Zambia",
40029         "zm",
40030         "260"
40031       ],
40032       [
40033         "Zimbabwe",
40034         "zw",
40035         "263"
40036       ],
40037       [
40038         "Åland Islands",
40039         "ax",
40040         "358",
40041         1
40042       ]
40043   ];
40044   
40045   return d;
40046 }/**
40047 *    This script refer to:
40048 *    Title: International Telephone Input
40049 *    Author: Jack O'Connor
40050 *    Code version:  v12.1.12
40051 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40052 **/
40053
40054 /**
40055  * @class Roo.bootstrap.PhoneInput
40056  * @extends Roo.bootstrap.TriggerField
40057  * An input with International dial-code selection
40058  
40059  * @cfg {String} defaultDialCode default '+852'
40060  * @cfg {Array} preferedCountries default []
40061   
40062  * @constructor
40063  * Create a new PhoneInput.
40064  * @param {Object} config Configuration options
40065  */
40066
40067 Roo.bootstrap.PhoneInput = function(config) {
40068     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40069 };
40070
40071 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40072         
40073         listWidth: undefined,
40074         
40075         selectedClass: 'active',
40076         
40077         invalidClass : "has-warning",
40078         
40079         validClass: 'has-success',
40080         
40081         allowed: '0123456789',
40082         
40083         max_length: 15,
40084         
40085         /**
40086          * @cfg {String} defaultDialCode The default dial code when initializing the input
40087          */
40088         defaultDialCode: '+852',
40089         
40090         /**
40091          * @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
40092          */
40093         preferedCountries: false,
40094         
40095         getAutoCreate : function()
40096         {
40097             var data = Roo.bootstrap.PhoneInputData();
40098             var align = this.labelAlign || this.parentLabelAlign();
40099             var id = Roo.id();
40100             
40101             this.allCountries = [];
40102             this.dialCodeMapping = [];
40103             
40104             for (var i = 0; i < data.length; i++) {
40105               var c = data[i];
40106               this.allCountries[i] = {
40107                 name: c[0],
40108                 iso2: c[1],
40109                 dialCode: c[2],
40110                 priority: c[3] || 0,
40111                 areaCodes: c[4] || null
40112               };
40113               this.dialCodeMapping[c[2]] = {
40114                   name: c[0],
40115                   iso2: c[1],
40116                   priority: c[3] || 0,
40117                   areaCodes: c[4] || null
40118               };
40119             }
40120             
40121             var cfg = {
40122                 cls: 'form-group',
40123                 cn: []
40124             };
40125             
40126             var input =  {
40127                 tag: 'input',
40128                 id : id,
40129                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40130                 maxlength: this.max_length,
40131                 cls : 'form-control tel-input',
40132                 autocomplete: 'new-password'
40133             };
40134             
40135             var hiddenInput = {
40136                 tag: 'input',
40137                 type: 'hidden',
40138                 cls: 'hidden-tel-input'
40139             };
40140             
40141             if (this.name) {
40142                 hiddenInput.name = this.name;
40143             }
40144             
40145             if (this.disabled) {
40146                 input.disabled = true;
40147             }
40148             
40149             var flag_container = {
40150                 tag: 'div',
40151                 cls: 'flag-box',
40152                 cn: [
40153                     {
40154                         tag: 'div',
40155                         cls: 'flag'
40156                     },
40157                     {
40158                         tag: 'div',
40159                         cls: 'caret'
40160                     }
40161                 ]
40162             };
40163             
40164             var box = {
40165                 tag: 'div',
40166                 cls: this.hasFeedback ? 'has-feedback' : '',
40167                 cn: [
40168                     hiddenInput,
40169                     input,
40170                     {
40171                         tag: 'input',
40172                         cls: 'dial-code-holder',
40173                         disabled: true
40174                     }
40175                 ]
40176             };
40177             
40178             var container = {
40179                 cls: 'roo-select2-container input-group',
40180                 cn: [
40181                     flag_container,
40182                     box
40183                 ]
40184             };
40185             
40186             if (this.fieldLabel.length) {
40187                 var indicator = {
40188                     tag: 'i',
40189                     tooltip: 'This field is required'
40190                 };
40191                 
40192                 var label = {
40193                     tag: 'label',
40194                     'for':  id,
40195                     cls: 'control-label',
40196                     cn: []
40197                 };
40198                 
40199                 var label_text = {
40200                     tag: 'span',
40201                     html: this.fieldLabel
40202                 };
40203                 
40204                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40205                 label.cn = [
40206                     indicator,
40207                     label_text
40208                 ];
40209                 
40210                 if(this.indicatorpos == 'right') {
40211                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40212                     label.cn = [
40213                         label_text,
40214                         indicator
40215                     ];
40216                 }
40217                 
40218                 if(align == 'left') {
40219                     container = {
40220                         tag: 'div',
40221                         cn: [
40222                             container
40223                         ]
40224                     };
40225                     
40226                     if(this.labelWidth > 12){
40227                         label.style = "width: " + this.labelWidth + 'px';
40228                     }
40229                     if(this.labelWidth < 13 && this.labelmd == 0){
40230                         this.labelmd = this.labelWidth;
40231                     }
40232                     if(this.labellg > 0){
40233                         label.cls += ' col-lg-' + this.labellg;
40234                         input.cls += ' col-lg-' + (12 - this.labellg);
40235                     }
40236                     if(this.labelmd > 0){
40237                         label.cls += ' col-md-' + this.labelmd;
40238                         container.cls += ' col-md-' + (12 - this.labelmd);
40239                     }
40240                     if(this.labelsm > 0){
40241                         label.cls += ' col-sm-' + this.labelsm;
40242                         container.cls += ' col-sm-' + (12 - this.labelsm);
40243                     }
40244                     if(this.labelxs > 0){
40245                         label.cls += ' col-xs-' + this.labelxs;
40246                         container.cls += ' col-xs-' + (12 - this.labelxs);
40247                     }
40248                 }
40249             }
40250             
40251             cfg.cn = [
40252                 label,
40253                 container
40254             ];
40255             
40256             var settings = this;
40257             
40258             ['xs','sm','md','lg'].map(function(size){
40259                 if (settings[size]) {
40260                     cfg.cls += ' col-' + size + '-' + settings[size];
40261                 }
40262             });
40263             
40264             this.store = new Roo.data.Store({
40265                 proxy : new Roo.data.MemoryProxy({}),
40266                 reader : new Roo.data.JsonReader({
40267                     fields : [
40268                         {
40269                             'name' : 'name',
40270                             'type' : 'string'
40271                         },
40272                         {
40273                             'name' : 'iso2',
40274                             'type' : 'string'
40275                         },
40276                         {
40277                             'name' : 'dialCode',
40278                             'type' : 'string'
40279                         },
40280                         {
40281                             'name' : 'priority',
40282                             'type' : 'string'
40283                         },
40284                         {
40285                             'name' : 'areaCodes',
40286                             'type' : 'string'
40287                         }
40288                     ]
40289                 })
40290             });
40291             
40292             if(!this.preferedCountries) {
40293                 this.preferedCountries = [
40294                     'hk',
40295                     'gb',
40296                     'us'
40297                 ];
40298             }
40299             
40300             var p = this.preferedCountries.reverse();
40301             
40302             if(p) {
40303                 for (var i = 0; i < p.length; i++) {
40304                     for (var j = 0; j < this.allCountries.length; j++) {
40305                         if(this.allCountries[j].iso2 == p[i]) {
40306                             var t = this.allCountries[j];
40307                             this.allCountries.splice(j,1);
40308                             this.allCountries.unshift(t);
40309                         }
40310                     } 
40311                 }
40312             }
40313             
40314             this.store.proxy.data = {
40315                 success: true,
40316                 data: this.allCountries
40317             };
40318             
40319             return cfg;
40320         },
40321         
40322         initEvents : function()
40323         {
40324             this.createList();
40325             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40326             
40327             this.indicator = this.indicatorEl();
40328             this.flag = this.flagEl();
40329             this.dialCodeHolder = this.dialCodeHolderEl();
40330             
40331             this.trigger = this.el.select('div.flag-box',true).first();
40332             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40333             
40334             var _this = this;
40335             
40336             (function(){
40337                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40338                 _this.list.setWidth(lw);
40339             }).defer(100);
40340             
40341             this.list.on('mouseover', this.onViewOver, this);
40342             this.list.on('mousemove', this.onViewMove, this);
40343             this.inputEl().on("keyup", this.onKeyUp, this);
40344             this.inputEl().on("keypress", this.onKeyPress, this);
40345             
40346             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40347
40348             this.view = new Roo.View(this.list, this.tpl, {
40349                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40350             });
40351             
40352             this.view.on('click', this.onViewClick, this);
40353             this.setValue(this.defaultDialCode);
40354         },
40355         
40356         onTriggerClick : function(e)
40357         {
40358             Roo.log('trigger click');
40359             if(this.disabled){
40360                 return;
40361             }
40362             
40363             if(this.isExpanded()){
40364                 this.collapse();
40365                 this.hasFocus = false;
40366             }else {
40367                 this.store.load({});
40368                 this.hasFocus = true;
40369                 this.expand();
40370             }
40371         },
40372         
40373         isExpanded : function()
40374         {
40375             return this.list.isVisible();
40376         },
40377         
40378         collapse : function()
40379         {
40380             if(!this.isExpanded()){
40381                 return;
40382             }
40383             this.list.hide();
40384             Roo.get(document).un('mousedown', this.collapseIf, this);
40385             Roo.get(document).un('mousewheel', this.collapseIf, this);
40386             this.fireEvent('collapse', this);
40387             this.validate();
40388         },
40389         
40390         expand : function()
40391         {
40392             Roo.log('expand');
40393
40394             if(this.isExpanded() || !this.hasFocus){
40395                 return;
40396             }
40397             
40398             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40399             this.list.setWidth(lw);
40400             
40401             this.list.show();
40402             this.restrictHeight();
40403             
40404             Roo.get(document).on('mousedown', this.collapseIf, this);
40405             Roo.get(document).on('mousewheel', this.collapseIf, this);
40406             
40407             this.fireEvent('expand', this);
40408         },
40409         
40410         restrictHeight : function()
40411         {
40412             this.list.alignTo(this.inputEl(), this.listAlign);
40413             this.list.alignTo(this.inputEl(), this.listAlign);
40414         },
40415         
40416         onViewOver : function(e, t)
40417         {
40418             if(this.inKeyMode){
40419                 return;
40420             }
40421             var item = this.view.findItemFromChild(t);
40422             
40423             if(item){
40424                 var index = this.view.indexOf(item);
40425                 this.select(index, false);
40426             }
40427         },
40428
40429         // private
40430         onViewClick : function(view, doFocus, el, e)
40431         {
40432             var index = this.view.getSelectedIndexes()[0];
40433             
40434             var r = this.store.getAt(index);
40435             
40436             if(r){
40437                 this.onSelect(r, index);
40438             }
40439             if(doFocus !== false && !this.blockFocus){
40440                 this.inputEl().focus();
40441             }
40442         },
40443         
40444         onViewMove : function(e, t)
40445         {
40446             this.inKeyMode = false;
40447         },
40448         
40449         select : function(index, scrollIntoView)
40450         {
40451             this.selectedIndex = index;
40452             this.view.select(index);
40453             if(scrollIntoView !== false){
40454                 var el = this.view.getNode(index);
40455                 if(el){
40456                     this.list.scrollChildIntoView(el, false);
40457                 }
40458             }
40459         },
40460         
40461         createList : function()
40462         {
40463             this.list = Roo.get(document.body).createChild({
40464                 tag: 'ul',
40465                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40466                 style: 'display:none'
40467             });
40468             
40469             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40470         },
40471         
40472         collapseIf : function(e)
40473         {
40474             var in_combo  = e.within(this.el);
40475             var in_list =  e.within(this.list);
40476             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40477             
40478             if (in_combo || in_list || is_list) {
40479                 return;
40480             }
40481             this.collapse();
40482         },
40483         
40484         onSelect : function(record, index)
40485         {
40486             if(this.fireEvent('beforeselect', this, record, index) !== false){
40487                 
40488                 this.setFlagClass(record.data.iso2);
40489                 this.setDialCode(record.data.dialCode);
40490                 this.hasFocus = false;
40491                 this.collapse();
40492                 this.fireEvent('select', this, record, index);
40493             }
40494         },
40495         
40496         flagEl : function()
40497         {
40498             var flag = this.el.select('div.flag',true).first();
40499             if(!flag){
40500                 return false;
40501             }
40502             return flag;
40503         },
40504         
40505         dialCodeHolderEl : function()
40506         {
40507             var d = this.el.select('input.dial-code-holder',true).first();
40508             if(!d){
40509                 return false;
40510             }
40511             return d;
40512         },
40513         
40514         setDialCode : function(v)
40515         {
40516             this.dialCodeHolder.dom.value = '+'+v;
40517         },
40518         
40519         setFlagClass : function(n)
40520         {
40521             this.flag.dom.className = 'flag '+n;
40522         },
40523         
40524         getValue : function()
40525         {
40526             var v = this.inputEl().getValue();
40527             if(this.dialCodeHolder) {
40528                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40529             }
40530             return v;
40531         },
40532         
40533         setValue : function(v)
40534         {
40535             var d = this.getDialCode(v);
40536             
40537             //invalid dial code
40538             if(v.length == 0 || !d || d.length == 0) {
40539                 if(this.rendered){
40540                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40541                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40542                 }
40543                 return;
40544             }
40545             
40546             //valid dial code
40547             this.setFlagClass(this.dialCodeMapping[d].iso2);
40548             this.setDialCode(d);
40549             this.inputEl().dom.value = v.replace('+'+d,'');
40550             this.hiddenEl().dom.value = this.getValue();
40551             
40552             this.validate();
40553         },
40554         
40555         getDialCode : function(v)
40556         {
40557             v = v ||  '';
40558             
40559             if (v.length == 0) {
40560                 return this.dialCodeHolder.dom.value;
40561             }
40562             
40563             var dialCode = "";
40564             if (v.charAt(0) != "+") {
40565                 return false;
40566             }
40567             var numericChars = "";
40568             for (var i = 1; i < v.length; i++) {
40569               var c = v.charAt(i);
40570               if (!isNaN(c)) {
40571                 numericChars += c;
40572                 if (this.dialCodeMapping[numericChars]) {
40573                   dialCode = v.substr(1, i);
40574                 }
40575                 if (numericChars.length == 4) {
40576                   break;
40577                 }
40578               }
40579             }
40580             return dialCode;
40581         },
40582         
40583         reset : function()
40584         {
40585             this.setValue(this.defaultDialCode);
40586             this.validate();
40587         },
40588         
40589         hiddenEl : function()
40590         {
40591             return this.el.select('input.hidden-tel-input',true).first();
40592         },
40593         
40594         // after setting val
40595         onKeyUp : function(e){
40596             this.setValue(this.getValue());
40597         },
40598         
40599         onKeyPress : function(e){
40600             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40601                 e.stopEvent();
40602             }
40603         }
40604         
40605 });
40606 /**
40607  * @class Roo.bootstrap.MoneyField
40608  * @extends Roo.bootstrap.ComboBox
40609  * Bootstrap MoneyField class
40610  * 
40611  * @constructor
40612  * Create a new MoneyField.
40613  * @param {Object} config Configuration options
40614  */
40615
40616 Roo.bootstrap.MoneyField = function(config) {
40617     
40618     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40619     
40620 };
40621
40622 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40623     
40624     /**
40625      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40626      */
40627     allowDecimals : true,
40628     /**
40629      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40630      */
40631     decimalSeparator : ".",
40632     /**
40633      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40634      */
40635     decimalPrecision : 0,
40636     /**
40637      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40638      */
40639     allowNegative : true,
40640     /**
40641      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40642      */
40643     allowZero: true,
40644     /**
40645      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40646      */
40647     minValue : Number.NEGATIVE_INFINITY,
40648     /**
40649      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40650      */
40651     maxValue : Number.MAX_VALUE,
40652     /**
40653      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40654      */
40655     minText : "The minimum value for this field is {0}",
40656     /**
40657      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40658      */
40659     maxText : "The maximum value for this field is {0}",
40660     /**
40661      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40662      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40663      */
40664     nanText : "{0} is not a valid number",
40665     /**
40666      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40667      */
40668     castInt : true,
40669     /**
40670      * @cfg {String} defaults currency of the MoneyField
40671      * value should be in lkey
40672      */
40673     defaultCurrency : false,
40674     /**
40675      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40676      */
40677     thousandsDelimiter : false,
40678     /**
40679      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40680      */
40681     max_length: false,
40682     
40683     inputlg : 9,
40684     inputmd : 9,
40685     inputsm : 9,
40686     inputxs : 6,
40687     
40688     store : false,
40689     
40690     getAutoCreate : function()
40691     {
40692         var align = this.labelAlign || this.parentLabelAlign();
40693         
40694         var id = Roo.id();
40695
40696         var cfg = {
40697             cls: 'form-group',
40698             cn: []
40699         };
40700
40701         var input =  {
40702             tag: 'input',
40703             id : id,
40704             cls : 'form-control roo-money-amount-input',
40705             autocomplete: 'new-password'
40706         };
40707         
40708         var hiddenInput = {
40709             tag: 'input',
40710             type: 'hidden',
40711             id: Roo.id(),
40712             cls: 'hidden-number-input'
40713         };
40714         
40715         if(this.max_length) {
40716             input.maxlength = this.max_length; 
40717         }
40718         
40719         if (this.name) {
40720             hiddenInput.name = this.name;
40721         }
40722
40723         if (this.disabled) {
40724             input.disabled = true;
40725         }
40726
40727         var clg = 12 - this.inputlg;
40728         var cmd = 12 - this.inputmd;
40729         var csm = 12 - this.inputsm;
40730         var cxs = 12 - this.inputxs;
40731         
40732         var container = {
40733             tag : 'div',
40734             cls : 'row roo-money-field',
40735             cn : [
40736                 {
40737                     tag : 'div',
40738                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40739                     cn : [
40740                         {
40741                             tag : 'div',
40742                             cls: 'roo-select2-container input-group',
40743                             cn: [
40744                                 {
40745                                     tag : 'input',
40746                                     cls : 'form-control roo-money-currency-input',
40747                                     autocomplete: 'new-password',
40748                                     readOnly : 1,
40749                                     name : this.currencyName
40750                                 },
40751                                 {
40752                                     tag :'span',
40753                                     cls : 'input-group-addon',
40754                                     cn : [
40755                                         {
40756                                             tag: 'span',
40757                                             cls: 'caret'
40758                                         }
40759                                     ]
40760                                 }
40761                             ]
40762                         }
40763                     ]
40764                 },
40765                 {
40766                     tag : 'div',
40767                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40768                     cn : [
40769                         {
40770                             tag: 'div',
40771                             cls: this.hasFeedback ? 'has-feedback' : '',
40772                             cn: [
40773                                 input
40774                             ]
40775                         }
40776                     ]
40777                 }
40778             ]
40779             
40780         };
40781         
40782         if (this.fieldLabel.length) {
40783             var indicator = {
40784                 tag: 'i',
40785                 tooltip: 'This field is required'
40786             };
40787
40788             var label = {
40789                 tag: 'label',
40790                 'for':  id,
40791                 cls: 'control-label',
40792                 cn: []
40793             };
40794
40795             var label_text = {
40796                 tag: 'span',
40797                 html: this.fieldLabel
40798             };
40799
40800             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40801             label.cn = [
40802                 indicator,
40803                 label_text
40804             ];
40805
40806             if(this.indicatorpos == 'right') {
40807                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40808                 label.cn = [
40809                     label_text,
40810                     indicator
40811                 ];
40812             }
40813
40814             if(align == 'left') {
40815                 container = {
40816                     tag: 'div',
40817                     cn: [
40818                         container
40819                     ]
40820                 };
40821
40822                 if(this.labelWidth > 12){
40823                     label.style = "width: " + this.labelWidth + 'px';
40824                 }
40825                 if(this.labelWidth < 13 && this.labelmd == 0){
40826                     this.labelmd = this.labelWidth;
40827                 }
40828                 if(this.labellg > 0){
40829                     label.cls += ' col-lg-' + this.labellg;
40830                     input.cls += ' col-lg-' + (12 - this.labellg);
40831                 }
40832                 if(this.labelmd > 0){
40833                     label.cls += ' col-md-' + this.labelmd;
40834                     container.cls += ' col-md-' + (12 - this.labelmd);
40835                 }
40836                 if(this.labelsm > 0){
40837                     label.cls += ' col-sm-' + this.labelsm;
40838                     container.cls += ' col-sm-' + (12 - this.labelsm);
40839                 }
40840                 if(this.labelxs > 0){
40841                     label.cls += ' col-xs-' + this.labelxs;
40842                     container.cls += ' col-xs-' + (12 - this.labelxs);
40843                 }
40844             }
40845         }
40846
40847         cfg.cn = [
40848             label,
40849             container,
40850             hiddenInput
40851         ];
40852         
40853         var settings = this;
40854
40855         ['xs','sm','md','lg'].map(function(size){
40856             if (settings[size]) {
40857                 cfg.cls += ' col-' + size + '-' + settings[size];
40858             }
40859         });
40860         
40861         return cfg;
40862     },
40863     
40864     initEvents : function()
40865     {
40866         this.indicator = this.indicatorEl();
40867         
40868         this.initCurrencyEvent();
40869         
40870         this.initNumberEvent();
40871     },
40872     
40873     initCurrencyEvent : function()
40874     {
40875         if (!this.store) {
40876             throw "can not find store for combo";
40877         }
40878         
40879         this.store = Roo.factory(this.store, Roo.data);
40880         this.store.parent = this;
40881         
40882         this.createList();
40883         
40884         this.triggerEl = this.el.select('.input-group-addon', true).first();
40885         
40886         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40887         
40888         var _this = this;
40889         
40890         (function(){
40891             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40892             _this.list.setWidth(lw);
40893         }).defer(100);
40894         
40895         this.list.on('mouseover', this.onViewOver, this);
40896         this.list.on('mousemove', this.onViewMove, this);
40897         this.list.on('scroll', this.onViewScroll, this);
40898         
40899         if(!this.tpl){
40900             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40901         }
40902         
40903         this.view = new Roo.View(this.list, this.tpl, {
40904             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40905         });
40906         
40907         this.view.on('click', this.onViewClick, this);
40908         
40909         this.store.on('beforeload', this.onBeforeLoad, this);
40910         this.store.on('load', this.onLoad, this);
40911         this.store.on('loadexception', this.onLoadException, this);
40912         
40913         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40914             "up" : function(e){
40915                 this.inKeyMode = true;
40916                 this.selectPrev();
40917             },
40918
40919             "down" : function(e){
40920                 if(!this.isExpanded()){
40921                     this.onTriggerClick();
40922                 }else{
40923                     this.inKeyMode = true;
40924                     this.selectNext();
40925                 }
40926             },
40927
40928             "enter" : function(e){
40929                 this.collapse();
40930                 
40931                 if(this.fireEvent("specialkey", this, e)){
40932                     this.onViewClick(false);
40933                 }
40934                 
40935                 return true;
40936             },
40937
40938             "esc" : function(e){
40939                 this.collapse();
40940             },
40941
40942             "tab" : function(e){
40943                 this.collapse();
40944                 
40945                 if(this.fireEvent("specialkey", this, e)){
40946                     this.onViewClick(false);
40947                 }
40948                 
40949                 return true;
40950             },
40951
40952             scope : this,
40953
40954             doRelay : function(foo, bar, hname){
40955                 if(hname == 'down' || this.scope.isExpanded()){
40956                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40957                 }
40958                 return true;
40959             },
40960
40961             forceKeyDown: true
40962         });
40963         
40964         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40965         
40966     },
40967     
40968     initNumberEvent : function(e)
40969     {
40970         this.inputEl().on("keydown" , this.fireKey,  this);
40971         this.inputEl().on("focus", this.onFocus,  this);
40972         this.inputEl().on("blur", this.onBlur,  this);
40973         
40974         this.inputEl().relayEvent('keyup', this);
40975         
40976         if(this.indicator){
40977             this.indicator.addClass('invisible');
40978         }
40979  
40980         this.originalValue = this.getValue();
40981         
40982         if(this.validationEvent == 'keyup'){
40983             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40984             this.inputEl().on('keyup', this.filterValidation, this);
40985         }
40986         else if(this.validationEvent !== false){
40987             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40988         }
40989         
40990         if(this.selectOnFocus){
40991             this.on("focus", this.preFocus, this);
40992             
40993         }
40994         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40995             this.inputEl().on("keypress", this.filterKeys, this);
40996         } else {
40997             this.inputEl().relayEvent('keypress', this);
40998         }
40999         
41000         var allowed = "0123456789";
41001         
41002         if(this.allowDecimals){
41003             allowed += this.decimalSeparator;
41004         }
41005         
41006         if(this.allowNegative){
41007             allowed += "-";
41008         }
41009         
41010         if(this.thousandsDelimiter) {
41011             allowed += ",";
41012         }
41013         
41014         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41015         
41016         var keyPress = function(e){
41017             
41018             var k = e.getKey();
41019             
41020             var c = e.getCharCode();
41021             
41022             if(
41023                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41024                     allowed.indexOf(String.fromCharCode(c)) === -1
41025             ){
41026                 e.stopEvent();
41027                 return;
41028             }
41029             
41030             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41031                 return;
41032             }
41033             
41034             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41035                 e.stopEvent();
41036             }
41037         };
41038         
41039         this.inputEl().on("keypress", keyPress, this);
41040         
41041     },
41042     
41043     onTriggerClick : function(e)
41044     {   
41045         if(this.disabled){
41046             return;
41047         }
41048         
41049         this.page = 0;
41050         this.loadNext = false;
41051         
41052         if(this.isExpanded()){
41053             this.collapse();
41054             return;
41055         }
41056         
41057         this.hasFocus = true;
41058         
41059         if(this.triggerAction == 'all') {
41060             this.doQuery(this.allQuery, true);
41061             return;
41062         }
41063         
41064         this.doQuery(this.getRawValue());
41065     },
41066     
41067     getCurrency : function()
41068     {   
41069         var v = this.currencyEl().getValue();
41070         
41071         return v;
41072     },
41073     
41074     restrictHeight : function()
41075     {
41076         this.list.alignTo(this.currencyEl(), this.listAlign);
41077         this.list.alignTo(this.currencyEl(), this.listAlign);
41078     },
41079     
41080     onViewClick : function(view, doFocus, el, e)
41081     {
41082         var index = this.view.getSelectedIndexes()[0];
41083         
41084         var r = this.store.getAt(index);
41085         
41086         if(r){
41087             this.onSelect(r, index);
41088         }
41089     },
41090     
41091     onSelect : function(record, index){
41092         
41093         if(this.fireEvent('beforeselect', this, record, index) !== false){
41094         
41095             this.setFromCurrencyData(index > -1 ? record.data : false);
41096             
41097             this.collapse();
41098             
41099             this.fireEvent('select', this, record, index);
41100         }
41101     },
41102     
41103     setFromCurrencyData : function(o)
41104     {
41105         var currency = '';
41106         
41107         this.lastCurrency = o;
41108         
41109         if (this.currencyField) {
41110             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41111         } else {
41112             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41113         }
41114         
41115         this.lastSelectionText = currency;
41116         
41117         //setting default currency
41118         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41119             this.setCurrency(this.defaultCurrency);
41120             return;
41121         }
41122         
41123         this.setCurrency(currency);
41124     },
41125     
41126     setFromData : function(o)
41127     {
41128         var c = {};
41129         
41130         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41131         
41132         this.setFromCurrencyData(c);
41133         
41134         var value = '';
41135         
41136         if (this.name) {
41137             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41138         } else {
41139             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41140         }
41141         
41142         this.setValue(value);
41143         
41144     },
41145     
41146     setCurrency : function(v)
41147     {   
41148         this.currencyValue = v;
41149         
41150         if(this.rendered){
41151             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41152             this.validate();
41153         }
41154     },
41155     
41156     setValue : function(v)
41157     {
41158         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41159         
41160         this.value = v;
41161         
41162         if(this.rendered){
41163             
41164             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41165             
41166             this.inputEl().dom.value = (v == '') ? '' :
41167                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41168             
41169             if(!this.allowZero && v === '0') {
41170                 this.hiddenEl().dom.value = '';
41171                 this.inputEl().dom.value = '';
41172             }
41173             
41174             this.validate();
41175         }
41176     },
41177     
41178     getRawValue : function()
41179     {
41180         var v = this.inputEl().getValue();
41181         
41182         return v;
41183     },
41184     
41185     getValue : function()
41186     {
41187         return this.fixPrecision(this.parseValue(this.getRawValue()));
41188     },
41189     
41190     parseValue : function(value)
41191     {
41192         if(this.thousandsDelimiter) {
41193             value += "";
41194             r = new RegExp(",", "g");
41195             value = value.replace(r, "");
41196         }
41197         
41198         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41199         return isNaN(value) ? '' : value;
41200         
41201     },
41202     
41203     fixPrecision : function(value)
41204     {
41205         if(this.thousandsDelimiter) {
41206             value += "";
41207             r = new RegExp(",", "g");
41208             value = value.replace(r, "");
41209         }
41210         
41211         var nan = isNaN(value);
41212         
41213         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41214             return nan ? '' : value;
41215         }
41216         return parseFloat(value).toFixed(this.decimalPrecision);
41217     },
41218     
41219     decimalPrecisionFcn : function(v)
41220     {
41221         return Math.floor(v);
41222     },
41223     
41224     validateValue : function(value)
41225     {
41226         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41227             return false;
41228         }
41229         
41230         var num = this.parseValue(value);
41231         
41232         if(isNaN(num)){
41233             this.markInvalid(String.format(this.nanText, value));
41234             return false;
41235         }
41236         
41237         if(num < this.minValue){
41238             this.markInvalid(String.format(this.minText, this.minValue));
41239             return false;
41240         }
41241         
41242         if(num > this.maxValue){
41243             this.markInvalid(String.format(this.maxText, this.maxValue));
41244             return false;
41245         }
41246         
41247         return true;
41248     },
41249     
41250     validate : function()
41251     {
41252         if(this.disabled || this.allowBlank){
41253             this.markValid();
41254             return true;
41255         }
41256         
41257         var currency = this.getCurrency();
41258         
41259         if(this.validateValue(this.getRawValue()) && currency.length){
41260             this.markValid();
41261             return true;
41262         }
41263         
41264         this.markInvalid();
41265         return false;
41266     },
41267     
41268     getName: function()
41269     {
41270         return this.name;
41271     },
41272     
41273     beforeBlur : function()
41274     {
41275         if(!this.castInt){
41276             return;
41277         }
41278         
41279         var v = this.parseValue(this.getRawValue());
41280         
41281         if(v || v == 0){
41282             this.setValue(v);
41283         }
41284     },
41285     
41286     onBlur : function()
41287     {
41288         this.beforeBlur();
41289         
41290         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41291             //this.el.removeClass(this.focusClass);
41292         }
41293         
41294         this.hasFocus = false;
41295         
41296         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41297             this.validate();
41298         }
41299         
41300         var v = this.getValue();
41301         
41302         if(String(v) !== String(this.startValue)){
41303             this.fireEvent('change', this, v, this.startValue);
41304         }
41305         
41306         this.fireEvent("blur", this);
41307     },
41308     
41309     inputEl : function()
41310     {
41311         return this.el.select('.roo-money-amount-input', true).first();
41312     },
41313     
41314     currencyEl : function()
41315     {
41316         return this.el.select('.roo-money-currency-input', true).first();
41317     },
41318     
41319     hiddenEl : function()
41320     {
41321         return this.el.select('input.hidden-number-input',true).first();
41322     }
41323     
41324 });