5b7ee4e872065f4eacb721e692303e71e0c96fec
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets[0], function(s) {
10                     if (s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })();/*
16  * - LGPL
17  *
18  * base class for bootstrap elements.
19  * 
20  */
21
22 Roo.bootstrap = Roo.bootstrap || {};
23 /**
24  * @class Roo.bootstrap.Component
25  * @extends Roo.Component
26  * Bootstrap Component base class
27  * @cfg {String} cls css class
28  * @cfg {String} style any extra css
29  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
31  * @cfg {string} dataId cutomer id
32  * @cfg {string} name Specifies name attribute
33  * @cfg {string} tooltip  Text for the tooltip
34  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
35  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
36  
37  * @constructor
38  * Do not use directly - it does not do anything..
39  * @param {Object} config The config object
40  */
41
42
43
44 Roo.bootstrap.Component = function(config){
45     Roo.bootstrap.Component.superclass.constructor.call(this, config);
46        
47     this.addEvents({
48         /**
49          * @event childrenrendered
50          * Fires when the children have been rendered..
51          * @param {Roo.bootstrap.Component} this
52          */
53         "childrenrendered" : true
54         
55         
56         
57     });
58     
59     
60 };
61
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
63     
64     
65     allowDomMove : false, // to stop relocations in parent onRender...
66     
67     cls : false,
68     
69     style : false,
70     
71     autoCreate : false,
72     
73     tooltip : null,
74     /**
75      * Initialize Events for the element
76      */
77     initEvents : function() { },
78     
79     xattr : false,
80     
81     parentId : false,
82     
83     can_build_overlaid : true,
84     
85     container_method : false,
86     
87     dataId : false,
88     
89     name : false,
90     
91     parent: function() {
92         // returns the parent component..
93         return Roo.ComponentMgr.get(this.parentId)
94         
95         
96     },
97     
98     // private
99     onRender : function(ct, position)
100     {
101        // Roo.log("Call onRender: " + this.xtype);
102         
103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
104         
105         if(this.el){
106             if (this.el.attr('xtype')) {
107                 this.el.attr('xtypex', this.el.attr('xtype'));
108                 this.el.dom.removeAttribute('xtype');
109                 
110                 this.initEvents();
111             }
112             
113             return;
114         }
115         
116          
117         
118         var cfg = Roo.apply({},  this.getAutoCreate());
119         
120         cfg.id = this.id || Roo.id();
121         
122         // fill in the extra attributes 
123         if (this.xattr && typeof(this.xattr) =='object') {
124             for (var i in this.xattr) {
125                 cfg[i] = this.xattr[i];
126             }
127         }
128         
129         if(this.dataId){
130             cfg.dataId = this.dataId;
131         }
132         
133         if (this.cls) {
134             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
135         }
136         
137         if (this.style) { // fixme needs to support more complex style data.
138             cfg.style = this.style;
139         }
140         
141         if(this.name){
142             cfg.name = this.name;
143         }
144         
145         this.el = ct.createChild(cfg, position);
146         
147         if (this.tooltip) {
148             this.tooltipEl().attr('tooltip', this.tooltip);
149         }
150         
151         if(this.tabIndex !== undefined){
152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
153         }
154         
155         this.initEvents();
156         
157     },
158     /**
159      * Fetch the element to add children to
160      * @return {Roo.Element} defaults to this.el
161      */
162     getChildContainer : function()
163     {
164         return this.el;
165     },
166     /**
167      * Fetch the element to display the tooltip on.
168      * @return {Roo.Element} defaults to this.el
169      */
170     tooltipEl : function()
171     {
172         return this.el;
173     },
174         
175     addxtype  : function(tree,cntr)
176     {
177         var cn = this;
178         
179         cn = Roo.factory(tree);
180         //Roo.log(['addxtype', cn]);
181            
182         cn.parentType = this.xtype; //??
183         cn.parentId = this.id;
184         
185         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186         if (typeof(cn.container_method) == 'string') {
187             cntr = cn.container_method;
188         }
189         
190         
191         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
192         
193         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
194         
195         var build_from_html =  Roo.XComponent.build_from_html;
196           
197         var is_body  = (tree.xtype == 'Body') ;
198           
199         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
200           
201         var self_cntr_el = Roo.get(this[cntr](false));
202         
203         // do not try and build conditional elements 
204         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
205             return false;
206         }
207         
208         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210                 return this.addxtypeChild(tree,cntr, is_body);
211             }
212             
213             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
214                 
215             if(echild){
216                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
217             }
218             
219             Roo.log('skipping render');
220             return cn;
221             
222         }
223         
224         var ret = false;
225         if (!build_from_html) {
226             return false;
227         }
228         
229         // this i think handles overlaying multiple children of the same type
230         // with the sam eelement.. - which might be buggy..
231         while (true) {
232             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
233             
234             if (!echild) {
235                 break;
236             }
237             
238             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
239                 break;
240             }
241             
242             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
243         }
244        
245         return ret;
246     },
247     
248     
249     addxtypeChild : function (tree, cntr, is_body)
250     {
251         Roo.debug && Roo.log('addxtypeChild:' + cntr);
252         var cn = this;
253         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
254         
255         
256         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257                     (typeof(tree['flexy:foreach']) != 'undefined');
258           
259     
260         
261         skip_children = false;
262         // render the element if it's not BODY.
263         if (!is_body) {
264             
265             // if parent was disabled, then do not try and create the children..
266             if(!this[cntr](true)){
267                 tree.items = [];
268                 return tree;
269             }
270            
271             cn = Roo.factory(tree);
272            
273             cn.parentType = this.xtype; //??
274             cn.parentId = this.id;
275             
276             var build_from_html =  Roo.XComponent.build_from_html;
277             
278             
279             // does the container contain child eleemnts with 'xtype' attributes.
280             // that match this xtype..
281             // note - when we render we create these as well..
282             // so we should check to see if body has xtype set.
283             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
284                
285                 var self_cntr_el = Roo.get(this[cntr](false));
286                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
287                 if (echild) { 
288                     //Roo.log(Roo.XComponent.build_from_html);
289                     //Roo.log("got echild:");
290                     //Roo.log(echild);
291                 }
292                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293                 // and are not displayed -this causes this to use up the wrong element when matching.
294                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
295                 
296                 
297                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
299                   
300                   
301                   
302                     cn.el = echild;
303                   //  Roo.log("GOT");
304                     //echild.dom.removeAttribute('xtype');
305                 } else {
306                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307                     Roo.debug && Roo.log(self_cntr_el);
308                     Roo.debug && Roo.log(echild);
309                     Roo.debug && Roo.log(cn);
310                 }
311             }
312            
313             
314            
315             // if object has flexy:if - then it may or may not be rendered.
316             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
317                 // skip a flexy if element.
318                 Roo.debug && Roo.log('skipping render');
319                 Roo.debug && Roo.log(tree);
320                 if (!cn.el) {
321                     Roo.debug && Roo.log('skipping all children');
322                     skip_children = true;
323                 }
324                 
325              } else {
326                  
327                 // actually if flexy:foreach is found, we really want to create 
328                 // multiple copies here...
329                 //Roo.log('render');
330                 //Roo.log(this[cntr]());
331                 // some elements do not have render methods.. like the layouts...
332                 /*
333                 if(this[cntr](true) === false){
334                     cn.items = [];
335                     return cn;
336                 }
337                 */
338                 cn.render && cn.render(this[cntr](true));
339                 
340              }
341             // then add the element..
342         }
343          
344         // handle the kids..
345         
346         var nitems = [];
347         /*
348         if (typeof (tree.menu) != 'undefined') {
349             tree.menu.parentType = cn.xtype;
350             tree.menu.triggerEl = cn.el;
351             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
352             
353         }
354         */
355         if (!tree.items || !tree.items.length) {
356             cn.items = nitems;
357             //Roo.log(["no children", this]);
358             
359             return cn;
360         }
361          
362         var items = tree.items;
363         delete tree.items;
364         
365         //Roo.log(items.length);
366             // add the items..
367         if (!skip_children) {    
368             for(var i =0;i < items.length;i++) {
369               //  Roo.log(['add child', items[i]]);
370                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
371             }
372         }
373         
374         cn.items = nitems;
375         
376         //Roo.log("fire childrenrendered");
377         
378         cn.fireEvent('childrenrendered', this);
379         
380         return cn;
381     },
382     
383     /**
384      * Set the element that will be used to show or hide
385      */
386     setVisibilityEl : function(el)
387     {
388         this.visibilityEl = el;
389     },
390     
391      /**
392      * Get the element that will be used to show or hide
393      */
394     getVisibilityEl : function()
395     {
396         if (typeof(this.visibilityEl) == 'object') {
397             return this.visibilityEl;
398         }
399         
400         if (typeof(this.visibilityEl) == 'string') {
401             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
402         }
403         
404         return this.getEl();
405     },
406     
407     /**
408      * Show a component - removes 'hidden' class
409      */
410     show : function()
411     {
412         if(!this.getVisibilityEl()){
413             return;
414         }
415          
416         this.getVisibilityEl().removeClass(['hidden','d-none']);
417         
418         this.fireEvent('show', this);
419         
420         
421     },
422     /**
423      * Hide a component - adds 'hidden' class
424      */
425     hide: function()
426     {
427         if(!this.getVisibilityEl()){
428             return;
429         }
430         
431         this.getVisibilityEl().addClass(['hidden','d-none']);
432         
433         this.fireEvent('hide', this);
434         
435     }
436 });
437
438  /*
439  * - LGPL
440  *
441  * Body
442  *
443  */
444
445 /**
446  * @class Roo.bootstrap.Body
447  * @extends Roo.bootstrap.Component
448  * Bootstrap Body class
449  *
450  * @constructor
451  * Create a new body
452  * @param {Object} config The config object
453  */
454
455 Roo.bootstrap.Body = function(config){
456
457     config = config || {};
458
459     Roo.bootstrap.Body.superclass.constructor.call(this, config);
460     this.el = Roo.get(config.el ? config.el : document.body );
461     if (this.cls && this.cls.length) {
462         Roo.get(document.body).addClass(this.cls);
463     }
464 };
465
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
467
468     is_body : true,// just to make sure it's constructed?
469
470         autoCreate : {
471         cls: 'container'
472     },
473     onRender : function(ct, position)
474     {
475        /* Roo.log("Roo.bootstrap.Body - onRender");
476         if (this.cls && this.cls.length) {
477             Roo.get(document.body).addClass(this.cls);
478         }
479         // style??? xttr???
480         */
481     }
482
483
484
485
486 });
487 /*
488  * - LGPL
489  *
490  * button group
491  * 
492  */
493
494
495 /**
496  * @class Roo.bootstrap.ButtonGroup
497  * @extends Roo.bootstrap.Component
498  * Bootstrap ButtonGroup class
499  * @cfg {String} size lg | sm | xs (default empty normal)
500  * @cfg {String} align vertical | justified  (default none)
501  * @cfg {String} direction up | down (default down)
502  * @cfg {Boolean} toolbar false | true
503  * @cfg {Boolean} btn true | false
504  * 
505  * 
506  * @constructor
507  * Create a new Input
508  * @param {Object} config The config object
509  */
510
511 Roo.bootstrap.ButtonGroup = function(config){
512     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
513 };
514
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
516     
517     size: '',
518     align: '',
519     direction: '',
520     toolbar: false,
521     btn: true,
522
523     getAutoCreate : function(){
524         var cfg = {
525             cls: 'btn-group',
526             html : null
527         };
528         
529         cfg.html = this.html || cfg.html;
530         
531         if (this.toolbar) {
532             cfg = {
533                 cls: 'btn-toolbar',
534                 html: null
535             };
536             
537             return cfg;
538         }
539         
540         if (['vertical','justified'].indexOf(this.align)!==-1) {
541             cfg.cls = 'btn-group-' + this.align;
542             
543             if (this.align == 'justified') {
544                 console.log(this.items);
545             }
546         }
547         
548         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549             cfg.cls += ' btn-group-' + this.size;
550         }
551         
552         if (this.direction == 'up') {
553             cfg.cls += ' dropup' ;
554         }
555         
556         return cfg;
557     },
558     /**
559      * Add a button to the group (similar to NavItem API.)
560      */
561     addItem : function(cfg)
562     {
563         var cn = new Roo.bootstrap.Button(cfg);
564         //this.register(cn);
565         cn.parentId = this.id;
566         cn.onRender(this.el, null);
567         return cn;
568     }
569    
570 });
571
572  /*
573  * - LGPL
574  *
575  * button
576  * 
577  */
578
579 /**
580  * @class Roo.bootstrap.Button
581  * @extends Roo.bootstrap.Component
582  * Bootstrap Button class
583  * @cfg {String} html The button content
584  * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
585  * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
586  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
587  * @cfg {String} size ( lg | sm | xs)
588  * @cfg {String} tag ( a | input | submit)
589  * @cfg {String} href empty or href
590  * @cfg {Boolean} disabled default false;
591  * @cfg {Boolean} isClose default false;
592  * @cfg {String} glyphicon depricated - use fa
593  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
594  * @cfg {String} badge text for badge
595  * @cfg {String} theme (default|glow)  
596  * @cfg {Boolean} inverse dark themed version
597  * @cfg {Boolean} toggle is it a slidy toggle button
598  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
599  * @cfg {String} ontext text for on slidy toggle state
600  * @cfg {String} offtext text for off slidy toggle state
601  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
602  * @cfg {Boolean} removeClass remove the standard class..
603  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
604  * 
605  * @constructor
606  * Create a new button
607  * @param {Object} config The config object
608  */
609
610
611 Roo.bootstrap.Button = function(config){
612     Roo.bootstrap.Button.superclass.constructor.call(this, config);
613     this.weightClass = ["btn-default btn-outline-secondary", 
614                        "btn-primary", 
615                        "btn-success", 
616                        "btn-info", 
617                        "btn-warning",
618                        "btn-danger",
619                        "btn-link"
620                       ],  
621     this.addEvents({
622         // raw events
623         /**
624          * @event click
625          * When a butotn is pressed
626          * @param {Roo.bootstrap.Button} btn
627          * @param {Roo.EventObject} e
628          */
629         "click" : true,
630          /**
631          * @event toggle
632          * After the button has been toggles
633          * @param {Roo.bootstrap.Button} btn
634          * @param {Roo.EventObject} e
635          * @param {boolean} pressed (also available as button.pressed)
636          */
637         "toggle" : true
638     });
639 };
640
641 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
642     html: false,
643     active: false,
644     weight: '',
645     badge_weight: '',
646     outline : false,
647     size: '',
648     tag: 'button',
649     href: '',
650     disabled: false,
651     isClose: false,
652     glyphicon: '',
653     fa: '',
654     badge: '',
655     theme: 'default',
656     inverse: false,
657     
658     toggle: false,
659     ontext: 'ON',
660     offtext: 'OFF',
661     defaulton: true,
662     preventDefault: true,
663     removeClass: false,
664     name: false,
665     target: false,
666      
667     pressed : null,
668      
669     
670     getAutoCreate : function(){
671         
672         var cfg = {
673             tag : 'button',
674             cls : 'roo-button',
675             html: ''
676         };
677         
678         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
679             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
680             this.tag = 'button';
681         } else {
682             cfg.tag = this.tag;
683         }
684         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
685         
686         if (this.toggle == true) {
687             cfg={
688                 tag: 'div',
689                 cls: 'slider-frame roo-button',
690                 cn: [
691                     {
692                         tag: 'span',
693                         'data-on-text':'ON',
694                         'data-off-text':'OFF',
695                         cls: 'slider-button',
696                         html: this.offtext
697                     }
698                 ]
699             };
700             
701             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
702                 cfg.cls += ' '+this.weight;
703             }
704             
705             return cfg;
706         }
707         
708         if (this.isClose) {
709             cfg.cls += ' close';
710             
711             cfg["aria-hidden"] = true;
712             
713             cfg.html = "&times;";
714             
715             return cfg;
716         }
717         
718          
719         if (this.theme==='default') {
720             cfg.cls = 'btn roo-button';
721             
722             //if (this.parentType != 'Navbar') {
723             this.weight = this.weight.length ?  this.weight : 'default';
724             //}
725             if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
726                 
727                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
728                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
729                 cfg.cls += ' btn-' + outline + weight;
730                 if (this.weight == 'default') {
731                     // BC
732                     cfg.cls += ' btn-' + this.weight;
733                 }
734             }
735         } else if (this.theme==='glow') {
736             
737             cfg.tag = 'a';
738             cfg.cls = 'btn-glow roo-button';
739             
740             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
741                 
742                 cfg.cls += ' ' + this.weight;
743             }
744         }
745    
746         
747         if (this.inverse) {
748             this.cls += ' inverse';
749         }
750         
751         
752         if (this.active || this.pressed === true) {
753             cfg.cls += ' active';
754         }
755         
756         if (this.disabled) {
757             cfg.disabled = 'disabled';
758         }
759         
760         if (this.items) {
761             Roo.log('changing to ul' );
762             cfg.tag = 'ul';
763             this.glyphicon = 'caret';
764             if (Roo.bootstrap.version == 4) {
765                 this.fa = 'caret-down';
766             }
767             
768         }
769         
770         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
771          
772         //gsRoo.log(this.parentType);
773         if (this.parentType === 'Navbar' && !this.parent().bar) {
774             Roo.log('changing to li?');
775             
776             cfg.tag = 'li';
777             
778             cfg.cls = '';
779             cfg.cn =  [{
780                 tag : 'a',
781                 cls : 'roo-button',
782                 html : this.html,
783                 href : this.href || '#'
784             }];
785             if (this.menu) {
786                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
787                 cfg.cls += ' dropdown';
788             }   
789             
790             delete cfg.html;
791             
792         }
793         
794        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
795         
796         if (this.glyphicon) {
797             cfg.html = ' ' + cfg.html;
798             
799             cfg.cn = [
800                 {
801                     tag: 'span',
802                     cls: 'glyphicon glyphicon-' + this.glyphicon
803                 }
804             ];
805         }
806         if (this.fa) {
807             cfg.html = ' ' + cfg.html;
808             
809             cfg.cn = [
810                 {
811                     tag: 'i',
812                     cls: 'fa fas fa-' + this.fa
813                 }
814             ];
815         }
816         
817         if (this.badge) {
818             cfg.html += ' ';
819             
820             cfg.tag = 'a';
821             
822 //            cfg.cls='btn roo-button';
823             
824             cfg.href=this.href;
825             
826             var value = cfg.html;
827             
828             if(this.glyphicon){
829                 value = {
830                     tag: 'span',
831                     cls: 'glyphicon glyphicon-' + this.glyphicon,
832                     html: this.html
833                 };
834             }
835             if(this.fa){
836                 value = {
837                     tag: 'i',
838                     cls: 'fa fas fa-' + this.fa,
839                     html: this.html
840                 };
841             }
842             
843             var bw = this.badge_weight.length ? this.badge_weight :
844                 (this.weight.length ? this.weight : 'secondary');
845             bw = bw == 'default' ? 'secondary' : bw;
846             
847             cfg.cn = [
848                 value,
849                 {
850                     tag: 'span',
851                     cls: 'badge badge-' + bw,
852                     html: this.badge
853                 }
854             ];
855             
856             cfg.html='';
857         }
858         
859         if (this.menu) {
860             cfg.cls += ' dropdown';
861             cfg.html = typeof(cfg.html) != 'undefined' ?
862                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
863         }
864         
865         if (cfg.tag !== 'a' && this.href !== '') {
866             throw "Tag must be a to set href.";
867         } else if (this.href.length > 0) {
868             cfg.href = this.href;
869         }
870         
871         if(this.removeClass){
872             cfg.cls = '';
873         }
874         
875         if(this.target){
876             cfg.target = this.target;
877         }
878         
879         return cfg;
880     },
881     initEvents: function() {
882        // Roo.log('init events?');
883 //        Roo.log(this.el.dom);
884         // add the menu...
885         
886         if (typeof (this.menu) != 'undefined') {
887             this.menu.parentType = this.xtype;
888             this.menu.triggerEl = this.el;
889             this.addxtype(Roo.apply({}, this.menu));
890         }
891
892
893        if (this.el.hasClass('roo-button')) {
894             this.el.on('click', this.onClick, this);
895        } else {
896             this.el.select('.roo-button').on('click', this.onClick, this);
897        }
898        
899        if(this.removeClass){
900            this.el.on('click', this.onClick, this);
901        }
902        
903        this.el.enableDisplayMode();
904         
905     },
906     onClick : function(e)
907     {
908         if (this.disabled) {
909             return;
910         }
911         
912         Roo.log('button on click ');
913         if(this.preventDefault){
914             e.preventDefault();
915         }
916         
917         if (this.pressed === true || this.pressed === false) {
918             this.toggleActive(e);
919         }
920         
921         
922         this.fireEvent('click', this, e);
923     },
924     
925     /**
926      * Enables this button
927      */
928     enable : function()
929     {
930         this.disabled = false;
931         this.el.removeClass('disabled');
932     },
933     
934     /**
935      * Disable this button
936      */
937     disable : function()
938     {
939         this.disabled = true;
940         this.el.addClass('disabled');
941     },
942      /**
943      * sets the active state on/off, 
944      * @param {Boolean} state (optional) Force a particular state
945      */
946     setActive : function(v) {
947         
948         this.el[v ? 'addClass' : 'removeClass']('active');
949         this.pressed = v;
950     },
951      /**
952      * toggles the current active state 
953      */
954     toggleActive : function(e)
955     {
956         this.setActive(!this.pressed);
957         this.fireEvent('toggle', this, e, !this.pressed);
958     },
959      /**
960      * get the current active state
961      * @return {boolean} true if it's active
962      */
963     isActive : function()
964     {
965         return this.el.hasClass('active');
966     },
967     /**
968      * set the text of the first selected button
969      */
970     setText : function(str)
971     {
972         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
973     },
974     /**
975      * get the text of the first selected button
976      */
977     getText : function()
978     {
979         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
980     },
981     
982     setWeight : function(str)
983     {
984         this.el.removeClass(this.weightClass);
985         this.weight = str;
986         var outline = this.outline ? 'outline-' : '';
987         if (str == 'default') {
988             this.el.addClass('btn-default btn-outline-secondary');        
989             return;
990         }
991         this.el.addClass('btn-' + outline + str);        
992     }
993     
994     
995 });
996
997  /*
998  * - LGPL
999  *
1000  * column
1001  * 
1002  */
1003
1004 /**
1005  * @class Roo.bootstrap.Column
1006  * @extends Roo.bootstrap.Component
1007  * Bootstrap Column class
1008  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1009  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1010  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1011  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1012  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1013  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1014  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1015  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1016  *
1017  * 
1018  * @cfg {Boolean} hidden (true|false) hide the element
1019  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1020  * @cfg {String} fa (ban|check|...) font awesome icon
1021  * @cfg {Number} fasize (1|2|....) font awsome size
1022
1023  * @cfg {String} icon (info-sign|check|...) glyphicon name
1024
1025  * @cfg {String} html content of column.
1026  * 
1027  * @constructor
1028  * Create a new Column
1029  * @param {Object} config The config object
1030  */
1031
1032 Roo.bootstrap.Column = function(config){
1033     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1034 };
1035
1036 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1037     
1038     xs: false,
1039     sm: false,
1040     md: false,
1041     lg: false,
1042     xsoff: false,
1043     smoff: false,
1044     mdoff: false,
1045     lgoff: false,
1046     html: '',
1047     offset: 0,
1048     alert: false,
1049     fa: false,
1050     icon : false,
1051     hidden : false,
1052     fasize : 1,
1053     
1054     getAutoCreate : function(){
1055         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1056         
1057         cfg = {
1058             tag: 'div',
1059             cls: 'column'
1060         };
1061         
1062         var settings=this;
1063         ['xs','sm','md','lg'].map(function(size){
1064             //Roo.log( size + ':' + settings[size]);
1065             
1066             if (settings[size+'off'] !== false) {
1067                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1068             }
1069             
1070             if (settings[size] === false) {
1071                 return;
1072             }
1073             
1074             if (!settings[size]) { // 0 = hidden
1075                 cfg.cls += ' hidden-' + size;
1076                 return;
1077             }
1078             cfg.cls += ' col-' + size + '-' + settings[size];
1079             
1080         });
1081         
1082         if (this.hidden) {
1083             cfg.cls += ' hidden';
1084         }
1085         
1086         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1087             cfg.cls +=' alert alert-' + this.alert;
1088         }
1089         
1090         
1091         if (this.html.length) {
1092             cfg.html = this.html;
1093         }
1094         if (this.fa) {
1095             var fasize = '';
1096             if (this.fasize > 1) {
1097                 fasize = ' fa-' + this.fasize + 'x';
1098             }
1099             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1100             
1101             
1102         }
1103         if (this.icon) {
1104             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1105         }
1106         
1107         return cfg;
1108     }
1109    
1110 });
1111
1112  
1113
1114  /*
1115  * - LGPL
1116  *
1117  * page container.
1118  * 
1119  */
1120
1121
1122 /**
1123  * @class Roo.bootstrap.Container
1124  * @extends Roo.bootstrap.Component
1125  * Bootstrap Container class
1126  * @cfg {Boolean} jumbotron is it a jumbotron element
1127  * @cfg {String} html content of element
1128  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1129  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1130  * @cfg {String} header content of header (for panel)
1131  * @cfg {String} footer content of footer (for panel)
1132  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1133  * @cfg {String} tag (header|aside|section) type of HTML tag.
1134  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1135  * @cfg {String} fa font awesome icon
1136  * @cfg {String} icon (info-sign|check|...) glyphicon name
1137  * @cfg {Boolean} hidden (true|false) hide the element
1138  * @cfg {Boolean} expandable (true|false) default false
1139  * @cfg {Boolean} expanded (true|false) default true
1140  * @cfg {String} rheader contet on the right of header
1141  * @cfg {Boolean} clickable (true|false) default false
1142
1143  *     
1144  * @constructor
1145  * Create a new Container
1146  * @param {Object} config The config object
1147  */
1148
1149 Roo.bootstrap.Container = function(config){
1150     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1151     
1152     this.addEvents({
1153         // raw events
1154          /**
1155          * @event expand
1156          * After the panel has been expand
1157          * 
1158          * @param {Roo.bootstrap.Container} this
1159          */
1160         "expand" : true,
1161         /**
1162          * @event collapse
1163          * After the panel has been collapsed
1164          * 
1165          * @param {Roo.bootstrap.Container} this
1166          */
1167         "collapse" : true,
1168         /**
1169          * @event click
1170          * When a element is chick
1171          * @param {Roo.bootstrap.Container} this
1172          * @param {Roo.EventObject} e
1173          */
1174         "click" : true
1175     });
1176 };
1177
1178 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1179     
1180     jumbotron : false,
1181     well: '',
1182     panel : '',
1183     header: '',
1184     footer : '',
1185     sticky: '',
1186     tag : false,
1187     alert : false,
1188     fa: false,
1189     icon : false,
1190     expandable : false,
1191     rheader : '',
1192     expanded : true,
1193     clickable: false,
1194   
1195      
1196     getChildContainer : function() {
1197         
1198         if(!this.el){
1199             return false;
1200         }
1201         
1202         if (this.panel.length) {
1203             return this.el.select('.panel-body',true).first();
1204         }
1205         
1206         return this.el;
1207     },
1208     
1209     
1210     getAutoCreate : function(){
1211         
1212         var cfg = {
1213             tag : this.tag || 'div',
1214             html : '',
1215             cls : ''
1216         };
1217         if (this.jumbotron) {
1218             cfg.cls = 'jumbotron';
1219         }
1220         
1221         
1222         
1223         // - this is applied by the parent..
1224         //if (this.cls) {
1225         //    cfg.cls = this.cls + '';
1226         //}
1227         
1228         if (this.sticky.length) {
1229             
1230             var bd = Roo.get(document.body);
1231             if (!bd.hasClass('bootstrap-sticky')) {
1232                 bd.addClass('bootstrap-sticky');
1233                 Roo.select('html',true).setStyle('height', '100%');
1234             }
1235              
1236             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1237         }
1238         
1239         
1240         if (this.well.length) {
1241             switch (this.well) {
1242                 case 'lg':
1243                 case 'sm':
1244                     cfg.cls +=' well well-' +this.well;
1245                     break;
1246                 default:
1247                     cfg.cls +=' well';
1248                     break;
1249             }
1250         }
1251         
1252         if (this.hidden) {
1253             cfg.cls += ' hidden';
1254         }
1255         
1256         
1257         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1258             cfg.cls +=' alert alert-' + this.alert;
1259         }
1260         
1261         var body = cfg;
1262         
1263         if (this.panel.length) {
1264             cfg.cls += ' panel panel-' + this.panel;
1265             cfg.cn = [];
1266             if (this.header.length) {
1267                 
1268                 var h = [];
1269                 
1270                 if(this.expandable){
1271                     
1272                     cfg.cls = cfg.cls + ' expandable';
1273                     
1274                     h.push({
1275                         tag: 'i',
1276                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1277                     });
1278                     
1279                 }
1280                 
1281                 h.push(
1282                     {
1283                         tag: 'span',
1284                         cls : 'panel-title',
1285                         html : (this.expandable ? '&nbsp;' : '') + this.header
1286                     },
1287                     {
1288                         tag: 'span',
1289                         cls: 'panel-header-right',
1290                         html: this.rheader
1291                     }
1292                 );
1293                 
1294                 cfg.cn.push({
1295                     cls : 'panel-heading',
1296                     style : this.expandable ? 'cursor: pointer' : '',
1297                     cn : h
1298                 });
1299                 
1300             }
1301             
1302             body = false;
1303             cfg.cn.push({
1304                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1305                 html : this.html
1306             });
1307             
1308             
1309             if (this.footer.length) {
1310                 cfg.cn.push({
1311                     cls : 'panel-footer',
1312                     html : this.footer
1313                     
1314                 });
1315             }
1316             
1317         }
1318         
1319         if (body) {
1320             body.html = this.html || cfg.html;
1321             // prefix with the icons..
1322             if (this.fa) {
1323                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1324             }
1325             if (this.icon) {
1326                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1327             }
1328             
1329             
1330         }
1331         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1332             cfg.cls =  'container';
1333         }
1334         
1335         return cfg;
1336     },
1337     
1338     initEvents: function() 
1339     {
1340         if(this.expandable){
1341             var headerEl = this.headerEl();
1342         
1343             if(headerEl){
1344                 headerEl.on('click', this.onToggleClick, this);
1345             }
1346         }
1347         
1348         if(this.clickable){
1349             this.el.on('click', this.onClick, this);
1350         }
1351         
1352     },
1353     
1354     onToggleClick : function()
1355     {
1356         var headerEl = this.headerEl();
1357         
1358         if(!headerEl){
1359             return;
1360         }
1361         
1362         if(this.expanded){
1363             this.collapse();
1364             return;
1365         }
1366         
1367         this.expand();
1368     },
1369     
1370     expand : function()
1371     {
1372         if(this.fireEvent('expand', this)) {
1373             
1374             this.expanded = true;
1375             
1376             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1377             
1378             this.el.select('.panel-body',true).first().removeClass('hide');
1379             
1380             var toggleEl = this.toggleEl();
1381
1382             if(!toggleEl){
1383                 return;
1384             }
1385
1386             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1387         }
1388         
1389     },
1390     
1391     collapse : function()
1392     {
1393         if(this.fireEvent('collapse', this)) {
1394             
1395             this.expanded = false;
1396             
1397             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1398             this.el.select('.panel-body',true).first().addClass('hide');
1399         
1400             var toggleEl = this.toggleEl();
1401
1402             if(!toggleEl){
1403                 return;
1404             }
1405
1406             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1407         }
1408     },
1409     
1410     toggleEl : function()
1411     {
1412         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1413             return;
1414         }
1415         
1416         return this.el.select('.panel-heading .fa',true).first();
1417     },
1418     
1419     headerEl : function()
1420     {
1421         if(!this.el || !this.panel.length || !this.header.length){
1422             return;
1423         }
1424         
1425         return this.el.select('.panel-heading',true).first()
1426     },
1427     
1428     bodyEl : function()
1429     {
1430         if(!this.el || !this.panel.length){
1431             return;
1432         }
1433         
1434         return this.el.select('.panel-body',true).first()
1435     },
1436     
1437     titleEl : function()
1438     {
1439         if(!this.el || !this.panel.length || !this.header.length){
1440             return;
1441         }
1442         
1443         return this.el.select('.panel-title',true).first();
1444     },
1445     
1446     setTitle : function(v)
1447     {
1448         var titleEl = this.titleEl();
1449         
1450         if(!titleEl){
1451             return;
1452         }
1453         
1454         titleEl.dom.innerHTML = v;
1455     },
1456     
1457     getTitle : function()
1458     {
1459         
1460         var titleEl = this.titleEl();
1461         
1462         if(!titleEl){
1463             return '';
1464         }
1465         
1466         return titleEl.dom.innerHTML;
1467     },
1468     
1469     setRightTitle : function(v)
1470     {
1471         var t = this.el.select('.panel-header-right',true).first();
1472         
1473         if(!t){
1474             return;
1475         }
1476         
1477         t.dom.innerHTML = v;
1478     },
1479     
1480     onClick : function(e)
1481     {
1482         e.preventDefault();
1483         
1484         this.fireEvent('click', this, e);
1485     }
1486 });
1487
1488  /*
1489  * - LGPL
1490  *
1491  * image
1492  * 
1493  */
1494
1495
1496 /**
1497  * @class Roo.bootstrap.Img
1498  * @extends Roo.bootstrap.Component
1499  * Bootstrap Img class
1500  * @cfg {Boolean} imgResponsive false | true
1501  * @cfg {String} border rounded | circle | thumbnail
1502  * @cfg {String} src image source
1503  * @cfg {String} alt image alternative text
1504  * @cfg {String} href a tag href
1505  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1506  * @cfg {String} xsUrl xs image source
1507  * @cfg {String} smUrl sm image source
1508  * @cfg {String} mdUrl md image source
1509  * @cfg {String} lgUrl lg image source
1510  * 
1511  * @constructor
1512  * Create a new Input
1513  * @param {Object} config The config object
1514  */
1515
1516 Roo.bootstrap.Img = function(config){
1517     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1518     
1519     this.addEvents({
1520         // img events
1521         /**
1522          * @event click
1523          * The img click event for the img.
1524          * @param {Roo.EventObject} e
1525          */
1526         "click" : true
1527     });
1528 };
1529
1530 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1531     
1532     imgResponsive: true,
1533     border: '',
1534     src: 'about:blank',
1535     href: false,
1536     target: false,
1537     xsUrl: '',
1538     smUrl: '',
1539     mdUrl: '',
1540     lgUrl: '',
1541
1542     getAutoCreate : function()
1543     {   
1544         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1545             return this.createSingleImg();
1546         }
1547         
1548         var cfg = {
1549             tag: 'div',
1550             cls: 'roo-image-responsive-group',
1551             cn: []
1552         };
1553         var _this = this;
1554         
1555         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1556             
1557             if(!_this[size + 'Url']){
1558                 return;
1559             }
1560             
1561             var img = {
1562                 tag: 'img',
1563                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1564                 html: _this.html || cfg.html,
1565                 src: _this[size + 'Url']
1566             };
1567             
1568             img.cls += ' roo-image-responsive-' + size;
1569             
1570             var s = ['xs', 'sm', 'md', 'lg'];
1571             
1572             s.splice(s.indexOf(size), 1);
1573             
1574             Roo.each(s, function(ss){
1575                 img.cls += ' hidden-' + ss;
1576             });
1577             
1578             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1579                 cfg.cls += ' img-' + _this.border;
1580             }
1581             
1582             if(_this.alt){
1583                 cfg.alt = _this.alt;
1584             }
1585             
1586             if(_this.href){
1587                 var a = {
1588                     tag: 'a',
1589                     href: _this.href,
1590                     cn: [
1591                         img
1592                     ]
1593                 };
1594
1595                 if(this.target){
1596                     a.target = _this.target;
1597                 }
1598             }
1599             
1600             cfg.cn.push((_this.href) ? a : img);
1601             
1602         });
1603         
1604         return cfg;
1605     },
1606     
1607     createSingleImg : function()
1608     {
1609         var cfg = {
1610             tag: 'img',
1611             cls: (this.imgResponsive) ? 'img-responsive' : '',
1612             html : null,
1613             src : 'about:blank'  // just incase src get's set to undefined?!?
1614         };
1615         
1616         cfg.html = this.html || cfg.html;
1617         
1618         cfg.src = this.src || cfg.src;
1619         
1620         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1621             cfg.cls += ' img-' + this.border;
1622         }
1623         
1624         if(this.alt){
1625             cfg.alt = this.alt;
1626         }
1627         
1628         if(this.href){
1629             var a = {
1630                 tag: 'a',
1631                 href: this.href,
1632                 cn: [
1633                     cfg
1634                 ]
1635             };
1636             
1637             if(this.target){
1638                 a.target = this.target;
1639             }
1640             
1641         }
1642         
1643         return (this.href) ? a : cfg;
1644     },
1645     
1646     initEvents: function() 
1647     {
1648         if(!this.href){
1649             this.el.on('click', this.onClick, this);
1650         }
1651         
1652     },
1653     
1654     onClick : function(e)
1655     {
1656         Roo.log('img onclick');
1657         this.fireEvent('click', this, e);
1658     },
1659     /**
1660      * Sets the url of the image - used to update it
1661      * @param {String} url the url of the image
1662      */
1663     
1664     setSrc : function(url)
1665     {
1666         this.src =  url;
1667         
1668         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1669             this.el.dom.src =  url;
1670             return;
1671         }
1672         
1673         this.el.select('img', true).first().dom.src =  url;
1674     }
1675     
1676     
1677    
1678 });
1679
1680  /*
1681  * - LGPL
1682  *
1683  * image
1684  * 
1685  */
1686
1687
1688 /**
1689  * @class Roo.bootstrap.Link
1690  * @extends Roo.bootstrap.Component
1691  * Bootstrap Link Class
1692  * @cfg {String} alt image alternative text
1693  * @cfg {String} href a tag href
1694  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1695  * @cfg {String} html the content of the link.
1696  * @cfg {String} anchor name for the anchor link
1697  * @cfg {String} fa - favicon
1698
1699  * @cfg {Boolean} preventDefault (true | false) default false
1700
1701  * 
1702  * @constructor
1703  * Create a new Input
1704  * @param {Object} config The config object
1705  */
1706
1707 Roo.bootstrap.Link = function(config){
1708     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1709     
1710     this.addEvents({
1711         // img events
1712         /**
1713          * @event click
1714          * The img click event for the img.
1715          * @param {Roo.EventObject} e
1716          */
1717         "click" : true
1718     });
1719 };
1720
1721 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1722     
1723     href: false,
1724     target: false,
1725     preventDefault: false,
1726     anchor : false,
1727     alt : false,
1728     fa: false,
1729
1730
1731     getAutoCreate : function()
1732     {
1733         var html = this.html || '';
1734         
1735         if (this.fa !== false) {
1736             html = '<i class="fa fa-' + this.fa + '"></i>';
1737         }
1738         var cfg = {
1739             tag: 'a'
1740         };
1741         // anchor's do not require html/href...
1742         if (this.anchor === false) {
1743             cfg.html = html;
1744             cfg.href = this.href || '#';
1745         } else {
1746             cfg.name = this.anchor;
1747             if (this.html !== false || this.fa !== false) {
1748                 cfg.html = html;
1749             }
1750             if (this.href !== false) {
1751                 cfg.href = this.href;
1752             }
1753         }
1754         
1755         if(this.alt !== false){
1756             cfg.alt = this.alt;
1757         }
1758         
1759         
1760         if(this.target !== false) {
1761             cfg.target = this.target;
1762         }
1763         
1764         return cfg;
1765     },
1766     
1767     initEvents: function() {
1768         
1769         if(!this.href || this.preventDefault){
1770             this.el.on('click', this.onClick, this);
1771         }
1772     },
1773     
1774     onClick : function(e)
1775     {
1776         if(this.preventDefault){
1777             e.preventDefault();
1778         }
1779         //Roo.log('img onclick');
1780         this.fireEvent('click', this, e);
1781     }
1782    
1783 });
1784
1785  /*
1786  * - LGPL
1787  *
1788  * header
1789  * 
1790  */
1791
1792 /**
1793  * @class Roo.bootstrap.Header
1794  * @extends Roo.bootstrap.Component
1795  * Bootstrap Header class
1796  * @cfg {String} html content of header
1797  * @cfg {Number} level (1|2|3|4|5|6) default 1
1798  * 
1799  * @constructor
1800  * Create a new Header
1801  * @param {Object} config The config object
1802  */
1803
1804
1805 Roo.bootstrap.Header  = function(config){
1806     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1807 };
1808
1809 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1810     
1811     //href : false,
1812     html : false,
1813     level : 1,
1814     
1815     
1816     
1817     getAutoCreate : function(){
1818         
1819         
1820         
1821         var cfg = {
1822             tag: 'h' + (1 *this.level),
1823             html: this.html || ''
1824         } ;
1825         
1826         return cfg;
1827     }
1828    
1829 });
1830
1831  
1832
1833  /*
1834  * Based on:
1835  * Ext JS Library 1.1.1
1836  * Copyright(c) 2006-2007, Ext JS, LLC.
1837  *
1838  * Originally Released Under LGPL - original licence link has changed is not relivant.
1839  *
1840  * Fork - LGPL
1841  * <script type="text/javascript">
1842  */
1843  
1844 /**
1845  * @class Roo.bootstrap.MenuMgr
1846  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1847  * @singleton
1848  */
1849 Roo.bootstrap.MenuMgr = function(){
1850    var menus, active, groups = {}, attached = false, lastShow = new Date();
1851
1852    // private - called when first menu is created
1853    function init(){
1854        menus = {};
1855        active = new Roo.util.MixedCollection();
1856        Roo.get(document).addKeyListener(27, function(){
1857            if(active.length > 0){
1858                hideAll();
1859            }
1860        });
1861    }
1862
1863    // private
1864    function hideAll(){
1865        if(active && active.length > 0){
1866            var c = active.clone();
1867            c.each(function(m){
1868                m.hide();
1869            });
1870        }
1871    }
1872
1873    // private
1874    function onHide(m){
1875        active.remove(m);
1876        if(active.length < 1){
1877            Roo.get(document).un("mouseup", onMouseDown);
1878             
1879            attached = false;
1880        }
1881    }
1882
1883    // private
1884    function onShow(m){
1885        var last = active.last();
1886        lastShow = new Date();
1887        active.add(m);
1888        if(!attached){
1889           Roo.get(document).on("mouseup", onMouseDown);
1890            
1891            attached = true;
1892        }
1893        if(m.parentMenu){
1894           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1895           m.parentMenu.activeChild = m;
1896        }else if(last && last.isVisible()){
1897           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1898        }
1899    }
1900
1901    // private
1902    function onBeforeHide(m){
1903        if(m.activeChild){
1904            m.activeChild.hide();
1905        }
1906        if(m.autoHideTimer){
1907            clearTimeout(m.autoHideTimer);
1908            delete m.autoHideTimer;
1909        }
1910    }
1911
1912    // private
1913    function onBeforeShow(m){
1914        var pm = m.parentMenu;
1915        if(!pm && !m.allowOtherMenus){
1916            hideAll();
1917        }else if(pm && pm.activeChild && active != m){
1918            pm.activeChild.hide();
1919        }
1920    }
1921
1922    // private this should really trigger on mouseup..
1923    function onMouseDown(e){
1924         Roo.log("on Mouse Up");
1925         
1926         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1927             Roo.log("MenuManager hideAll");
1928             hideAll();
1929             e.stopEvent();
1930         }
1931         
1932         
1933    }
1934
1935    // private
1936    function onBeforeCheck(mi, state){
1937        if(state){
1938            var g = groups[mi.group];
1939            for(var i = 0, l = g.length; i < l; i++){
1940                if(g[i] != mi){
1941                    g[i].setChecked(false);
1942                }
1943            }
1944        }
1945    }
1946
1947    return {
1948
1949        /**
1950         * Hides all menus that are currently visible
1951         */
1952        hideAll : function(){
1953             hideAll();  
1954        },
1955
1956        // private
1957        register : function(menu){
1958            if(!menus){
1959                init();
1960            }
1961            menus[menu.id] = menu;
1962            menu.on("beforehide", onBeforeHide);
1963            menu.on("hide", onHide);
1964            menu.on("beforeshow", onBeforeShow);
1965            menu.on("show", onShow);
1966            var g = menu.group;
1967            if(g && menu.events["checkchange"]){
1968                if(!groups[g]){
1969                    groups[g] = [];
1970                }
1971                groups[g].push(menu);
1972                menu.on("checkchange", onCheck);
1973            }
1974        },
1975
1976         /**
1977          * Returns a {@link Roo.menu.Menu} object
1978          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1979          * be used to generate and return a new Menu instance.
1980          */
1981        get : function(menu){
1982            if(typeof menu == "string"){ // menu id
1983                return menus[menu];
1984            }else if(menu.events){  // menu instance
1985                return menu;
1986            }
1987            /*else if(typeof menu.length == 'number'){ // array of menu items?
1988                return new Roo.bootstrap.Menu({items:menu});
1989            }else{ // otherwise, must be a config
1990                return new Roo.bootstrap.Menu(menu);
1991            }
1992            */
1993            return false;
1994        },
1995
1996        // private
1997        unregister : function(menu){
1998            delete menus[menu.id];
1999            menu.un("beforehide", onBeforeHide);
2000            menu.un("hide", onHide);
2001            menu.un("beforeshow", onBeforeShow);
2002            menu.un("show", onShow);
2003            var g = menu.group;
2004            if(g && menu.events["checkchange"]){
2005                groups[g].remove(menu);
2006                menu.un("checkchange", onCheck);
2007            }
2008        },
2009
2010        // private
2011        registerCheckable : function(menuItem){
2012            var g = menuItem.group;
2013            if(g){
2014                if(!groups[g]){
2015                    groups[g] = [];
2016                }
2017                groups[g].push(menuItem);
2018                menuItem.on("beforecheckchange", onBeforeCheck);
2019            }
2020        },
2021
2022        // private
2023        unregisterCheckable : function(menuItem){
2024            var g = menuItem.group;
2025            if(g){
2026                groups[g].remove(menuItem);
2027                menuItem.un("beforecheckchange", onBeforeCheck);
2028            }
2029        }
2030    };
2031 }();/*
2032  * - LGPL
2033  *
2034  * menu
2035  * 
2036  */
2037
2038 /**
2039  * @class Roo.bootstrap.Menu
2040  * @extends Roo.bootstrap.Component
2041  * Bootstrap Menu class - container for MenuItems
2042  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2043  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2044  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2045  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2046  * 
2047  * @constructor
2048  * Create a new Menu
2049  * @param {Object} config The config object
2050  */
2051
2052
2053 Roo.bootstrap.Menu = function(config){
2054     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2055     if (this.registerMenu && this.type != 'treeview')  {
2056         Roo.bootstrap.MenuMgr.register(this);
2057     }
2058     
2059     
2060     this.addEvents({
2061         /**
2062          * @event beforeshow
2063          * Fires before this menu is displayed
2064          * @param {Roo.menu.Menu} this
2065          */
2066         beforeshow : true,
2067         /**
2068          * @event beforehide
2069          * Fires before this menu is hidden
2070          * @param {Roo.menu.Menu} this
2071          */
2072         beforehide : true,
2073         /**
2074          * @event show
2075          * Fires after this menu is displayed
2076          * @param {Roo.menu.Menu} this
2077          */
2078         show : true,
2079         /**
2080          * @event hide
2081          * Fires after this menu is hidden
2082          * @param {Roo.menu.Menu} this
2083          */
2084         hide : true,
2085         /**
2086          * @event click
2087          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2088          * @param {Roo.menu.Menu} this
2089          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2090          * @param {Roo.EventObject} e
2091          */
2092         click : true,
2093         /**
2094          * @event mouseover
2095          * Fires when the mouse is hovering over this menu
2096          * @param {Roo.menu.Menu} this
2097          * @param {Roo.EventObject} e
2098          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2099          */
2100         mouseover : true,
2101         /**
2102          * @event mouseout
2103          * Fires when the mouse exits this menu
2104          * @param {Roo.menu.Menu} this
2105          * @param {Roo.EventObject} e
2106          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2107          */
2108         mouseout : true,
2109         /**
2110          * @event itemclick
2111          * Fires when a menu item contained in this menu is clicked
2112          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2113          * @param {Roo.EventObject} e
2114          */
2115         itemclick: true
2116     });
2117     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2118 };
2119
2120 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2121     
2122    /// html : false,
2123     //align : '',
2124     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2125     type: false,
2126     /**
2127      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2128      */
2129     registerMenu : true,
2130     
2131     menuItems :false, // stores the menu items..
2132     
2133     hidden:true,
2134         
2135     parentMenu : false,
2136     
2137     stopEvent : true,
2138     
2139     isLink : false,
2140     
2141     getChildContainer : function() {
2142         return this.el;  
2143     },
2144     
2145     getAutoCreate : function(){
2146          
2147         //if (['right'].indexOf(this.align)!==-1) {
2148         //    cfg.cn[1].cls += ' pull-right'
2149         //}
2150         
2151         
2152         var cfg = {
2153             tag : 'ul',
2154             cls : 'dropdown-menu' ,
2155             style : 'z-index:1000'
2156             
2157         };
2158         
2159         if (this.type === 'submenu') {
2160             cfg.cls = 'submenu active';
2161         }
2162         if (this.type === 'treeview') {
2163             cfg.cls = 'treeview-menu';
2164         }
2165         
2166         return cfg;
2167     },
2168     initEvents : function() {
2169         
2170        // Roo.log("ADD event");
2171        // Roo.log(this.triggerEl.dom);
2172         
2173         this.triggerEl.on('click', this.onTriggerClick, this);
2174         
2175         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2176         
2177         
2178         if (this.triggerEl.hasClass('nav-item')) {
2179             // dropdown toggle on the 'a' in BS4?
2180             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2181         } else {
2182             this.triggerEl.addClass('dropdown-toggle');
2183         }
2184         if (Roo.isTouch) {
2185             this.el.on('touchstart'  , this.onTouch, this);
2186         }
2187         this.el.on('click' , this.onClick, this);
2188
2189         this.el.on("mouseover", this.onMouseOver, this);
2190         this.el.on("mouseout", this.onMouseOut, this);
2191         
2192     },
2193     
2194     findTargetItem : function(e)
2195     {
2196         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2197         if(!t){
2198             return false;
2199         }
2200         //Roo.log(t);         Roo.log(t.id);
2201         if(t && t.id){
2202             //Roo.log(this.menuitems);
2203             return this.menuitems.get(t.id);
2204             
2205             //return this.items.get(t.menuItemId);
2206         }
2207         
2208         return false;
2209     },
2210     
2211     onTouch : function(e) 
2212     {
2213         Roo.log("menu.onTouch");
2214         //e.stopEvent(); this make the user popdown broken
2215         this.onClick(e);
2216     },
2217     
2218     onClick : function(e)
2219     {
2220         Roo.log("menu.onClick");
2221         
2222         var t = this.findTargetItem(e);
2223         if(!t || t.isContainer){
2224             return;
2225         }
2226         Roo.log(e);
2227         /*
2228         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2229             if(t == this.activeItem && t.shouldDeactivate(e)){
2230                 this.activeItem.deactivate();
2231                 delete this.activeItem;
2232                 return;
2233             }
2234             if(t.canActivate){
2235                 this.setActiveItem(t, true);
2236             }
2237             return;
2238             
2239             
2240         }
2241         */
2242        
2243         Roo.log('pass click event');
2244         
2245         t.onClick(e);
2246         
2247         this.fireEvent("click", this, t, e);
2248         
2249         var _this = this;
2250         
2251         if(!t.href.length || t.href == '#'){
2252             (function() { _this.hide(); }).defer(100);
2253         }
2254         
2255     },
2256     
2257     onMouseOver : function(e){
2258         var t  = this.findTargetItem(e);
2259         //Roo.log(t);
2260         //if(t){
2261         //    if(t.canActivate && !t.disabled){
2262         //        this.setActiveItem(t, true);
2263         //    }
2264         //}
2265         
2266         this.fireEvent("mouseover", this, e, t);
2267     },
2268     isVisible : function(){
2269         return !this.hidden;
2270     },
2271      onMouseOut : function(e){
2272         var t  = this.findTargetItem(e);
2273         
2274         //if(t ){
2275         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2276         //        this.activeItem.deactivate();
2277         //        delete this.activeItem;
2278         //    }
2279         //}
2280         this.fireEvent("mouseout", this, e, t);
2281     },
2282     
2283     
2284     /**
2285      * Displays this menu relative to another element
2286      * @param {String/HTMLElement/Roo.Element} element The element to align to
2287      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2288      * the element (defaults to this.defaultAlign)
2289      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2290      */
2291     show : function(el, pos, parentMenu){
2292         this.parentMenu = parentMenu;
2293         if(!this.el){
2294             this.render();
2295         }
2296         this.fireEvent("beforeshow", this);
2297         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2298     },
2299      /**
2300      * Displays this menu at a specific xy position
2301      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2302      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2303      */
2304     showAt : function(xy, parentMenu, /* private: */_e){
2305         this.parentMenu = parentMenu;
2306         if(!this.el){
2307             this.render();
2308         }
2309         if(_e !== false){
2310             this.fireEvent("beforeshow", this);
2311             //xy = this.el.adjustForConstraints(xy);
2312         }
2313         
2314         //this.el.show();
2315         this.hideMenuItems();
2316         this.hidden = false;
2317         this.triggerEl.addClass('open');
2318         this.el.addClass('show');
2319         
2320         // reassign x when hitting right
2321         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2322             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2323         }
2324         
2325         // reassign y when hitting bottom
2326         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2327             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2328         }
2329         
2330         // but the list may align on trigger left or trigger top... should it be a properity?
2331         
2332         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2333             this.el.setXY(xy);
2334         }
2335         
2336         this.focus();
2337         this.fireEvent("show", this);
2338     },
2339     
2340     focus : function(){
2341         return;
2342         if(!this.hidden){
2343             this.doFocus.defer(50, this);
2344         }
2345     },
2346
2347     doFocus : function(){
2348         if(!this.hidden){
2349             this.focusEl.focus();
2350         }
2351     },
2352
2353     /**
2354      * Hides this menu and optionally all parent menus
2355      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2356      */
2357     hide : function(deep)
2358     {
2359         
2360         this.hideMenuItems();
2361         if(this.el && this.isVisible()){
2362             this.fireEvent("beforehide", this);
2363             if(this.activeItem){
2364                 this.activeItem.deactivate();
2365                 this.activeItem = null;
2366             }
2367             this.triggerEl.removeClass('open');;
2368             this.el.removeClass('show');
2369             this.hidden = true;
2370             this.fireEvent("hide", this);
2371         }
2372         if(deep === true && this.parentMenu){
2373             this.parentMenu.hide(true);
2374         }
2375     },
2376     
2377     onTriggerClick : function(e)
2378     {
2379         Roo.log('trigger click');
2380         
2381         var target = e.getTarget();
2382         
2383         Roo.log(target.nodeName.toLowerCase());
2384         
2385         if(target.nodeName.toLowerCase() === 'i'){
2386             e.preventDefault();
2387         }
2388         
2389     },
2390     
2391     onTriggerPress  : function(e)
2392     {
2393         Roo.log('trigger press');
2394         //Roo.log(e.getTarget());
2395        // Roo.log(this.triggerEl.dom);
2396        
2397         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2398         var pel = Roo.get(e.getTarget());
2399         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2400             Roo.log('is treeview or dropdown?');
2401             return;
2402         }
2403         
2404         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2405             return;
2406         }
2407         
2408         if (this.isVisible()) {
2409             Roo.log('hide');
2410             this.hide();
2411         } else {
2412             Roo.log('show');
2413             this.show(this.triggerEl, '?', false);
2414         }
2415         
2416         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2417             e.stopEvent();
2418         }
2419         
2420     },
2421        
2422     
2423     hideMenuItems : function()
2424     {
2425         Roo.log("hide Menu Items");
2426         if (!this.el) { 
2427             return;
2428         }
2429         //$(backdrop).remove()
2430         this.el.select('.open',true).each(function(aa) {
2431             
2432             aa.removeClass('open');
2433           //var parent = getParent($(this))
2434           //var relatedTarget = { relatedTarget: this }
2435           
2436            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2437           //if (e.isDefaultPrevented()) return
2438            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2439         });
2440     },
2441     addxtypeChild : function (tree, cntr) {
2442         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2443           
2444         this.menuitems.add(comp);
2445         return comp;
2446
2447     },
2448     getEl : function()
2449     {
2450         Roo.log(this.el);
2451         return this.el;
2452     },
2453     
2454     clear : function()
2455     {
2456         this.getEl().dom.innerHTML = '';
2457         this.menuitems.clear();
2458     }
2459 });
2460
2461  
2462  /*
2463  * - LGPL
2464  *
2465  * menu item
2466  * 
2467  */
2468
2469
2470 /**
2471  * @class Roo.bootstrap.MenuItem
2472  * @extends Roo.bootstrap.Component
2473  * Bootstrap MenuItem class
2474  * @cfg {String} html the menu label
2475  * @cfg {String} href the link
2476  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2477  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2478  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2479  * @cfg {String} fa favicon to show on left of menu item.
2480  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2481  * 
2482  * 
2483  * @constructor
2484  * Create a new MenuItem
2485  * @param {Object} config The config object
2486  */
2487
2488
2489 Roo.bootstrap.MenuItem = function(config){
2490     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2491     this.addEvents({
2492         // raw events
2493         /**
2494          * @event click
2495          * The raw click event for the entire grid.
2496          * @param {Roo.bootstrap.MenuItem} this
2497          * @param {Roo.EventObject} e
2498          */
2499         "click" : true
2500     });
2501 };
2502
2503 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2504     
2505     href : false,
2506     html : false,
2507     preventDefault: false,
2508     isContainer : false,
2509     active : false,
2510     fa: false,
2511     
2512     getAutoCreate : function(){
2513         
2514         if(this.isContainer){
2515             return {
2516                 tag: 'li',
2517                 cls: 'dropdown-menu-item '
2518             };
2519         }
2520         var ctag = {
2521             tag: 'span',
2522             html: 'Link'
2523         };
2524         
2525         var anc = {
2526             tag : 'a',
2527             cls : 'dropdown-item',
2528             href : '#',
2529             cn : [  ]
2530         };
2531         
2532         if (this.fa !== false) {
2533             anc.cn.push({
2534                 tag : 'i',
2535                 cls : 'fa fa-' + this.fa
2536             });
2537         }
2538         
2539         anc.cn.push(ctag);
2540         
2541         
2542         var cfg= {
2543             tag: 'li',
2544             cls: 'dropdown-menu-item',
2545             cn: [ anc ]
2546         };
2547         if (this.parent().type == 'treeview') {
2548             cfg.cls = 'treeview-menu';
2549         }
2550         if (this.active) {
2551             cfg.cls += ' active';
2552         }
2553         
2554         
2555         
2556         anc.href = this.href || cfg.cn[0].href ;
2557         ctag.html = this.html || cfg.cn[0].html ;
2558         return cfg;
2559     },
2560     
2561     initEvents: function()
2562     {
2563         if (this.parent().type == 'treeview') {
2564             this.el.select('a').on('click', this.onClick, this);
2565         }
2566         
2567         if (this.menu) {
2568             this.menu.parentType = this.xtype;
2569             this.menu.triggerEl = this.el;
2570             this.menu = this.addxtype(Roo.apply({}, this.menu));
2571         }
2572         
2573     },
2574     onClick : function(e)
2575     {
2576         Roo.log('item on click ');
2577         
2578         if(this.preventDefault){
2579             e.preventDefault();
2580         }
2581         //this.parent().hideMenuItems();
2582         
2583         this.fireEvent('click', this, e);
2584     },
2585     getEl : function()
2586     {
2587         return this.el;
2588     } 
2589 });
2590
2591  
2592
2593  /*
2594  * - LGPL
2595  *
2596  * menu separator
2597  * 
2598  */
2599
2600
2601 /**
2602  * @class Roo.bootstrap.MenuSeparator
2603  * @extends Roo.bootstrap.Component
2604  * Bootstrap MenuSeparator class
2605  * 
2606  * @constructor
2607  * Create a new MenuItem
2608  * @param {Object} config The config object
2609  */
2610
2611
2612 Roo.bootstrap.MenuSeparator = function(config){
2613     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2614 };
2615
2616 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2617     
2618     getAutoCreate : function(){
2619         var cfg = {
2620             cls: 'divider',
2621             tag : 'li'
2622         };
2623         
2624         return cfg;
2625     }
2626    
2627 });
2628
2629  
2630
2631  
2632 /*
2633 * Licence: LGPL
2634 */
2635
2636 /**
2637  * @class Roo.bootstrap.Modal
2638  * @extends Roo.bootstrap.Component
2639  * Bootstrap Modal class
2640  * @cfg {String} title Title of dialog
2641  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2642  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2643  * @cfg {Boolean} specificTitle default false
2644  * @cfg {Array} buttons Array of buttons or standard button set..
2645  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2646  * @cfg {Boolean} animate default true
2647  * @cfg {Boolean} allow_close default true
2648  * @cfg {Boolean} fitwindow default false
2649  * @cfg {String} size (sm|lg) default empty
2650  * @cfg {Number} max_width set the max width of modal
2651  *
2652  *
2653  * @constructor
2654  * Create a new Modal Dialog
2655  * @param {Object} config The config object
2656  */
2657
2658 Roo.bootstrap.Modal = function(config){
2659     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2660     this.addEvents({
2661         // raw events
2662         /**
2663          * @event btnclick
2664          * The raw btnclick event for the button
2665          * @param {Roo.EventObject} e
2666          */
2667         "btnclick" : true,
2668         /**
2669          * @event resize
2670          * Fire when dialog resize
2671          * @param {Roo.bootstrap.Modal} this
2672          * @param {Roo.EventObject} e
2673          */
2674         "resize" : true
2675     });
2676     this.buttons = this.buttons || [];
2677
2678     if (this.tmpl) {
2679         this.tmpl = Roo.factory(this.tmpl);
2680     }
2681
2682 };
2683
2684 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2685
2686     title : 'test dialog',
2687
2688     buttons : false,
2689
2690     // set on load...
2691
2692     html: false,
2693
2694     tmp: false,
2695
2696     specificTitle: false,
2697
2698     buttonPosition: 'right',
2699
2700     allow_close : true,
2701
2702     animate : true,
2703
2704     fitwindow: false,
2705     
2706      // private
2707     dialogEl: false,
2708     bodyEl:  false,
2709     footerEl:  false,
2710     titleEl:  false,
2711     closeEl:  false,
2712
2713     size: '',
2714     
2715     max_width: 0,
2716     
2717     max_height: 0,
2718     
2719     fit_content: false,
2720
2721     onRender : function(ct, position)
2722     {
2723         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2724
2725         if(!this.el){
2726             var cfg = Roo.apply({},  this.getAutoCreate());
2727             cfg.id = Roo.id();
2728             //if(!cfg.name){
2729             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2730             //}
2731             //if (!cfg.name.length) {
2732             //    delete cfg.name;
2733            // }
2734             if (this.cls) {
2735                 cfg.cls += ' ' + this.cls;
2736             }
2737             if (this.style) {
2738                 cfg.style = this.style;
2739             }
2740             this.el = Roo.get(document.body).createChild(cfg, position);
2741         }
2742         //var type = this.el.dom.type;
2743
2744
2745         if(this.tabIndex !== undefined){
2746             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2747         }
2748
2749         this.dialogEl = this.el.select('.modal-dialog',true).first();
2750         this.bodyEl = this.el.select('.modal-body',true).first();
2751         this.closeEl = this.el.select('.modal-header .close', true).first();
2752         this.headerEl = this.el.select('.modal-header',true).first();
2753         this.titleEl = this.el.select('.modal-title',true).first();
2754         this.footerEl = this.el.select('.modal-footer',true).first();
2755
2756         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2757         
2758         //this.el.addClass("x-dlg-modal");
2759
2760         if (this.buttons.length) {
2761             Roo.each(this.buttons, function(bb) {
2762                 var b = Roo.apply({}, bb);
2763                 b.xns = b.xns || Roo.bootstrap;
2764                 b.xtype = b.xtype || 'Button';
2765                 if (typeof(b.listeners) == 'undefined') {
2766                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2767                 }
2768
2769                 var btn = Roo.factory(b);
2770
2771                 btn.render(this.getButtonContainer());
2772
2773             },this);
2774         }
2775         // render the children.
2776         var nitems = [];
2777
2778         if(typeof(this.items) != 'undefined'){
2779             var items = this.items;
2780             delete this.items;
2781
2782             for(var i =0;i < items.length;i++) {
2783                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2784             }
2785         }
2786
2787         this.items = nitems;
2788
2789         // where are these used - they used to be body/close/footer
2790
2791
2792         this.initEvents();
2793         //this.el.addClass([this.fieldClass, this.cls]);
2794
2795     },
2796
2797     getAutoCreate : function()
2798     {
2799         var bdy = {
2800                 cls : 'modal-body',
2801                 html : this.html || ''
2802         };
2803
2804         var title = {
2805             tag: 'h4',
2806             cls : 'modal-title',
2807             html : this.title
2808         };
2809
2810         if(this.specificTitle){
2811             title = this.title;
2812
2813         };
2814
2815         var header = [];
2816         if (this.allow_close && Roo.bootstrap.version == 3) {
2817             header.push({
2818                 tag: 'button',
2819                 cls : 'close',
2820                 html : '&times'
2821             });
2822         }
2823
2824         header.push(title);
2825
2826         if (this.allow_close && Roo.bootstrap.version == 4) {
2827             header.push({
2828                 tag: 'button',
2829                 cls : 'close',
2830                 html : '&times'
2831             });
2832         }
2833         
2834         var size = '';
2835
2836         if(this.size.length){
2837             size = 'modal-' + this.size;
2838         }
2839         
2840         var footer = Roo.bootstrap.version == 3 ?
2841             {
2842                 cls : 'modal-footer',
2843                 cn : [
2844                     {
2845                         tag: 'div',
2846                         cls: 'btn-' + this.buttonPosition
2847                     }
2848                 ]
2849
2850             } :
2851             {  // BS4 uses mr-auto on left buttons....
2852                 cls : 'modal-footer'
2853             };
2854
2855             
2856
2857         
2858         
2859         var modal = {
2860             cls: "modal",
2861              cn : [
2862                 {
2863                     cls: "modal-dialog " + size,
2864                     cn : [
2865                         {
2866                             cls : "modal-content",
2867                             cn : [
2868                                 {
2869                                     cls : 'modal-header',
2870                                     cn : header
2871                                 },
2872                                 bdy,
2873                                 footer
2874                             ]
2875
2876                         }
2877                     ]
2878
2879                 }
2880             ]
2881         };
2882
2883         if(this.animate){
2884             modal.cls += ' fade';
2885         }
2886
2887         return modal;
2888
2889     },
2890     getChildContainer : function() {
2891
2892          return this.bodyEl;
2893
2894     },
2895     getButtonContainer : function() {
2896         
2897          return Roo.bootstrap.version == 4 ?
2898             this.el.select('.modal-footer',true).first()
2899             : this.el.select('.modal-footer div',true).first();
2900
2901     },
2902     initEvents : function()
2903     {
2904         if (this.allow_close) {
2905             this.closeEl.on('click', this.hide, this);
2906         }
2907         Roo.EventManager.onWindowResize(this.resize, this, true);
2908
2909
2910     },
2911
2912     resize : function()
2913     {
2914         this.maskEl.setSize(
2915             Roo.lib.Dom.getViewWidth(true),
2916             Roo.lib.Dom.getViewHeight(true)
2917         );
2918         
2919         if (this.fitwindow) {
2920             this.setSize(
2921                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2922                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2923             );
2924             return;
2925         }
2926         
2927         if(this.max_width !== 0) {
2928             
2929             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2930             
2931             if(this.height) {
2932                 this.setSize(w, this.height);
2933                 return;
2934             }
2935             
2936             if(this.max_height) {
2937                 this.setSize(w,Math.min(
2938                     this.max_height,
2939                     Roo.lib.Dom.getViewportHeight(true) - 60
2940                 ));
2941                 
2942                 return;
2943             }
2944             
2945             if(!this.fit_content) {
2946                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2947                 return;
2948             }
2949             
2950             this.setSize(w, Math.min(
2951                 60 +
2952                 this.headerEl.getHeight() + 
2953                 this.footerEl.getHeight() + 
2954                 this.getChildHeight(this.bodyEl.dom.childNodes),
2955                 Roo.lib.Dom.getViewportHeight(true) - 60)
2956             );
2957         }
2958         
2959     },
2960
2961     setSize : function(w,h)
2962     {
2963         if (!w && !h) {
2964             return;
2965         }
2966         
2967         this.resizeTo(w,h);
2968     },
2969
2970     show : function() {
2971
2972         if (!this.rendered) {
2973             this.render();
2974         }
2975
2976         //this.el.setStyle('display', 'block');
2977         this.el.removeClass('hideing');
2978         this.el.dom.style.display='block';
2979         
2980         Roo.get(document.body).addClass('modal-open');
2981  
2982         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2983             var _this = this;
2984             (function(){
2985                 this.el.addClass('show');
2986                 this.el.addClass('in');
2987             }).defer(50, this);
2988         }else{
2989             this.el.addClass('show');
2990             this.el.addClass('in');
2991         }
2992
2993         // not sure how we can show data in here..
2994         //if (this.tmpl) {
2995         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2996         //}
2997
2998         Roo.get(document.body).addClass("x-body-masked");
2999         
3000         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
3001         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3002         this.maskEl.dom.style.display = 'block';
3003         this.maskEl.addClass('show');
3004         
3005         
3006         this.resize();
3007         
3008         this.fireEvent('show', this);
3009
3010         // set zindex here - otherwise it appears to be ignored...
3011         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3012
3013         (function () {
3014             this.items.forEach( function(e) {
3015                 e.layout ? e.layout() : false;
3016
3017             });
3018         }).defer(100,this);
3019
3020     },
3021     hide : function()
3022     {
3023         if(this.fireEvent("beforehide", this) !== false){
3024             
3025             this.maskEl.removeClass('show');
3026             
3027             this.maskEl.dom.style.display = '';
3028             Roo.get(document.body).removeClass("x-body-masked");
3029             this.el.removeClass('in');
3030             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3031
3032             if(this.animate){ // why
3033                 this.el.addClass('hideing');
3034                 this.el.removeClass('show');
3035                 (function(){
3036                     if (!this.el.hasClass('hideing')) {
3037                         return; // it's been shown again...
3038                     }
3039                     
3040                     this.el.dom.style.display='';
3041
3042                     Roo.get(document.body).removeClass('modal-open');
3043                     this.el.removeClass('hideing');
3044                 }).defer(150,this);
3045                 
3046             }else{
3047                 this.el.removeClass('show');
3048                 this.el.dom.style.display='';
3049                 Roo.get(document.body).removeClass('modal-open');
3050
3051             }
3052             this.fireEvent('hide', this);
3053         }
3054     },
3055     isVisible : function()
3056     {
3057         
3058         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3059         
3060     },
3061
3062     addButton : function(str, cb)
3063     {
3064
3065
3066         var b = Roo.apply({}, { html : str } );
3067         b.xns = b.xns || Roo.bootstrap;
3068         b.xtype = b.xtype || 'Button';
3069         if (typeof(b.listeners) == 'undefined') {
3070             b.listeners = { click : cb.createDelegate(this)  };
3071         }
3072
3073         var btn = Roo.factory(b);
3074
3075         btn.render(this.getButtonContainer());
3076
3077         return btn;
3078
3079     },
3080
3081     setDefaultButton : function(btn)
3082     {
3083         //this.el.select('.modal-footer').()
3084     },
3085     diff : false,
3086
3087     resizeTo: function(w,h)
3088     {
3089         // skip.. ?? why??
3090
3091         this.dialogEl.setWidth(w);
3092         if (this.diff === false) {
3093             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
3094         }
3095
3096         this.bodyEl.setHeight(h - this.diff);
3097
3098         this.fireEvent('resize', this);
3099
3100     },
3101     setContentSize  : function(w, h)
3102     {
3103
3104     },
3105     onButtonClick: function(btn,e)
3106     {
3107         //Roo.log([a,b,c]);
3108         this.fireEvent('btnclick', btn.name, e);
3109     },
3110      /**
3111      * Set the title of the Dialog
3112      * @param {String} str new Title
3113      */
3114     setTitle: function(str) {
3115         this.titleEl.dom.innerHTML = str;
3116     },
3117     /**
3118      * Set the body of the Dialog
3119      * @param {String} str new Title
3120      */
3121     setBody: function(str) {
3122         this.bodyEl.dom.innerHTML = str;
3123     },
3124     /**
3125      * Set the body of the Dialog using the template
3126      * @param {Obj} data - apply this data to the template and replace the body contents.
3127      */
3128     applyBody: function(obj)
3129     {
3130         if (!this.tmpl) {
3131             Roo.log("Error - using apply Body without a template");
3132             //code
3133         }
3134         this.tmpl.overwrite(this.bodyEl, obj);
3135     },
3136     
3137     getChildHeight : function(child_nodes)
3138     {
3139         if(
3140             !child_nodes ||
3141             child_nodes.length == 0
3142         ) {
3143             return;
3144         }
3145         
3146         var child_height = 0;
3147         
3148         for(var i = 0; i < child_nodes.length; i++) {
3149             
3150             /*
3151             * for modal with tabs...
3152             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3153                 
3154                 var layout_childs = child_nodes[i].childNodes;
3155                 
3156                 for(var j = 0; j < layout_childs.length; j++) {
3157                     
3158                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3159                         
3160                         var layout_body_childs = layout_childs[j].childNodes;
3161                         
3162                         for(var k = 0; k < layout_body_childs.length; k++) {
3163                             
3164                             if(layout_body_childs[k].classList.contains('navbar')) {
3165                                 child_height += layout_body_childs[k].offsetHeight;
3166                                 continue;
3167                             }
3168                             
3169                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3170                                 
3171                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3172                                 
3173                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3174                                     
3175                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3176                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3177                                         continue;
3178                                     }
3179                                     
3180                                 }
3181                                 
3182                             }
3183                             
3184                         }
3185                     }
3186                 }
3187                 continue;
3188             }
3189             */
3190             
3191             child_height += child_nodes[i].offsetHeight;
3192             // Roo.log(child_nodes[i].offsetHeight);
3193         }
3194         
3195         return child_height;
3196     }
3197
3198 });
3199
3200
3201 Roo.apply(Roo.bootstrap.Modal,  {
3202     /**
3203          * Button config that displays a single OK button
3204          * @type Object
3205          */
3206         OK :  [{
3207             name : 'ok',
3208             weight : 'primary',
3209             html : 'OK'
3210         }],
3211         /**
3212          * Button config that displays Yes and No buttons
3213          * @type Object
3214          */
3215         YESNO : [
3216             {
3217                 name  : 'no',
3218                 html : 'No'
3219             },
3220             {
3221                 name  :'yes',
3222                 weight : 'primary',
3223                 html : 'Yes'
3224             }
3225         ],
3226
3227         /**
3228          * Button config that displays OK and Cancel buttons
3229          * @type Object
3230          */
3231         OKCANCEL : [
3232             {
3233                name : 'cancel',
3234                 html : 'Cancel'
3235             },
3236             {
3237                 name : 'ok',
3238                 weight : 'primary',
3239                 html : 'OK'
3240             }
3241         ],
3242         /**
3243          * Button config that displays Yes, No and Cancel buttons
3244          * @type Object
3245          */
3246         YESNOCANCEL : [
3247             {
3248                 name : 'yes',
3249                 weight : 'primary',
3250                 html : 'Yes'
3251             },
3252             {
3253                 name : 'no',
3254                 html : 'No'
3255             },
3256             {
3257                 name : 'cancel',
3258                 html : 'Cancel'
3259             }
3260         ],
3261         
3262         zIndex : 10001
3263 });
3264 /*
3265  * - LGPL
3266  *
3267  * messagebox - can be used as a replace
3268  * 
3269  */
3270 /**
3271  * @class Roo.MessageBox
3272  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3273  * Example usage:
3274  *<pre><code>
3275 // Basic alert:
3276 Roo.Msg.alert('Status', 'Changes saved successfully.');
3277
3278 // Prompt for user data:
3279 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3280     if (btn == 'ok'){
3281         // process text value...
3282     }
3283 });
3284
3285 // Show a dialog using config options:
3286 Roo.Msg.show({
3287    title:'Save Changes?',
3288    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3289    buttons: Roo.Msg.YESNOCANCEL,
3290    fn: processResult,
3291    animEl: 'elId'
3292 });
3293 </code></pre>
3294  * @singleton
3295  */
3296 Roo.bootstrap.MessageBox = function(){
3297     var dlg, opt, mask, waitTimer;
3298     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3299     var buttons, activeTextEl, bwidth;
3300
3301     
3302     // private
3303     var handleButton = function(button){
3304         dlg.hide();
3305         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3306     };
3307
3308     // private
3309     var handleHide = function(){
3310         if(opt && opt.cls){
3311             dlg.el.removeClass(opt.cls);
3312         }
3313         //if(waitTimer){
3314         //    Roo.TaskMgr.stop(waitTimer);
3315         //    waitTimer = null;
3316         //}
3317     };
3318
3319     // private
3320     var updateButtons = function(b){
3321         var width = 0;
3322         if(!b){
3323             buttons["ok"].hide();
3324             buttons["cancel"].hide();
3325             buttons["yes"].hide();
3326             buttons["no"].hide();
3327             dlg.footerEl.hide();
3328             
3329             return width;
3330         }
3331         dlg.footerEl.show();
3332         for(var k in buttons){
3333             if(typeof buttons[k] != "function"){
3334                 if(b[k]){
3335                     buttons[k].show();
3336                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3337                     width += buttons[k].el.getWidth()+15;
3338                 }else{
3339                     buttons[k].hide();
3340                 }
3341             }
3342         }
3343         return width;
3344     };
3345
3346     // private
3347     var handleEsc = function(d, k, e){
3348         if(opt && opt.closable !== false){
3349             dlg.hide();
3350         }
3351         if(e){
3352             e.stopEvent();
3353         }
3354     };
3355
3356     return {
3357         /**
3358          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3359          * @return {Roo.BasicDialog} The BasicDialog element
3360          */
3361         getDialog : function(){
3362            if(!dlg){
3363                 dlg = new Roo.bootstrap.Modal( {
3364                     //draggable: true,
3365                     //resizable:false,
3366                     //constraintoviewport:false,
3367                     //fixedcenter:true,
3368                     //collapsible : false,
3369                     //shim:true,
3370                     //modal: true,
3371                 //    width: 'auto',
3372                   //  height:100,
3373                     //buttonAlign:"center",
3374                     closeClick : function(){
3375                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3376                             handleButton("no");
3377                         }else{
3378                             handleButton("cancel");
3379                         }
3380                     }
3381                 });
3382                 dlg.render();
3383                 dlg.on("hide", handleHide);
3384                 mask = dlg.mask;
3385                 //dlg.addKeyListener(27, handleEsc);
3386                 buttons = {};
3387                 this.buttons = buttons;
3388                 var bt = this.buttonText;
3389                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3390                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3391                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3392                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3393                 //Roo.log(buttons);
3394                 bodyEl = dlg.bodyEl.createChild({
3395
3396                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3397                         '<textarea class="roo-mb-textarea"></textarea>' +
3398                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3399                 });
3400                 msgEl = bodyEl.dom.firstChild;
3401                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3402                 textboxEl.enableDisplayMode();
3403                 textboxEl.addKeyListener([10,13], function(){
3404                     if(dlg.isVisible() && opt && opt.buttons){
3405                         if(opt.buttons.ok){
3406                             handleButton("ok");
3407                         }else if(opt.buttons.yes){
3408                             handleButton("yes");
3409                         }
3410                     }
3411                 });
3412                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3413                 textareaEl.enableDisplayMode();
3414                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3415                 progressEl.enableDisplayMode();
3416                 
3417                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3418                 var pf = progressEl.dom.firstChild;
3419                 if (pf) {
3420                     pp = Roo.get(pf.firstChild);
3421                     pp.setHeight(pf.offsetHeight);
3422                 }
3423                 
3424             }
3425             return dlg;
3426         },
3427
3428         /**
3429          * Updates the message box body text
3430          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3431          * the XHTML-compliant non-breaking space character '&amp;#160;')
3432          * @return {Roo.MessageBox} This message box
3433          */
3434         updateText : function(text)
3435         {
3436             if(!dlg.isVisible() && !opt.width){
3437                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3438                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3439             }
3440             msgEl.innerHTML = text || '&#160;';
3441       
3442             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3443             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3444             var w = Math.max(
3445                     Math.min(opt.width || cw , this.maxWidth), 
3446                     Math.max(opt.minWidth || this.minWidth, bwidth)
3447             );
3448             if(opt.prompt){
3449                 activeTextEl.setWidth(w);
3450             }
3451             if(dlg.isVisible()){
3452                 dlg.fixedcenter = false;
3453             }
3454             // to big, make it scroll. = But as usual stupid IE does not support
3455             // !important..
3456             
3457             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3458                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3459                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3460             } else {
3461                 bodyEl.dom.style.height = '';
3462                 bodyEl.dom.style.overflowY = '';
3463             }
3464             if (cw > w) {
3465                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3466             } else {
3467                 bodyEl.dom.style.overflowX = '';
3468             }
3469             
3470             dlg.setContentSize(w, bodyEl.getHeight());
3471             if(dlg.isVisible()){
3472                 dlg.fixedcenter = true;
3473             }
3474             return this;
3475         },
3476
3477         /**
3478          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3479          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3480          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3481          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3482          * @return {Roo.MessageBox} This message box
3483          */
3484         updateProgress : function(value, text){
3485             if(text){
3486                 this.updateText(text);
3487             }
3488             
3489             if (pp) { // weird bug on my firefox - for some reason this is not defined
3490                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3491                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3492             }
3493             return this;
3494         },        
3495
3496         /**
3497          * Returns true if the message box is currently displayed
3498          * @return {Boolean} True if the message box is visible, else false
3499          */
3500         isVisible : function(){
3501             return dlg && dlg.isVisible();  
3502         },
3503
3504         /**
3505          * Hides the message box if it is displayed
3506          */
3507         hide : function(){
3508             if(this.isVisible()){
3509                 dlg.hide();
3510             }  
3511         },
3512
3513         /**
3514          * Displays a new message box, or reinitializes an existing message box, based on the config options
3515          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3516          * The following config object properties are supported:
3517          * <pre>
3518 Property    Type             Description
3519 ----------  ---------------  ------------------------------------------------------------------------------------
3520 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3521                                    closes (defaults to undefined)
3522 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3523                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3524 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3525                                    progress and wait dialogs will ignore this property and always hide the
3526                                    close button as they can only be closed programmatically.
3527 cls               String           A custom CSS class to apply to the message box element
3528 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3529                                    displayed (defaults to 75)
3530 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3531                                    function will be btn (the name of the button that was clicked, if applicable,
3532                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3533                                    Progress and wait dialogs will ignore this option since they do not respond to
3534                                    user actions and can only be closed programmatically, so any required function
3535                                    should be called by the same code after it closes the dialog.
3536 icon              String           A CSS class that provides a background image to be used as an icon for
3537                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3538 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3539 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3540 modal             Boolean          False to allow user interaction with the page while the message box is
3541                                    displayed (defaults to true)
3542 msg               String           A string that will replace the existing message box body text (defaults
3543                                    to the XHTML-compliant non-breaking space character '&#160;')
3544 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3545 progress          Boolean          True to display a progress bar (defaults to false)
3546 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3547 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3548 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3549 title             String           The title text
3550 value             String           The string value to set into the active textbox element if displayed
3551 wait              Boolean          True to display a progress bar (defaults to false)
3552 width             Number           The width of the dialog in pixels
3553 </pre>
3554          *
3555          * Example usage:
3556          * <pre><code>
3557 Roo.Msg.show({
3558    title: 'Address',
3559    msg: 'Please enter your address:',
3560    width: 300,
3561    buttons: Roo.MessageBox.OKCANCEL,
3562    multiline: true,
3563    fn: saveAddress,
3564    animEl: 'addAddressBtn'
3565 });
3566 </code></pre>
3567          * @param {Object} config Configuration options
3568          * @return {Roo.MessageBox} This message box
3569          */
3570         show : function(options)
3571         {
3572             
3573             // this causes nightmares if you show one dialog after another
3574             // especially on callbacks..
3575              
3576             if(this.isVisible()){
3577                 
3578                 this.hide();
3579                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3580                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3581                 Roo.log("New Dialog Message:" +  options.msg )
3582                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3583                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3584                 
3585             }
3586             var d = this.getDialog();
3587             opt = options;
3588             d.setTitle(opt.title || "&#160;");
3589             d.closeEl.setDisplayed(opt.closable !== false);
3590             activeTextEl = textboxEl;
3591             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3592             if(opt.prompt){
3593                 if(opt.multiline){
3594                     textboxEl.hide();
3595                     textareaEl.show();
3596                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3597                         opt.multiline : this.defaultTextHeight);
3598                     activeTextEl = textareaEl;
3599                 }else{
3600                     textboxEl.show();
3601                     textareaEl.hide();
3602                 }
3603             }else{
3604                 textboxEl.hide();
3605                 textareaEl.hide();
3606             }
3607             progressEl.setDisplayed(opt.progress === true);
3608             if (opt.progress) {
3609                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3610             }
3611             this.updateProgress(0);
3612             activeTextEl.dom.value = opt.value || "";
3613             if(opt.prompt){
3614                 dlg.setDefaultButton(activeTextEl);
3615             }else{
3616                 var bs = opt.buttons;
3617                 var db = null;
3618                 if(bs && bs.ok){
3619                     db = buttons["ok"];
3620                 }else if(bs && bs.yes){
3621                     db = buttons["yes"];
3622                 }
3623                 dlg.setDefaultButton(db);
3624             }
3625             bwidth = updateButtons(opt.buttons);
3626             this.updateText(opt.msg);
3627             if(opt.cls){
3628                 d.el.addClass(opt.cls);
3629             }
3630             d.proxyDrag = opt.proxyDrag === true;
3631             d.modal = opt.modal !== false;
3632             d.mask = opt.modal !== false ? mask : false;
3633             if(!d.isVisible()){
3634                 // force it to the end of the z-index stack so it gets a cursor in FF
3635                 document.body.appendChild(dlg.el.dom);
3636                 d.animateTarget = null;
3637                 d.show(options.animEl);
3638             }
3639             return this;
3640         },
3641
3642         /**
3643          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3644          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3645          * and closing the message box when the process is complete.
3646          * @param {String} title The title bar text
3647          * @param {String} msg The message box body text
3648          * @return {Roo.MessageBox} This message box
3649          */
3650         progress : function(title, msg){
3651             this.show({
3652                 title : title,
3653                 msg : msg,
3654                 buttons: false,
3655                 progress:true,
3656                 closable:false,
3657                 minWidth: this.minProgressWidth,
3658                 modal : true
3659             });
3660             return this;
3661         },
3662
3663         /**
3664          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3665          * If a callback function is passed it will be called after the user clicks the button, and the
3666          * id of the button that was clicked will be passed as the only parameter to the callback
3667          * (could also be the top-right close button).
3668          * @param {String} title The title bar text
3669          * @param {String} msg The message box body text
3670          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3671          * @param {Object} scope (optional) The scope of the callback function
3672          * @return {Roo.MessageBox} This message box
3673          */
3674         alert : function(title, msg, fn, scope)
3675         {
3676             this.show({
3677                 title : title,
3678                 msg : msg,
3679                 buttons: this.OK,
3680                 fn: fn,
3681                 closable : false,
3682                 scope : scope,
3683                 modal : true
3684             });
3685             return this;
3686         },
3687
3688         /**
3689          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3690          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3691          * You are responsible for closing the message box when the process is complete.
3692          * @param {String} msg The message box body text
3693          * @param {String} title (optional) The title bar text
3694          * @return {Roo.MessageBox} This message box
3695          */
3696         wait : function(msg, title){
3697             this.show({
3698                 title : title,
3699                 msg : msg,
3700                 buttons: false,
3701                 closable:false,
3702                 progress:true,
3703                 modal:true,
3704                 width:300,
3705                 wait:true
3706             });
3707             waitTimer = Roo.TaskMgr.start({
3708                 run: function(i){
3709                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3710                 },
3711                 interval: 1000
3712             });
3713             return this;
3714         },
3715
3716         /**
3717          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3718          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3719          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3720          * @param {String} title The title bar text
3721          * @param {String} msg The message box body text
3722          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3723          * @param {Object} scope (optional) The scope of the callback function
3724          * @return {Roo.MessageBox} This message box
3725          */
3726         confirm : function(title, msg, fn, scope){
3727             this.show({
3728                 title : title,
3729                 msg : msg,
3730                 buttons: this.YESNO,
3731                 fn: fn,
3732                 scope : scope,
3733                 modal : true
3734             });
3735             return this;
3736         },
3737
3738         /**
3739          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3740          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3741          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3742          * (could also be the top-right close button) and the text that was entered will be passed as the two
3743          * parameters to the callback.
3744          * @param {String} title The title bar text
3745          * @param {String} msg The message box body text
3746          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3747          * @param {Object} scope (optional) The scope of the callback function
3748          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3749          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3750          * @return {Roo.MessageBox} This message box
3751          */
3752         prompt : function(title, msg, fn, scope, multiline){
3753             this.show({
3754                 title : title,
3755                 msg : msg,
3756                 buttons: this.OKCANCEL,
3757                 fn: fn,
3758                 minWidth:250,
3759                 scope : scope,
3760                 prompt:true,
3761                 multiline: multiline,
3762                 modal : true
3763             });
3764             return this;
3765         },
3766
3767         /**
3768          * Button config that displays a single OK button
3769          * @type Object
3770          */
3771         OK : {ok:true},
3772         /**
3773          * Button config that displays Yes and No buttons
3774          * @type Object
3775          */
3776         YESNO : {yes:true, no:true},
3777         /**
3778          * Button config that displays OK and Cancel buttons
3779          * @type Object
3780          */
3781         OKCANCEL : {ok:true, cancel:true},
3782         /**
3783          * Button config that displays Yes, No and Cancel buttons
3784          * @type Object
3785          */
3786         YESNOCANCEL : {yes:true, no:true, cancel:true},
3787
3788         /**
3789          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3790          * @type Number
3791          */
3792         defaultTextHeight : 75,
3793         /**
3794          * The maximum width in pixels of the message box (defaults to 600)
3795          * @type Number
3796          */
3797         maxWidth : 600,
3798         /**
3799          * The minimum width in pixels of the message box (defaults to 100)
3800          * @type Number
3801          */
3802         minWidth : 100,
3803         /**
3804          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3805          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3806          * @type Number
3807          */
3808         minProgressWidth : 250,
3809         /**
3810          * An object containing the default button text strings that can be overriden for localized language support.
3811          * Supported properties are: ok, cancel, yes and no.
3812          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3813          * @type Object
3814          */
3815         buttonText : {
3816             ok : "OK",
3817             cancel : "Cancel",
3818             yes : "Yes",
3819             no : "No"
3820         }
3821     };
3822 }();
3823
3824 /**
3825  * Shorthand for {@link Roo.MessageBox}
3826  */
3827 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3828 Roo.Msg = Roo.Msg || Roo.MessageBox;
3829 /*
3830  * - LGPL
3831  *
3832  * navbar
3833  * 
3834  */
3835
3836 /**
3837  * @class Roo.bootstrap.Navbar
3838  * @extends Roo.bootstrap.Component
3839  * Bootstrap Navbar class
3840
3841  * @constructor
3842  * Create a new Navbar
3843  * @param {Object} config The config object
3844  */
3845
3846
3847 Roo.bootstrap.Navbar = function(config){
3848     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3849     this.addEvents({
3850         // raw events
3851         /**
3852          * @event beforetoggle
3853          * Fire before toggle the menu
3854          * @param {Roo.EventObject} e
3855          */
3856         "beforetoggle" : true
3857     });
3858 };
3859
3860 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3861     
3862     
3863    
3864     // private
3865     navItems : false,
3866     loadMask : false,
3867     
3868     
3869     getAutoCreate : function(){
3870         
3871         
3872         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3873         
3874     },
3875     
3876     initEvents :function ()
3877     {
3878         //Roo.log(this.el.select('.navbar-toggle',true));
3879         this.el.select('.navbar-toggle',true).on('click', function() {
3880             if(this.fireEvent('beforetoggle', this) !== false){
3881                 var ce = this.el.select('.navbar-collapse',true).first();
3882                 ce.toggleClass('in'); // old...
3883                 if (ce.hasClass('collapse')) {
3884                     // show it...
3885                     ce.removeClass('collapse');
3886                     ce.addClass('show');
3887                     var h = ce.getHeight();
3888                     Roo.log(h);
3889                     ce.removeClass('show');
3890                     // at this point we should be able to see it..
3891                     ce.addClass('collapsing');
3892                     
3893                     ce.setHeight(0); // resize it ...
3894                     ce.on('transitionend', function() {
3895                         Roo.log('done transition');
3896                         ce.removeClass('collapsing');
3897                         ce.addClass('show');
3898                         ce.removeClass('collapse');
3899
3900                         ce.dom.style.height = '';
3901                     }, this, { single: true} );
3902                     ce.setHeight(h);
3903                     
3904                 } else {
3905                     ce.setHeight(ce.getHeight());
3906                     ce.removeClass('show');
3907                     ce.addClass('collapsing');
3908                     
3909                     ce.on('transitionend', function() {
3910                         ce.dom.style.height = '';
3911                         ce.removeClass('collapsing');
3912                         ce.addClass('collapse');
3913                     }, this, { single: true} );
3914                     ce.setHeight(0);
3915                 }
3916             }
3917             
3918         }, this);
3919         
3920         var mark = {
3921             tag: "div",
3922             cls:"x-dlg-mask"
3923         };
3924         
3925         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3926         
3927         var size = this.el.getSize();
3928         this.maskEl.setSize(size.width, size.height);
3929         this.maskEl.enableDisplayMode("block");
3930         this.maskEl.hide();
3931         
3932         if(this.loadMask){
3933             this.maskEl.show();
3934         }
3935     },
3936     
3937     
3938     getChildContainer : function()
3939     {
3940         if (this.el.select('.collapse').getCount()) {
3941             return this.el.select('.collapse',true).first();
3942         }
3943         
3944         return this.el;
3945     },
3946     
3947     mask : function()
3948     {
3949         this.maskEl.show();
3950     },
3951     
3952     unmask : function()
3953     {
3954         this.maskEl.hide();
3955     } 
3956     
3957     
3958     
3959     
3960 });
3961
3962
3963
3964  
3965
3966  /*
3967  * - LGPL
3968  *
3969  * navbar
3970  * 
3971  */
3972
3973 /**
3974  * @class Roo.bootstrap.NavSimplebar
3975  * @extends Roo.bootstrap.Navbar
3976  * Bootstrap Sidebar class
3977  *
3978  * @cfg {Boolean} inverse is inverted color
3979  * 
3980  * @cfg {String} type (nav | pills | tabs)
3981  * @cfg {Boolean} arrangement stacked | justified
3982  * @cfg {String} align (left | right) alignment
3983  * 
3984  * @cfg {Boolean} main (true|false) main nav bar? default false
3985  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3986  * 
3987  * @cfg {String} tag (header|footer|nav|div) default is nav 
3988
3989  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3990  * 
3991  * 
3992  * @constructor
3993  * Create a new Sidebar
3994  * @param {Object} config The config object
3995  */
3996
3997
3998 Roo.bootstrap.NavSimplebar = function(config){
3999     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4000 };
4001
4002 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
4003     
4004     inverse: false,
4005     
4006     type: false,
4007     arrangement: '',
4008     align : false,
4009     
4010     weight : 'light',
4011     
4012     main : false,
4013     
4014     
4015     tag : false,
4016     
4017     
4018     getAutoCreate : function(){
4019         
4020         
4021         var cfg = {
4022             tag : this.tag || 'div',
4023             cls : 'navbar navbar-expand-lg'
4024         };
4025         if (['light','white'].indexOf(this.weight) > -1) {
4026             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4027         }
4028         cfg.cls += ' bg-' + this.weight;
4029         
4030         if (this.inverse) {
4031             cfg.cls += ' navbar-inverse';
4032             
4033         }
4034         
4035         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4036         
4037         if (Roo.bootstrap.version == 4) {
4038             return cfg;
4039         }
4040         
4041         cfg.cn = [
4042             {
4043                 cls: 'nav',
4044                 tag : 'ul'
4045             }
4046         ];
4047         
4048          
4049         this.type = this.type || 'nav';
4050         if (['tabs','pills'].indexOf(this.type)!==-1) {
4051             cfg.cn[0].cls += ' nav-' + this.type
4052         
4053         
4054         } else {
4055             if (this.type!=='nav') {
4056                 Roo.log('nav type must be nav/tabs/pills')
4057             }
4058             cfg.cn[0].cls += ' navbar-nav'
4059         }
4060         
4061         
4062         
4063         
4064         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
4065             cfg.cn[0].cls += ' nav-' + this.arrangement;
4066         }
4067         
4068         
4069         if (this.align === 'right') {
4070             cfg.cn[0].cls += ' navbar-right';
4071         }
4072         
4073         
4074         
4075         
4076         return cfg;
4077     
4078         
4079     }
4080     
4081     
4082     
4083 });
4084
4085
4086
4087  
4088
4089  
4090        /*
4091  * - LGPL
4092  *
4093  * navbar
4094  * navbar-fixed-top
4095  * navbar-expand-md  fixed-top 
4096  */
4097
4098 /**
4099  * @class Roo.bootstrap.NavHeaderbar
4100  * @extends Roo.bootstrap.NavSimplebar
4101  * Bootstrap Sidebar class
4102  *
4103  * @cfg {String} brand what is brand
4104  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4105  * @cfg {String} brand_href href of the brand
4106  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4107  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4108  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4109  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4110  * 
4111  * @constructor
4112  * Create a new Sidebar
4113  * @param {Object} config The config object
4114  */
4115
4116
4117 Roo.bootstrap.NavHeaderbar = function(config){
4118     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4119       
4120 };
4121
4122 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4123     
4124     position: '',
4125     brand: '',
4126     brand_href: false,
4127     srButton : true,
4128     autohide : false,
4129     desktopCenter : false,
4130    
4131     
4132     getAutoCreate : function(){
4133         
4134         var   cfg = {
4135             tag: this.nav || 'nav',
4136             cls: 'navbar navbar-expand-md',
4137             role: 'navigation',
4138             cn: []
4139         };
4140         
4141         var cn = cfg.cn;
4142         if (this.desktopCenter) {
4143             cn.push({cls : 'container', cn : []});
4144             cn = cn[0].cn;
4145         }
4146         
4147         if(this.srButton){
4148             var btn = {
4149                 tag: 'button',
4150                 type: 'button',
4151                 cls: 'navbar-toggle navbar-toggler',
4152                 'data-toggle': 'collapse',
4153                 cn: [
4154                     {
4155                         tag: 'span',
4156                         cls: 'sr-only',
4157                         html: 'Toggle navigation'
4158                     },
4159                     {
4160                         tag: 'span',
4161                         cls: 'icon-bar navbar-toggler-icon'
4162                     },
4163                     {
4164                         tag: 'span',
4165                         cls: 'icon-bar'
4166                     },
4167                     {
4168                         tag: 'span',
4169                         cls: 'icon-bar'
4170                     }
4171                 ]
4172             };
4173             
4174             cn.push( Roo.bootstrap.version == 4 ? btn : {
4175                 tag: 'div',
4176                 cls: 'navbar-header',
4177                 cn: [
4178                     btn
4179                 ]
4180             });
4181         }
4182         
4183         cn.push({
4184             tag: 'div',
4185             cls: 'collapse navbar-collapse',
4186             cn : []
4187         });
4188         
4189         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4190         
4191         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4192             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4193             
4194             // tag can override this..
4195             
4196             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4197         }
4198         
4199         if (this.brand !== '') {
4200             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4201             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4202                 tag: 'a',
4203                 href: this.brand_href ? this.brand_href : '#',
4204                 cls: 'navbar-brand',
4205                 cn: [
4206                 this.brand
4207                 ]
4208             });
4209         }
4210         
4211         if(this.main){
4212             cfg.cls += ' main-nav';
4213         }
4214         
4215         
4216         return cfg;
4217
4218         
4219     },
4220     getHeaderChildContainer : function()
4221     {
4222         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4223             return this.el.select('.navbar-header',true).first();
4224         }
4225         
4226         return this.getChildContainer();
4227     },
4228     
4229     
4230     initEvents : function()
4231     {
4232         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4233         
4234         if (this.autohide) {
4235             
4236             var prevScroll = 0;
4237             var ft = this.el;
4238             
4239             Roo.get(document).on('scroll',function(e) {
4240                 var ns = Roo.get(document).getScroll().top;
4241                 var os = prevScroll;
4242                 prevScroll = ns;
4243                 
4244                 if(ns > os){
4245                     ft.removeClass('slideDown');
4246                     ft.addClass('slideUp');
4247                     return;
4248                 }
4249                 ft.removeClass('slideUp');
4250                 ft.addClass('slideDown');
4251                  
4252               
4253           },this);
4254         }
4255     }    
4256     
4257 });
4258
4259
4260
4261  
4262
4263  /*
4264  * - LGPL
4265  *
4266  * navbar
4267  * 
4268  */
4269
4270 /**
4271  * @class Roo.bootstrap.NavSidebar
4272  * @extends Roo.bootstrap.Navbar
4273  * Bootstrap Sidebar class
4274  * 
4275  * @constructor
4276  * Create a new Sidebar
4277  * @param {Object} config The config object
4278  */
4279
4280
4281 Roo.bootstrap.NavSidebar = function(config){
4282     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4283 };
4284
4285 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4286     
4287     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4288     
4289     getAutoCreate : function(){
4290         
4291         
4292         return  {
4293             tag: 'div',
4294             cls: 'sidebar sidebar-nav'
4295         };
4296     
4297         
4298     }
4299     
4300     
4301     
4302 });
4303
4304
4305
4306  
4307
4308  /*
4309  * - LGPL
4310  *
4311  * nav group
4312  * 
4313  */
4314
4315 /**
4316  * @class Roo.bootstrap.NavGroup
4317  * @extends Roo.bootstrap.Component
4318  * Bootstrap NavGroup class
4319  * @cfg {String} align (left|right)
4320  * @cfg {Boolean} inverse
4321  * @cfg {String} type (nav|pills|tab) default nav
4322  * @cfg {String} navId - reference Id for navbar.
4323
4324  * 
4325  * @constructor
4326  * Create a new nav group
4327  * @param {Object} config The config object
4328  */
4329
4330 Roo.bootstrap.NavGroup = function(config){
4331     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4332     this.navItems = [];
4333    
4334     Roo.bootstrap.NavGroup.register(this);
4335      this.addEvents({
4336         /**
4337              * @event changed
4338              * Fires when the active item changes
4339              * @param {Roo.bootstrap.NavGroup} this
4340              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4341              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4342          */
4343         'changed': true
4344      });
4345     
4346 };
4347
4348 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4349     
4350     align: '',
4351     inverse: false,
4352     form: false,
4353     type: 'nav',
4354     navId : '',
4355     // private
4356     
4357     navItems : false, 
4358     
4359     getAutoCreate : function()
4360     {
4361         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4362         
4363         cfg = {
4364             tag : 'ul',
4365             cls: 'nav' 
4366         };
4367         if (Roo.bootstrap.version == 4) {
4368             if (this.type == 'pills') {
4369                 cfg.cls = ' nav-pills';
4370             }
4371         } else {
4372             if (['tabs','pills'].indexOf(this.type)!==-1) {
4373                 cfg.cls += ' nav-' + this.type
4374             } else {
4375                 if (this.type !== 'nav') {
4376                     Roo.log('nav type must be nav/tabs/pills')
4377                 }
4378                 cfg.cls += ' navbar-nav'
4379             }
4380         }
4381         
4382         if (this.parent() && this.parent().sidebar) {
4383             cfg = {
4384                 tag: 'ul',
4385                 cls: 'dashboard-menu sidebar-menu'
4386             };
4387             
4388             return cfg;
4389         }
4390         
4391         if (this.form === true) {
4392             cfg = {
4393                 tag: 'form',
4394                 cls: 'navbar-form form-inline'
4395             };
4396             
4397             if (this.align === 'right') {
4398                 cfg.cls += ' navbar-right ml-md-auto';
4399             } else {
4400                 cfg.cls += ' navbar-left';
4401             }
4402         }
4403         
4404         if (this.align === 'right') {
4405             cfg.cls += ' navbar-right ml-md-auto';
4406         } else {
4407             cfg.cls += ' mr-auto';
4408         }
4409         
4410         if (this.inverse) {
4411             cfg.cls += ' navbar-inverse';
4412             
4413         }
4414         
4415         
4416         return cfg;
4417     },
4418     /**
4419     * sets the active Navigation item
4420     * @param {Roo.bootstrap.NavItem} the new current navitem
4421     */
4422     setActiveItem : function(item)
4423     {
4424         var prev = false;
4425         Roo.each(this.navItems, function(v){
4426             if (v == item) {
4427                 return ;
4428             }
4429             if (v.isActive()) {
4430                 v.setActive(false, true);
4431                 prev = v;
4432                 
4433             }
4434             
4435         });
4436
4437         item.setActive(true, true);
4438         this.fireEvent('changed', this, item, prev);
4439         
4440         
4441     },
4442     /**
4443     * gets the active Navigation item
4444     * @return {Roo.bootstrap.NavItem} the current navitem
4445     */
4446     getActive : function()
4447     {
4448         
4449         var prev = false;
4450         Roo.each(this.navItems, function(v){
4451             
4452             if (v.isActive()) {
4453                 prev = v;
4454                 
4455             }
4456             
4457         });
4458         return prev;
4459     },
4460     
4461     indexOfNav : function()
4462     {
4463         
4464         var prev = false;
4465         Roo.each(this.navItems, function(v,i){
4466             
4467             if (v.isActive()) {
4468                 prev = i;
4469                 
4470             }
4471             
4472         });
4473         return prev;
4474     },
4475     /**
4476     * adds a Navigation item
4477     * @param {Roo.bootstrap.NavItem} the navitem to add
4478     */
4479     addItem : function(cfg)
4480     {
4481         if (this.form && Roo.bootstrap.version == 4) {
4482             cfg.tag = 'div';
4483         }
4484         var cn = new Roo.bootstrap.NavItem(cfg);
4485         this.register(cn);
4486         cn.parentId = this.id;
4487         cn.onRender(this.el, null);
4488         return cn;
4489     },
4490     /**
4491     * register a Navigation item
4492     * @param {Roo.bootstrap.NavItem} the navitem to add
4493     */
4494     register : function(item)
4495     {
4496         this.navItems.push( item);
4497         item.navId = this.navId;
4498     
4499     },
4500     
4501     /**
4502     * clear all the Navigation item
4503     */
4504    
4505     clearAll : function()
4506     {
4507         this.navItems = [];
4508         this.el.dom.innerHTML = '';
4509     },
4510     
4511     getNavItem: function(tabId)
4512     {
4513         var ret = false;
4514         Roo.each(this.navItems, function(e) {
4515             if (e.tabId == tabId) {
4516                ret =  e;
4517                return false;
4518             }
4519             return true;
4520             
4521         });
4522         return ret;
4523     },
4524     
4525     setActiveNext : function()
4526     {
4527         var i = this.indexOfNav(this.getActive());
4528         if (i > this.navItems.length) {
4529             return;
4530         }
4531         this.setActiveItem(this.navItems[i+1]);
4532     },
4533     setActivePrev : function()
4534     {
4535         var i = this.indexOfNav(this.getActive());
4536         if (i  < 1) {
4537             return;
4538         }
4539         this.setActiveItem(this.navItems[i-1]);
4540     },
4541     clearWasActive : function(except) {
4542         Roo.each(this.navItems, function(e) {
4543             if (e.tabId != except.tabId && e.was_active) {
4544                e.was_active = false;
4545                return false;
4546             }
4547             return true;
4548             
4549         });
4550     },
4551     getWasActive : function ()
4552     {
4553         var r = false;
4554         Roo.each(this.navItems, function(e) {
4555             if (e.was_active) {
4556                r = e;
4557                return false;
4558             }
4559             return true;
4560             
4561         });
4562         return r;
4563     }
4564     
4565     
4566 });
4567
4568  
4569 Roo.apply(Roo.bootstrap.NavGroup, {
4570     
4571     groups: {},
4572      /**
4573     * register a Navigation Group
4574     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4575     */
4576     register : function(navgrp)
4577     {
4578         this.groups[navgrp.navId] = navgrp;
4579         
4580     },
4581     /**
4582     * fetch a Navigation Group based on the navigation ID
4583     * @param {string} the navgroup to add
4584     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4585     */
4586     get: function(navId) {
4587         if (typeof(this.groups[navId]) == 'undefined') {
4588             return false;
4589             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4590         }
4591         return this.groups[navId] ;
4592     }
4593     
4594     
4595     
4596 });
4597
4598  /*
4599  * - LGPL
4600  *
4601  * row
4602  * 
4603  */
4604
4605 /**
4606  * @class Roo.bootstrap.NavItem
4607  * @extends Roo.bootstrap.Component
4608  * Bootstrap Navbar.NavItem class
4609  * @cfg {String} href  link to
4610  * @cfg {String} html content of button
4611  * @cfg {String} badge text inside badge
4612  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4613  * @cfg {String} glyphicon DEPRICATED - use fa
4614  * @cfg {String} icon DEPRICATED - use fa
4615  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4616  * @cfg {Boolean} active Is item active
4617  * @cfg {Boolean} disabled Is item disabled
4618  
4619  * @cfg {Boolean} preventDefault (true | false) default false
4620  * @cfg {String} tabId the tab that this item activates.
4621  * @cfg {String} tagtype (a|span) render as a href or span?
4622  * @cfg {Boolean} animateRef (true|false) link to element default false  
4623   
4624  * @constructor
4625  * Create a new Navbar Item
4626  * @param {Object} config The config object
4627  */
4628 Roo.bootstrap.NavItem = function(config){
4629     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4630     this.addEvents({
4631         // raw events
4632         /**
4633          * @event click
4634          * The raw click event for the entire grid.
4635          * @param {Roo.EventObject} e
4636          */
4637         "click" : true,
4638          /**
4639             * @event changed
4640             * Fires when the active item active state changes
4641             * @param {Roo.bootstrap.NavItem} this
4642             * @param {boolean} state the new state
4643              
4644          */
4645         'changed': true,
4646         /**
4647             * @event scrollto
4648             * Fires when scroll to element
4649             * @param {Roo.bootstrap.NavItem} this
4650             * @param {Object} options
4651             * @param {Roo.EventObject} e
4652              
4653          */
4654         'scrollto': true
4655     });
4656    
4657 };
4658
4659 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4660     
4661     href: false,
4662     html: '',
4663     badge: '',
4664     icon: false,
4665     fa : false,
4666     glyphicon: false,
4667     active: false,
4668     preventDefault : false,
4669     tabId : false,
4670     tagtype : 'a',
4671     tag: 'li',
4672     disabled : false,
4673     animateRef : false,
4674     was_active : false,
4675     
4676     getAutoCreate : function(){
4677          
4678         var cfg = {
4679             tag: this.tag,
4680             cls: 'nav-item'
4681             
4682         };
4683         
4684         if (this.active) {
4685             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4686         }
4687         if (this.disabled) {
4688             cfg.cls += ' disabled';
4689         }
4690         
4691         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4692             cfg.cn = [
4693                 {
4694                     tag: this.tagtype,
4695                     href : this.href || "#",
4696                     html: this.html || ''
4697                 }
4698             ];
4699             if (this.tagtype == 'a') {
4700                 cfg.cn[0].cls = 'nav-link';
4701             }
4702             if (this.icon) {
4703                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4704             }
4705             if (this.fa) {
4706                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4707             }
4708             if(this.glyphicon) {
4709                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4710             }
4711             
4712             if (this.menu) {
4713                 
4714                 cfg.cn[0].html += " <span class='caret'></span>";
4715              
4716             }
4717             
4718             if (this.badge !== '') {
4719                  
4720                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4721             }
4722         }
4723         
4724         
4725         
4726         return cfg;
4727     },
4728     onRender : function(ct, position)
4729     {
4730        // Roo.log("Call onRender: " + this.xtype);
4731         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
4732             this.tag = 'div';
4733         }
4734         
4735         return Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
4736     },
4737       
4738     
4739     initEvents: function() 
4740     {
4741         if (typeof (this.menu) != 'undefined') {
4742             this.menu.parentType = this.xtype;
4743             this.menu.triggerEl = this.el;
4744             this.menu = this.addxtype(Roo.apply({}, this.menu));
4745         }
4746         
4747         this.el.select('a',true).on('click', this.onClick, this);
4748         
4749         if(this.tagtype == 'span'){
4750             this.el.select('span',true).on('click', this.onClick, this);
4751         }
4752        
4753         // at this point parent should be available..
4754         this.parent().register(this);
4755     },
4756     
4757     onClick : function(e)
4758     {
4759         if (e.getTarget('.dropdown-menu-item')) {
4760             // did you click on a menu itemm.... - then don't trigger onclick..
4761             return;
4762         }
4763         
4764         if(
4765                 this.preventDefault || 
4766                 this.href == '#' 
4767         ){
4768             Roo.log("NavItem - prevent Default?");
4769             e.preventDefault();
4770         }
4771         
4772         if (this.disabled) {
4773             return;
4774         }
4775         
4776         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4777         if (tg && tg.transition) {
4778             Roo.log("waiting for the transitionend");
4779             return;
4780         }
4781         
4782         
4783         
4784         //Roo.log("fire event clicked");
4785         if(this.fireEvent('click', this, e) === false){
4786             return;
4787         };
4788         
4789         if(this.tagtype == 'span'){
4790             return;
4791         }
4792         
4793         //Roo.log(this.href);
4794         var ael = this.el.select('a',true).first();
4795         //Roo.log(ael);
4796         
4797         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4798             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4799             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4800                 return; // ignore... - it's a 'hash' to another page.
4801             }
4802             Roo.log("NavItem - prevent Default?");
4803             e.preventDefault();
4804             this.scrollToElement(e);
4805         }
4806         
4807         
4808         var p =  this.parent();
4809    
4810         if (['tabs','pills'].indexOf(p.type)!==-1) {
4811             if (typeof(p.setActiveItem) !== 'undefined') {
4812                 p.setActiveItem(this);
4813             }
4814         }
4815         
4816         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4817         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4818             // remove the collapsed menu expand...
4819             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4820         }
4821     },
4822     
4823     isActive: function () {
4824         return this.active
4825     },
4826     setActive : function(state, fire, is_was_active)
4827     {
4828         if (this.active && !state && this.navId) {
4829             this.was_active = true;
4830             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4831             if (nv) {
4832                 nv.clearWasActive(this);
4833             }
4834             
4835         }
4836         this.active = state;
4837         
4838         if (!state ) {
4839             this.el.removeClass('active');
4840         } else if (!this.el.hasClass('active')) {
4841             this.el.addClass('active');
4842         }
4843         if (fire) {
4844             this.fireEvent('changed', this, state);
4845         }
4846         
4847         // show a panel if it's registered and related..
4848         
4849         if (!this.navId || !this.tabId || !state || is_was_active) {
4850             return;
4851         }
4852         
4853         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4854         if (!tg) {
4855             return;
4856         }
4857         var pan = tg.getPanelByName(this.tabId);
4858         if (!pan) {
4859             return;
4860         }
4861         // if we can not flip to new panel - go back to old nav highlight..
4862         if (false == tg.showPanel(pan)) {
4863             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4864             if (nv) {
4865                 var onav = nv.getWasActive();
4866                 if (onav) {
4867                     onav.setActive(true, false, true);
4868                 }
4869             }
4870             
4871         }
4872         
4873         
4874         
4875     },
4876      // this should not be here...
4877     setDisabled : function(state)
4878     {
4879         this.disabled = state;
4880         if (!state ) {
4881             this.el.removeClass('disabled');
4882         } else if (!this.el.hasClass('disabled')) {
4883             this.el.addClass('disabled');
4884         }
4885         
4886     },
4887     
4888     /**
4889      * Fetch the element to display the tooltip on.
4890      * @return {Roo.Element} defaults to this.el
4891      */
4892     tooltipEl : function()
4893     {
4894         return this.el.select('' + this.tagtype + '', true).first();
4895     },
4896     
4897     scrollToElement : function(e)
4898     {
4899         var c = document.body;
4900         
4901         /*
4902          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4903          */
4904         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4905             c = document.documentElement;
4906         }
4907         
4908         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4909         
4910         if(!target){
4911             return;
4912         }
4913
4914         var o = target.calcOffsetsTo(c);
4915         
4916         var options = {
4917             target : target,
4918             value : o[1]
4919         };
4920         
4921         this.fireEvent('scrollto', this, options, e);
4922         
4923         Roo.get(c).scrollTo('top', options.value, true);
4924         
4925         return;
4926     }
4927 });
4928  
4929
4930  /*
4931  * - LGPL
4932  *
4933  * sidebar item
4934  *
4935  *  li
4936  *    <span> icon </span>
4937  *    <span> text </span>
4938  *    <span>badge </span>
4939  */
4940
4941 /**
4942  * @class Roo.bootstrap.NavSidebarItem
4943  * @extends Roo.bootstrap.NavItem
4944  * Bootstrap Navbar.NavSidebarItem class
4945  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4946  * {Boolean} open is the menu open
4947  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4948  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4949  * {String} buttonSize (sm|md|lg)the extra classes for the button
4950  * {Boolean} showArrow show arrow next to the text (default true)
4951  * @constructor
4952  * Create a new Navbar Button
4953  * @param {Object} config The config object
4954  */
4955 Roo.bootstrap.NavSidebarItem = function(config){
4956     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4957     this.addEvents({
4958         // raw events
4959         /**
4960          * @event click
4961          * The raw click event for the entire grid.
4962          * @param {Roo.EventObject} e
4963          */
4964         "click" : true,
4965          /**
4966             * @event changed
4967             * Fires when the active item active state changes
4968             * @param {Roo.bootstrap.NavSidebarItem} this
4969             * @param {boolean} state the new state
4970              
4971          */
4972         'changed': true
4973     });
4974    
4975 };
4976
4977 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4978     
4979     badgeWeight : 'default',
4980     
4981     open: false,
4982     
4983     buttonView : false,
4984     
4985     buttonWeight : 'default',
4986     
4987     buttonSize : 'md',
4988     
4989     showArrow : true,
4990     
4991     getAutoCreate : function(){
4992         
4993         
4994         var a = {
4995                 tag: 'a',
4996                 href : this.href || '#',
4997                 cls: '',
4998                 html : '',
4999                 cn : []
5000         };
5001         
5002         if(this.buttonView){
5003             a = {
5004                 tag: 'button',
5005                 href : this.href || '#',
5006                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5007                 html : this.html,
5008                 cn : []
5009             };
5010         }
5011         
5012         var cfg = {
5013             tag: 'li',
5014             cls: '',
5015             cn: [ a ]
5016         };
5017         
5018         if (this.active) {
5019             cfg.cls += ' active';
5020         }
5021         
5022         if (this.disabled) {
5023             cfg.cls += ' disabled';
5024         }
5025         if (this.open) {
5026             cfg.cls += ' open x-open';
5027         }
5028         // left icon..
5029         if (this.glyphicon || this.icon) {
5030             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5031             a.cn.push({ tag : 'i', cls : c }) ;
5032         }
5033         
5034         if(!this.buttonView){
5035             var span = {
5036                 tag: 'span',
5037                 html : this.html || ''
5038             };
5039
5040             a.cn.push(span);
5041             
5042         }
5043         
5044         if (this.badge !== '') {
5045             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5046         }
5047         
5048         if (this.menu) {
5049             
5050             if(this.showArrow){
5051                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5052             }
5053             
5054             a.cls += ' dropdown-toggle treeview' ;
5055         }
5056         
5057         return cfg;
5058     },
5059     
5060     initEvents : function()
5061     { 
5062         if (typeof (this.menu) != 'undefined') {
5063             this.menu.parentType = this.xtype;
5064             this.menu.triggerEl = this.el;
5065             this.menu = this.addxtype(Roo.apply({}, this.menu));
5066         }
5067         
5068         this.el.on('click', this.onClick, this);
5069         
5070         if(this.badge !== ''){
5071             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5072         }
5073         
5074     },
5075     
5076     onClick : function(e)
5077     {
5078         if(this.disabled){
5079             e.preventDefault();
5080             return;
5081         }
5082         
5083         if(this.preventDefault){
5084             e.preventDefault();
5085         }
5086         
5087         this.fireEvent('click', this);
5088     },
5089     
5090     disable : function()
5091     {
5092         this.setDisabled(true);
5093     },
5094     
5095     enable : function()
5096     {
5097         this.setDisabled(false);
5098     },
5099     
5100     setDisabled : function(state)
5101     {
5102         if(this.disabled == state){
5103             return;
5104         }
5105         
5106         this.disabled = state;
5107         
5108         if (state) {
5109             this.el.addClass('disabled');
5110             return;
5111         }
5112         
5113         this.el.removeClass('disabled');
5114         
5115         return;
5116     },
5117     
5118     setActive : function(state)
5119     {
5120         if(this.active == state){
5121             return;
5122         }
5123         
5124         this.active = state;
5125         
5126         if (state) {
5127             this.el.addClass('active');
5128             return;
5129         }
5130         
5131         this.el.removeClass('active');
5132         
5133         return;
5134     },
5135     
5136     isActive: function () 
5137     {
5138         return this.active;
5139     },
5140     
5141     setBadge : function(str)
5142     {
5143         if(!this.badgeEl){
5144             return;
5145         }
5146         
5147         this.badgeEl.dom.innerHTML = str;
5148     }
5149     
5150    
5151      
5152  
5153 });
5154  
5155
5156  /*
5157  * - LGPL
5158  *
5159  * row
5160  * 
5161  */
5162
5163 /**
5164  * @class Roo.bootstrap.Row
5165  * @extends Roo.bootstrap.Component
5166  * Bootstrap Row class (contains columns...)
5167  * 
5168  * @constructor
5169  * Create a new Row
5170  * @param {Object} config The config object
5171  */
5172
5173 Roo.bootstrap.Row = function(config){
5174     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5175 };
5176
5177 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5178     
5179     getAutoCreate : function(){
5180        return {
5181             cls: 'row clearfix'
5182        };
5183     }
5184     
5185     
5186 });
5187
5188  
5189
5190  /*
5191  * - LGPL
5192  *
5193  * element
5194  * 
5195  */
5196
5197 /**
5198  * @class Roo.bootstrap.Element
5199  * @extends Roo.bootstrap.Component
5200  * Bootstrap Element class
5201  * @cfg {String} html contents of the element
5202  * @cfg {String} tag tag of the element
5203  * @cfg {String} cls class of the element
5204  * @cfg {Boolean} preventDefault (true|false) default false
5205  * @cfg {Boolean} clickable (true|false) default false
5206  * 
5207  * @constructor
5208  * Create a new Element
5209  * @param {Object} config The config object
5210  */
5211
5212 Roo.bootstrap.Element = function(config){
5213     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5214     
5215     this.addEvents({
5216         // raw events
5217         /**
5218          * @event click
5219          * When a element is chick
5220          * @param {Roo.bootstrap.Element} this
5221          * @param {Roo.EventObject} e
5222          */
5223         "click" : true
5224     });
5225 };
5226
5227 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5228     
5229     tag: 'div',
5230     cls: '',
5231     html: '',
5232     preventDefault: false, 
5233     clickable: false,
5234     
5235     getAutoCreate : function(){
5236         
5237         var cfg = {
5238             tag: this.tag,
5239             // cls: this.cls, double assign in parent class Component.js :: onRender
5240             html: this.html
5241         };
5242         
5243         return cfg;
5244     },
5245     
5246     initEvents: function() 
5247     {
5248         Roo.bootstrap.Element.superclass.initEvents.call(this);
5249         
5250         if(this.clickable){
5251             this.el.on('click', this.onClick, this);
5252         }
5253         
5254     },
5255     
5256     onClick : function(e)
5257     {
5258         if(this.preventDefault){
5259             e.preventDefault();
5260         }
5261         
5262         this.fireEvent('click', this, e);
5263     },
5264     
5265     getValue : function()
5266     {
5267         return this.el.dom.innerHTML;
5268     },
5269     
5270     setValue : function(value)
5271     {
5272         this.el.dom.innerHTML = value;
5273     }
5274    
5275 });
5276
5277  
5278
5279  /*
5280  * - LGPL
5281  *
5282  * pagination
5283  * 
5284  */
5285
5286 /**
5287  * @class Roo.bootstrap.Pagination
5288  * @extends Roo.bootstrap.Component
5289  * Bootstrap Pagination class
5290  * @cfg {String} size xs | sm | md | lg
5291  * @cfg {Boolean} inverse false | true
5292  * 
5293  * @constructor
5294  * Create a new Pagination
5295  * @param {Object} config The config object
5296  */
5297
5298 Roo.bootstrap.Pagination = function(config){
5299     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5300 };
5301
5302 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5303     
5304     cls: false,
5305     size: false,
5306     inverse: false,
5307     
5308     getAutoCreate : function(){
5309         var cfg = {
5310             tag: 'ul',
5311                 cls: 'pagination'
5312         };
5313         if (this.inverse) {
5314             cfg.cls += ' inverse';
5315         }
5316         if (this.html) {
5317             cfg.html=this.html;
5318         }
5319         if (this.cls) {
5320             cfg.cls += " " + this.cls;
5321         }
5322         return cfg;
5323     }
5324    
5325 });
5326
5327  
5328
5329  /*
5330  * - LGPL
5331  *
5332  * Pagination item
5333  * 
5334  */
5335
5336
5337 /**
5338  * @class Roo.bootstrap.PaginationItem
5339  * @extends Roo.bootstrap.Component
5340  * Bootstrap PaginationItem class
5341  * @cfg {String} html text
5342  * @cfg {String} href the link
5343  * @cfg {Boolean} preventDefault (true | false) default true
5344  * @cfg {Boolean} active (true | false) default false
5345  * @cfg {Boolean} disabled default false
5346  * 
5347  * 
5348  * @constructor
5349  * Create a new PaginationItem
5350  * @param {Object} config The config object
5351  */
5352
5353
5354 Roo.bootstrap.PaginationItem = function(config){
5355     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5356     this.addEvents({
5357         // raw events
5358         /**
5359          * @event click
5360          * The raw click event for the entire grid.
5361          * @param {Roo.EventObject} e
5362          */
5363         "click" : true
5364     });
5365 };
5366
5367 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5368     
5369     href : false,
5370     html : false,
5371     preventDefault: true,
5372     active : false,
5373     cls : false,
5374     disabled: false,
5375     
5376     getAutoCreate : function(){
5377         var cfg= {
5378             tag: 'li',
5379             cn: [
5380                 {
5381                     tag : 'a',
5382                     href : this.href ? this.href : '#',
5383                     html : this.html ? this.html : ''
5384                 }
5385             ]
5386         };
5387         
5388         if(this.cls){
5389             cfg.cls = this.cls;
5390         }
5391         
5392         if(this.disabled){
5393             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5394         }
5395         
5396         if(this.active){
5397             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5398         }
5399         
5400         return cfg;
5401     },
5402     
5403     initEvents: function() {
5404         
5405         this.el.on('click', this.onClick, this);
5406         
5407     },
5408     onClick : function(e)
5409     {
5410         Roo.log('PaginationItem on click ');
5411         if(this.preventDefault){
5412             e.preventDefault();
5413         }
5414         
5415         if(this.disabled){
5416             return;
5417         }
5418         
5419         this.fireEvent('click', this, e);
5420     }
5421    
5422 });
5423
5424  
5425
5426  /*
5427  * - LGPL
5428  *
5429  * slider
5430  * 
5431  */
5432
5433
5434 /**
5435  * @class Roo.bootstrap.Slider
5436  * @extends Roo.bootstrap.Component
5437  * Bootstrap Slider class
5438  *    
5439  * @constructor
5440  * Create a new Slider
5441  * @param {Object} config The config object
5442  */
5443
5444 Roo.bootstrap.Slider = function(config){
5445     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5446 };
5447
5448 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5449     
5450     getAutoCreate : function(){
5451         
5452         var cfg = {
5453             tag: 'div',
5454             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5455             cn: [
5456                 {
5457                     tag: 'a',
5458                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5459                 }
5460             ]
5461         };
5462         
5463         return cfg;
5464     }
5465    
5466 });
5467
5468  /*
5469  * Based on:
5470  * Ext JS Library 1.1.1
5471  * Copyright(c) 2006-2007, Ext JS, LLC.
5472  *
5473  * Originally Released Under LGPL - original licence link has changed is not relivant.
5474  *
5475  * Fork - LGPL
5476  * <script type="text/javascript">
5477  */
5478  
5479
5480 /**
5481  * @class Roo.grid.ColumnModel
5482  * @extends Roo.util.Observable
5483  * This is the default implementation of a ColumnModel used by the Grid. It defines
5484  * the columns in the grid.
5485  * <br>Usage:<br>
5486  <pre><code>
5487  var colModel = new Roo.grid.ColumnModel([
5488         {header: "Ticker", width: 60, sortable: true, locked: true},
5489         {header: "Company Name", width: 150, sortable: true},
5490         {header: "Market Cap.", width: 100, sortable: true},
5491         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5492         {header: "Employees", width: 100, sortable: true, resizable: false}
5493  ]);
5494  </code></pre>
5495  * <p>
5496  
5497  * The config options listed for this class are options which may appear in each
5498  * individual column definition.
5499  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5500  * @constructor
5501  * @param {Object} config An Array of column config objects. See this class's
5502  * config objects for details.
5503 */
5504 Roo.grid.ColumnModel = function(config){
5505         /**
5506      * The config passed into the constructor
5507      */
5508     this.config = config;
5509     this.lookup = {};
5510
5511     // if no id, create one
5512     // if the column does not have a dataIndex mapping,
5513     // map it to the order it is in the config
5514     for(var i = 0, len = config.length; i < len; i++){
5515         var c = config[i];
5516         if(typeof c.dataIndex == "undefined"){
5517             c.dataIndex = i;
5518         }
5519         if(typeof c.renderer == "string"){
5520             c.renderer = Roo.util.Format[c.renderer];
5521         }
5522         if(typeof c.id == "undefined"){
5523             c.id = Roo.id();
5524         }
5525         if(c.editor && c.editor.xtype){
5526             c.editor  = Roo.factory(c.editor, Roo.grid);
5527         }
5528         if(c.editor && c.editor.isFormField){
5529             c.editor = new Roo.grid.GridEditor(c.editor);
5530         }
5531         this.lookup[c.id] = c;
5532     }
5533
5534     /**
5535      * The width of columns which have no width specified (defaults to 100)
5536      * @type Number
5537      */
5538     this.defaultWidth = 100;
5539
5540     /**
5541      * Default sortable of columns which have no sortable specified (defaults to false)
5542      * @type Boolean
5543      */
5544     this.defaultSortable = false;
5545
5546     this.addEvents({
5547         /**
5548              * @event widthchange
5549              * Fires when the width of a column changes.
5550              * @param {ColumnModel} this
5551              * @param {Number} columnIndex The column index
5552              * @param {Number} newWidth The new width
5553              */
5554             "widthchange": true,
5555         /**
5556              * @event headerchange
5557              * Fires when the text of a header changes.
5558              * @param {ColumnModel} this
5559              * @param {Number} columnIndex The column index
5560              * @param {Number} newText The new header text
5561              */
5562             "headerchange": true,
5563         /**
5564              * @event hiddenchange
5565              * Fires when a column is hidden or "unhidden".
5566              * @param {ColumnModel} this
5567              * @param {Number} columnIndex The column index
5568              * @param {Boolean} hidden true if hidden, false otherwise
5569              */
5570             "hiddenchange": true,
5571             /**
5572          * @event columnmoved
5573          * Fires when a column is moved.
5574          * @param {ColumnModel} this
5575          * @param {Number} oldIndex
5576          * @param {Number} newIndex
5577          */
5578         "columnmoved" : true,
5579         /**
5580          * @event columlockchange
5581          * Fires when a column's locked state is changed
5582          * @param {ColumnModel} this
5583          * @param {Number} colIndex
5584          * @param {Boolean} locked true if locked
5585          */
5586         "columnlockchange" : true
5587     });
5588     Roo.grid.ColumnModel.superclass.constructor.call(this);
5589 };
5590 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5591     /**
5592      * @cfg {String} header The header text to display in the Grid view.
5593      */
5594     /**
5595      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5596      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5597      * specified, the column's index is used as an index into the Record's data Array.
5598      */
5599     /**
5600      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5601      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5602      */
5603     /**
5604      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5605      * Defaults to the value of the {@link #defaultSortable} property.
5606      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5607      */
5608     /**
5609      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5610      */
5611     /**
5612      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5613      */
5614     /**
5615      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5616      */
5617     /**
5618      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5619      */
5620     /**
5621      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5622      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5623      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5624      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5625      */
5626        /**
5627      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5628      */
5629     /**
5630      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5631      */
5632     /**
5633      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5634      */
5635     /**
5636      * @cfg {String} cursor (Optional)
5637      */
5638     /**
5639      * @cfg {String} tooltip (Optional)
5640      */
5641     /**
5642      * @cfg {Number} xs (Optional)
5643      */
5644     /**
5645      * @cfg {Number} sm (Optional)
5646      */
5647     /**
5648      * @cfg {Number} md (Optional)
5649      */
5650     /**
5651      * @cfg {Number} lg (Optional)
5652      */
5653     /**
5654      * Returns the id of the column at the specified index.
5655      * @param {Number} index The column index
5656      * @return {String} the id
5657      */
5658     getColumnId : function(index){
5659         return this.config[index].id;
5660     },
5661
5662     /**
5663      * Returns the column for a specified id.
5664      * @param {String} id The column id
5665      * @return {Object} the column
5666      */
5667     getColumnById : function(id){
5668         return this.lookup[id];
5669     },
5670
5671     
5672     /**
5673      * Returns the column for a specified dataIndex.
5674      * @param {String} dataIndex The column dataIndex
5675      * @return {Object|Boolean} the column or false if not found
5676      */
5677     getColumnByDataIndex: function(dataIndex){
5678         var index = this.findColumnIndex(dataIndex);
5679         return index > -1 ? this.config[index] : false;
5680     },
5681     
5682     /**
5683      * Returns the index for a specified column id.
5684      * @param {String} id The column id
5685      * @return {Number} the index, or -1 if not found
5686      */
5687     getIndexById : function(id){
5688         for(var i = 0, len = this.config.length; i < len; i++){
5689             if(this.config[i].id == id){
5690                 return i;
5691             }
5692         }
5693         return -1;
5694     },
5695     
5696     /**
5697      * Returns the index for a specified column dataIndex.
5698      * @param {String} dataIndex The column dataIndex
5699      * @return {Number} the index, or -1 if not found
5700      */
5701     
5702     findColumnIndex : function(dataIndex){
5703         for(var i = 0, len = this.config.length; i < len; i++){
5704             if(this.config[i].dataIndex == dataIndex){
5705                 return i;
5706             }
5707         }
5708         return -1;
5709     },
5710     
5711     
5712     moveColumn : function(oldIndex, newIndex){
5713         var c = this.config[oldIndex];
5714         this.config.splice(oldIndex, 1);
5715         this.config.splice(newIndex, 0, c);
5716         this.dataMap = null;
5717         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5718     },
5719
5720     isLocked : function(colIndex){
5721         return this.config[colIndex].locked === true;
5722     },
5723
5724     setLocked : function(colIndex, value, suppressEvent){
5725         if(this.isLocked(colIndex) == value){
5726             return;
5727         }
5728         this.config[colIndex].locked = value;
5729         if(!suppressEvent){
5730             this.fireEvent("columnlockchange", this, colIndex, value);
5731         }
5732     },
5733
5734     getTotalLockedWidth : function(){
5735         var totalWidth = 0;
5736         for(var i = 0; i < this.config.length; i++){
5737             if(this.isLocked(i) && !this.isHidden(i)){
5738                 this.totalWidth += this.getColumnWidth(i);
5739             }
5740         }
5741         return totalWidth;
5742     },
5743
5744     getLockedCount : function(){
5745         for(var i = 0, len = this.config.length; i < len; i++){
5746             if(!this.isLocked(i)){
5747                 return i;
5748             }
5749         }
5750         
5751         return this.config.length;
5752     },
5753
5754     /**
5755      * Returns the number of columns.
5756      * @return {Number}
5757      */
5758     getColumnCount : function(visibleOnly){
5759         if(visibleOnly === true){
5760             var c = 0;
5761             for(var i = 0, len = this.config.length; i < len; i++){
5762                 if(!this.isHidden(i)){
5763                     c++;
5764                 }
5765             }
5766             return c;
5767         }
5768         return this.config.length;
5769     },
5770
5771     /**
5772      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5773      * @param {Function} fn
5774      * @param {Object} scope (optional)
5775      * @return {Array} result
5776      */
5777     getColumnsBy : function(fn, scope){
5778         var r = [];
5779         for(var i = 0, len = this.config.length; i < len; i++){
5780             var c = this.config[i];
5781             if(fn.call(scope||this, c, i) === true){
5782                 r[r.length] = c;
5783             }
5784         }
5785         return r;
5786     },
5787
5788     /**
5789      * Returns true if the specified column is sortable.
5790      * @param {Number} col The column index
5791      * @return {Boolean}
5792      */
5793     isSortable : function(col){
5794         if(typeof this.config[col].sortable == "undefined"){
5795             return this.defaultSortable;
5796         }
5797         return this.config[col].sortable;
5798     },
5799
5800     /**
5801      * Returns the rendering (formatting) function defined for the column.
5802      * @param {Number} col The column index.
5803      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5804      */
5805     getRenderer : function(col){
5806         if(!this.config[col].renderer){
5807             return Roo.grid.ColumnModel.defaultRenderer;
5808         }
5809         return this.config[col].renderer;
5810     },
5811
5812     /**
5813      * Sets the rendering (formatting) function for a column.
5814      * @param {Number} col The column index
5815      * @param {Function} fn The function to use to process the cell's raw data
5816      * to return HTML markup for the grid view. The render function is called with
5817      * the following parameters:<ul>
5818      * <li>Data value.</li>
5819      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5820      * <li>css A CSS style string to apply to the table cell.</li>
5821      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5822      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5823      * <li>Row index</li>
5824      * <li>Column index</li>
5825      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5826      */
5827     setRenderer : function(col, fn){
5828         this.config[col].renderer = fn;
5829     },
5830
5831     /**
5832      * Returns the width for the specified column.
5833      * @param {Number} col The column index
5834      * @return {Number}
5835      */
5836     getColumnWidth : function(col){
5837         return this.config[col].width * 1 || this.defaultWidth;
5838     },
5839
5840     /**
5841      * Sets the width for a column.
5842      * @param {Number} col The column index
5843      * @param {Number} width The new width
5844      */
5845     setColumnWidth : function(col, width, suppressEvent){
5846         this.config[col].width = width;
5847         this.totalWidth = null;
5848         if(!suppressEvent){
5849              this.fireEvent("widthchange", this, col, width);
5850         }
5851     },
5852
5853     /**
5854      * Returns the total width of all columns.
5855      * @param {Boolean} includeHidden True to include hidden column widths
5856      * @return {Number}
5857      */
5858     getTotalWidth : function(includeHidden){
5859         if(!this.totalWidth){
5860             this.totalWidth = 0;
5861             for(var i = 0, len = this.config.length; i < len; i++){
5862                 if(includeHidden || !this.isHidden(i)){
5863                     this.totalWidth += this.getColumnWidth(i);
5864                 }
5865             }
5866         }
5867         return this.totalWidth;
5868     },
5869
5870     /**
5871      * Returns the header for the specified column.
5872      * @param {Number} col The column index
5873      * @return {String}
5874      */
5875     getColumnHeader : function(col){
5876         return this.config[col].header;
5877     },
5878
5879     /**
5880      * Sets the header for a column.
5881      * @param {Number} col The column index
5882      * @param {String} header The new header
5883      */
5884     setColumnHeader : function(col, header){
5885         this.config[col].header = header;
5886         this.fireEvent("headerchange", this, col, header);
5887     },
5888
5889     /**
5890      * Returns the tooltip for the specified column.
5891      * @param {Number} col The column index
5892      * @return {String}
5893      */
5894     getColumnTooltip : function(col){
5895             return this.config[col].tooltip;
5896     },
5897     /**
5898      * Sets the tooltip for a column.
5899      * @param {Number} col The column index
5900      * @param {String} tooltip The new tooltip
5901      */
5902     setColumnTooltip : function(col, tooltip){
5903             this.config[col].tooltip = tooltip;
5904     },
5905
5906     /**
5907      * Returns the dataIndex for the specified column.
5908      * @param {Number} col The column index
5909      * @return {Number}
5910      */
5911     getDataIndex : function(col){
5912         return this.config[col].dataIndex;
5913     },
5914
5915     /**
5916      * Sets the dataIndex for a column.
5917      * @param {Number} col The column index
5918      * @param {Number} dataIndex The new dataIndex
5919      */
5920     setDataIndex : function(col, dataIndex){
5921         this.config[col].dataIndex = dataIndex;
5922     },
5923
5924     
5925     
5926     /**
5927      * Returns true if the cell is editable.
5928      * @param {Number} colIndex The column index
5929      * @param {Number} rowIndex The row index - this is nto actually used..?
5930      * @return {Boolean}
5931      */
5932     isCellEditable : function(colIndex, rowIndex){
5933         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5934     },
5935
5936     /**
5937      * Returns the editor defined for the cell/column.
5938      * return false or null to disable editing.
5939      * @param {Number} colIndex The column index
5940      * @param {Number} rowIndex The row index
5941      * @return {Object}
5942      */
5943     getCellEditor : function(colIndex, rowIndex){
5944         return this.config[colIndex].editor;
5945     },
5946
5947     /**
5948      * Sets if a column is editable.
5949      * @param {Number} col The column index
5950      * @param {Boolean} editable True if the column is editable
5951      */
5952     setEditable : function(col, editable){
5953         this.config[col].editable = editable;
5954     },
5955
5956
5957     /**
5958      * Returns true if the column is hidden.
5959      * @param {Number} colIndex The column index
5960      * @return {Boolean}
5961      */
5962     isHidden : function(colIndex){
5963         return this.config[colIndex].hidden;
5964     },
5965
5966
5967     /**
5968      * Returns true if the column width cannot be changed
5969      */
5970     isFixed : function(colIndex){
5971         return this.config[colIndex].fixed;
5972     },
5973
5974     /**
5975      * Returns true if the column can be resized
5976      * @return {Boolean}
5977      */
5978     isResizable : function(colIndex){
5979         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5980     },
5981     /**
5982      * Sets if a column is hidden.
5983      * @param {Number} colIndex The column index
5984      * @param {Boolean} hidden True if the column is hidden
5985      */
5986     setHidden : function(colIndex, hidden){
5987         this.config[colIndex].hidden = hidden;
5988         this.totalWidth = null;
5989         this.fireEvent("hiddenchange", this, colIndex, hidden);
5990     },
5991
5992     /**
5993      * Sets the editor for a column.
5994      * @param {Number} col The column index
5995      * @param {Object} editor The editor object
5996      */
5997     setEditor : function(col, editor){
5998         this.config[col].editor = editor;
5999     }
6000 });
6001
6002 Roo.grid.ColumnModel.defaultRenderer = function(value)
6003 {
6004     if(typeof value == "object") {
6005         return value;
6006     }
6007         if(typeof value == "string" && value.length < 1){
6008             return "&#160;";
6009         }
6010     
6011         return String.format("{0}", value);
6012 };
6013
6014 // Alias for backwards compatibility
6015 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6016 /*
6017  * Based on:
6018  * Ext JS Library 1.1.1
6019  * Copyright(c) 2006-2007, Ext JS, LLC.
6020  *
6021  * Originally Released Under LGPL - original licence link has changed is not relivant.
6022  *
6023  * Fork - LGPL
6024  * <script type="text/javascript">
6025  */
6026  
6027 /**
6028  * @class Roo.LoadMask
6029  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6030  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6031  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6032  * element's UpdateManager load indicator and will be destroyed after the initial load.
6033  * @constructor
6034  * Create a new LoadMask
6035  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6036  * @param {Object} config The config object
6037  */
6038 Roo.LoadMask = function(el, config){
6039     this.el = Roo.get(el);
6040     Roo.apply(this, config);
6041     if(this.store){
6042         this.store.on('beforeload', this.onBeforeLoad, this);
6043         this.store.on('load', this.onLoad, this);
6044         this.store.on('loadexception', this.onLoadException, this);
6045         this.removeMask = false;
6046     }else{
6047         var um = this.el.getUpdateManager();
6048         um.showLoadIndicator = false; // disable the default indicator
6049         um.on('beforeupdate', this.onBeforeLoad, this);
6050         um.on('update', this.onLoad, this);
6051         um.on('failure', this.onLoad, this);
6052         this.removeMask = true;
6053     }
6054 };
6055
6056 Roo.LoadMask.prototype = {
6057     /**
6058      * @cfg {Boolean} removeMask
6059      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6060      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6061      */
6062     /**
6063      * @cfg {String} msg
6064      * The text to display in a centered loading message box (defaults to 'Loading...')
6065      */
6066     msg : 'Loading...',
6067     /**
6068      * @cfg {String} msgCls
6069      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6070      */
6071     msgCls : 'x-mask-loading',
6072
6073     /**
6074      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6075      * @type Boolean
6076      */
6077     disabled: false,
6078
6079     /**
6080      * Disables the mask to prevent it from being displayed
6081      */
6082     disable : function(){
6083        this.disabled = true;
6084     },
6085
6086     /**
6087      * Enables the mask so that it can be displayed
6088      */
6089     enable : function(){
6090         this.disabled = false;
6091     },
6092     
6093     onLoadException : function()
6094     {
6095         Roo.log(arguments);
6096         
6097         if (typeof(arguments[3]) != 'undefined') {
6098             Roo.MessageBox.alert("Error loading",arguments[3]);
6099         } 
6100         /*
6101         try {
6102             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6103                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6104             }   
6105         } catch(e) {
6106             
6107         }
6108         */
6109     
6110         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6111     },
6112     // private
6113     onLoad : function()
6114     {
6115         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6116     },
6117
6118     // private
6119     onBeforeLoad : function(){
6120         if(!this.disabled){
6121             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6122         }
6123     },
6124
6125     // private
6126     destroy : function(){
6127         if(this.store){
6128             this.store.un('beforeload', this.onBeforeLoad, this);
6129             this.store.un('load', this.onLoad, this);
6130             this.store.un('loadexception', this.onLoadException, this);
6131         }else{
6132             var um = this.el.getUpdateManager();
6133             um.un('beforeupdate', this.onBeforeLoad, this);
6134             um.un('update', this.onLoad, this);
6135             um.un('failure', this.onLoad, this);
6136         }
6137     }
6138 };/*
6139  * - LGPL
6140  *
6141  * table
6142  * 
6143  */
6144
6145 /**
6146  * @class Roo.bootstrap.Table
6147  * @extends Roo.bootstrap.Component
6148  * Bootstrap Table class
6149  * @cfg {String} cls table class
6150  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6151  * @cfg {String} bgcolor Specifies the background color for a table
6152  * @cfg {Number} border Specifies whether the table cells should have borders or not
6153  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6154  * @cfg {Number} cellspacing Specifies the space between cells
6155  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6156  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6157  * @cfg {String} sortable Specifies that the table should be sortable
6158  * @cfg {String} summary Specifies a summary of the content of a table
6159  * @cfg {Number} width Specifies the width of a table
6160  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6161  * 
6162  * @cfg {boolean} striped Should the rows be alternative striped
6163  * @cfg {boolean} bordered Add borders to the table
6164  * @cfg {boolean} hover Add hover highlighting
6165  * @cfg {boolean} condensed Format condensed
6166  * @cfg {boolean} responsive Format condensed
6167  * @cfg {Boolean} loadMask (true|false) default false
6168  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6169  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6170  * @cfg {Boolean} rowSelection (true|false) default false
6171  * @cfg {Boolean} cellSelection (true|false) default false
6172  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6173  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6174  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6175  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6176  
6177  * 
6178  * @constructor
6179  * Create a new Table
6180  * @param {Object} config The config object
6181  */
6182
6183 Roo.bootstrap.Table = function(config){
6184     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6185     
6186   
6187     
6188     // BC...
6189     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6190     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6191     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6192     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6193     
6194     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6195     if (this.sm) {
6196         this.sm.grid = this;
6197         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6198         this.sm = this.selModel;
6199         this.sm.xmodule = this.xmodule || false;
6200     }
6201     
6202     if (this.cm && typeof(this.cm.config) == 'undefined') {
6203         this.colModel = new Roo.grid.ColumnModel(this.cm);
6204         this.cm = this.colModel;
6205         this.cm.xmodule = this.xmodule || false;
6206     }
6207     if (this.store) {
6208         this.store= Roo.factory(this.store, Roo.data);
6209         this.ds = this.store;
6210         this.ds.xmodule = this.xmodule || false;
6211          
6212     }
6213     if (this.footer && this.store) {
6214         this.footer.dataSource = this.ds;
6215         this.footer = Roo.factory(this.footer);
6216     }
6217     
6218     /** @private */
6219     this.addEvents({
6220         /**
6221          * @event cellclick
6222          * Fires when a cell is clicked
6223          * @param {Roo.bootstrap.Table} this
6224          * @param {Roo.Element} el
6225          * @param {Number} rowIndex
6226          * @param {Number} columnIndex
6227          * @param {Roo.EventObject} e
6228          */
6229         "cellclick" : true,
6230         /**
6231          * @event celldblclick
6232          * Fires when a cell is double clicked
6233          * @param {Roo.bootstrap.Table} this
6234          * @param {Roo.Element} el
6235          * @param {Number} rowIndex
6236          * @param {Number} columnIndex
6237          * @param {Roo.EventObject} e
6238          */
6239         "celldblclick" : true,
6240         /**
6241          * @event rowclick
6242          * Fires when a row is clicked
6243          * @param {Roo.bootstrap.Table} this
6244          * @param {Roo.Element} el
6245          * @param {Number} rowIndex
6246          * @param {Roo.EventObject} e
6247          */
6248         "rowclick" : true,
6249         /**
6250          * @event rowdblclick
6251          * Fires when a row is double clicked
6252          * @param {Roo.bootstrap.Table} this
6253          * @param {Roo.Element} el
6254          * @param {Number} rowIndex
6255          * @param {Roo.EventObject} e
6256          */
6257         "rowdblclick" : true,
6258         /**
6259          * @event mouseover
6260          * Fires when a mouseover occur
6261          * @param {Roo.bootstrap.Table} this
6262          * @param {Roo.Element} el
6263          * @param {Number} rowIndex
6264          * @param {Number} columnIndex
6265          * @param {Roo.EventObject} e
6266          */
6267         "mouseover" : true,
6268         /**
6269          * @event mouseout
6270          * Fires when a mouseout occur
6271          * @param {Roo.bootstrap.Table} this
6272          * @param {Roo.Element} el
6273          * @param {Number} rowIndex
6274          * @param {Number} columnIndex
6275          * @param {Roo.EventObject} e
6276          */
6277         "mouseout" : true,
6278         /**
6279          * @event rowclass
6280          * Fires when a row is rendered, so you can change add a style to it.
6281          * @param {Roo.bootstrap.Table} this
6282          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6283          */
6284         'rowclass' : true,
6285           /**
6286          * @event rowsrendered
6287          * Fires when all the  rows have been rendered
6288          * @param {Roo.bootstrap.Table} this
6289          */
6290         'rowsrendered' : true,
6291         /**
6292          * @event contextmenu
6293          * The raw contextmenu event for the entire grid.
6294          * @param {Roo.EventObject} e
6295          */
6296         "contextmenu" : true,
6297         /**
6298          * @event rowcontextmenu
6299          * Fires when a row is right clicked
6300          * @param {Roo.bootstrap.Table} this
6301          * @param {Number} rowIndex
6302          * @param {Roo.EventObject} e
6303          */
6304         "rowcontextmenu" : true,
6305         /**
6306          * @event cellcontextmenu
6307          * Fires when a cell is right clicked
6308          * @param {Roo.bootstrap.Table} this
6309          * @param {Number} rowIndex
6310          * @param {Number} cellIndex
6311          * @param {Roo.EventObject} e
6312          */
6313          "cellcontextmenu" : true,
6314          /**
6315          * @event headercontextmenu
6316          * Fires when a header is right clicked
6317          * @param {Roo.bootstrap.Table} this
6318          * @param {Number} columnIndex
6319          * @param {Roo.EventObject} e
6320          */
6321         "headercontextmenu" : true
6322     });
6323 };
6324
6325 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6326     
6327     cls: false,
6328     align: false,
6329     bgcolor: false,
6330     border: false,
6331     cellpadding: false,
6332     cellspacing: false,
6333     frame: false,
6334     rules: false,
6335     sortable: false,
6336     summary: false,
6337     width: false,
6338     striped : false,
6339     scrollBody : false,
6340     bordered: false,
6341     hover:  false,
6342     condensed : false,
6343     responsive : false,
6344     sm : false,
6345     cm : false,
6346     store : false,
6347     loadMask : false,
6348     footerShow : true,
6349     headerShow : true,
6350   
6351     rowSelection : false,
6352     cellSelection : false,
6353     layout : false,
6354     
6355     // Roo.Element - the tbody
6356     mainBody: false,
6357     // Roo.Element - thead element
6358     mainHead: false,
6359     
6360     container: false, // used by gridpanel...
6361     
6362     lazyLoad : false,
6363     
6364     CSS : Roo.util.CSS,
6365     
6366     auto_hide_footer : false,
6367     
6368     getAutoCreate : function()
6369     {
6370         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6371         
6372         cfg = {
6373             tag: 'table',
6374             cls : 'table',
6375             cn : []
6376         };
6377         if (this.scrollBody) {
6378             cfg.cls += ' table-body-fixed';
6379         }    
6380         if (this.striped) {
6381             cfg.cls += ' table-striped';
6382         }
6383         
6384         if (this.hover) {
6385             cfg.cls += ' table-hover';
6386         }
6387         if (this.bordered) {
6388             cfg.cls += ' table-bordered';
6389         }
6390         if (this.condensed) {
6391             cfg.cls += ' table-condensed';
6392         }
6393         if (this.responsive) {
6394             cfg.cls += ' table-responsive';
6395         }
6396         
6397         if (this.cls) {
6398             cfg.cls+=  ' ' +this.cls;
6399         }
6400         
6401         // this lot should be simplifed...
6402         var _t = this;
6403         var cp = [
6404             'align',
6405             'bgcolor',
6406             'border',
6407             'cellpadding',
6408             'cellspacing',
6409             'frame',
6410             'rules',
6411             'sortable',
6412             'summary',
6413             'width'
6414         ].forEach(function(k) {
6415             if (_t[k]) {
6416                 cfg[k] = _t[k];
6417             }
6418         });
6419         
6420         
6421         if (this.layout) {
6422             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6423         }
6424         
6425         if(this.store || this.cm){
6426             if(this.headerShow){
6427                 cfg.cn.push(this.renderHeader());
6428             }
6429             
6430             cfg.cn.push(this.renderBody());
6431             
6432             if(this.footerShow){
6433                 cfg.cn.push(this.renderFooter());
6434             }
6435             // where does this come from?
6436             //cfg.cls+=  ' TableGrid';
6437         }
6438         
6439         return { cn : [ cfg ] };
6440     },
6441     
6442     initEvents : function()
6443     {   
6444         if(!this.store || !this.cm){
6445             return;
6446         }
6447         if (this.selModel) {
6448             this.selModel.initEvents();
6449         }
6450         
6451         
6452         //Roo.log('initEvents with ds!!!!');
6453         
6454         this.mainBody = this.el.select('tbody', true).first();
6455         this.mainHead = this.el.select('thead', true).first();
6456         this.mainFoot = this.el.select('tfoot', true).first();
6457         
6458         
6459         
6460         var _this = this;
6461         
6462         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6463             e.on('click', _this.sort, _this);
6464         });
6465         
6466         this.mainBody.on("click", this.onClick, this);
6467         this.mainBody.on("dblclick", this.onDblClick, this);
6468         
6469         // why is this done????? = it breaks dialogs??
6470         //this.parent().el.setStyle('position', 'relative');
6471         
6472         
6473         if (this.footer) {
6474             this.footer.parentId = this.id;
6475             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6476             
6477             if(this.lazyLoad){
6478                 this.el.select('tfoot tr td').first().addClass('hide');
6479             }
6480         } 
6481         
6482         if(this.loadMask) {
6483             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6484         }
6485         
6486         this.store.on('load', this.onLoad, this);
6487         this.store.on('beforeload', this.onBeforeLoad, this);
6488         this.store.on('update', this.onUpdate, this);
6489         this.store.on('add', this.onAdd, this);
6490         this.store.on("clear", this.clear, this);
6491         
6492         this.el.on("contextmenu", this.onContextMenu, this);
6493         
6494         this.mainBody.on('scroll', this.onBodyScroll, this);
6495         
6496         this.cm.on("headerchange", this.onHeaderChange, this);
6497         
6498         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6499         
6500     },
6501     
6502     onContextMenu : function(e, t)
6503     {
6504         this.processEvent("contextmenu", e);
6505     },
6506     
6507     processEvent : function(name, e)
6508     {
6509         if (name != 'touchstart' ) {
6510             this.fireEvent(name, e);    
6511         }
6512         
6513         var t = e.getTarget();
6514         
6515         var cell = Roo.get(t);
6516         
6517         if(!cell){
6518             return;
6519         }
6520         
6521         if(cell.findParent('tfoot', false, true)){
6522             return;
6523         }
6524         
6525         if(cell.findParent('thead', false, true)){
6526             
6527             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6528                 cell = Roo.get(t).findParent('th', false, true);
6529                 if (!cell) {
6530                     Roo.log("failed to find th in thead?");
6531                     Roo.log(e.getTarget());
6532                     return;
6533                 }
6534             }
6535             
6536             var cellIndex = cell.dom.cellIndex;
6537             
6538             var ename = name == 'touchstart' ? 'click' : name;
6539             this.fireEvent("header" + ename, this, cellIndex, e);
6540             
6541             return;
6542         }
6543         
6544         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6545             cell = Roo.get(t).findParent('td', false, true);
6546             if (!cell) {
6547                 Roo.log("failed to find th in tbody?");
6548                 Roo.log(e.getTarget());
6549                 return;
6550             }
6551         }
6552         
6553         var row = cell.findParent('tr', false, true);
6554         var cellIndex = cell.dom.cellIndex;
6555         var rowIndex = row.dom.rowIndex - 1;
6556         
6557         if(row !== false){
6558             
6559             this.fireEvent("row" + name, this, rowIndex, e);
6560             
6561             if(cell !== false){
6562             
6563                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6564             }
6565         }
6566         
6567     },
6568     
6569     onMouseover : function(e, el)
6570     {
6571         var cell = Roo.get(el);
6572         
6573         if(!cell){
6574             return;
6575         }
6576         
6577         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6578             cell = cell.findParent('td', false, true);
6579         }
6580         
6581         var row = cell.findParent('tr', false, true);
6582         var cellIndex = cell.dom.cellIndex;
6583         var rowIndex = row.dom.rowIndex - 1; // start from 0
6584         
6585         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6586         
6587     },
6588     
6589     onMouseout : function(e, el)
6590     {
6591         var cell = Roo.get(el);
6592         
6593         if(!cell){
6594             return;
6595         }
6596         
6597         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6598             cell = cell.findParent('td', false, true);
6599         }
6600         
6601         var row = cell.findParent('tr', false, true);
6602         var cellIndex = cell.dom.cellIndex;
6603         var rowIndex = row.dom.rowIndex - 1; // start from 0
6604         
6605         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6606         
6607     },
6608     
6609     onClick : function(e, el)
6610     {
6611         var cell = Roo.get(el);
6612         
6613         if(!cell || (!this.cellSelection && !this.rowSelection)){
6614             return;
6615         }
6616         
6617         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6618             cell = cell.findParent('td', false, true);
6619         }
6620         
6621         if(!cell || typeof(cell) == 'undefined'){
6622             return;
6623         }
6624         
6625         var row = cell.findParent('tr', false, true);
6626         
6627         if(!row || typeof(row) == 'undefined'){
6628             return;
6629         }
6630         
6631         var cellIndex = cell.dom.cellIndex;
6632         var rowIndex = this.getRowIndex(row);
6633         
6634         // why??? - should these not be based on SelectionModel?
6635         if(this.cellSelection){
6636             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6637         }
6638         
6639         if(this.rowSelection){
6640             this.fireEvent('rowclick', this, row, rowIndex, e);
6641         }
6642         
6643         
6644     },
6645         
6646     onDblClick : function(e,el)
6647     {
6648         var cell = Roo.get(el);
6649         
6650         if(!cell || (!this.cellSelection && !this.rowSelection)){
6651             return;
6652         }
6653         
6654         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6655             cell = cell.findParent('td', false, true);
6656         }
6657         
6658         if(!cell || typeof(cell) == 'undefined'){
6659             return;
6660         }
6661         
6662         var row = cell.findParent('tr', false, true);
6663         
6664         if(!row || typeof(row) == 'undefined'){
6665             return;
6666         }
6667         
6668         var cellIndex = cell.dom.cellIndex;
6669         var rowIndex = this.getRowIndex(row);
6670         
6671         if(this.cellSelection){
6672             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6673         }
6674         
6675         if(this.rowSelection){
6676             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6677         }
6678     },
6679     
6680     sort : function(e,el)
6681     {
6682         var col = Roo.get(el);
6683         
6684         if(!col.hasClass('sortable')){
6685             return;
6686         }
6687         
6688         var sort = col.attr('sort');
6689         var dir = 'ASC';
6690         
6691         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6692             dir = 'DESC';
6693         }
6694         
6695         this.store.sortInfo = {field : sort, direction : dir};
6696         
6697         if (this.footer) {
6698             Roo.log("calling footer first");
6699             this.footer.onClick('first');
6700         } else {
6701         
6702             this.store.load({ params : { start : 0 } });
6703         }
6704     },
6705     
6706     renderHeader : function()
6707     {
6708         var header = {
6709             tag: 'thead',
6710             cn : []
6711         };
6712         
6713         var cm = this.cm;
6714         this.totalWidth = 0;
6715         
6716         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6717             
6718             var config = cm.config[i];
6719             
6720             var c = {
6721                 tag: 'th',
6722                 cls : 'x-hcol-' + i,
6723                 style : '',
6724                 html: cm.getColumnHeader(i)
6725             };
6726             
6727             var hh = '';
6728             
6729             if(typeof(config.sortable) != 'undefined' && config.sortable){
6730                 c.cls = 'sortable';
6731                 c.html = '<i class="glyphicon"></i>' + c.html;
6732             }
6733             
6734             if(typeof(config.lgHeader) != 'undefined'){
6735                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6736             }
6737             
6738             if(typeof(config.mdHeader) != 'undefined'){
6739                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6740             }
6741             
6742             if(typeof(config.smHeader) != 'undefined'){
6743                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6744             }
6745             
6746             if(typeof(config.xsHeader) != 'undefined'){
6747                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6748             }
6749             
6750             if(hh.length){
6751                 c.html = hh;
6752             }
6753             
6754             if(typeof(config.tooltip) != 'undefined'){
6755                 c.tooltip = config.tooltip;
6756             }
6757             
6758             if(typeof(config.colspan) != 'undefined'){
6759                 c.colspan = config.colspan;
6760             }
6761             
6762             if(typeof(config.hidden) != 'undefined' && config.hidden){
6763                 c.style += ' display:none;';
6764             }
6765             
6766             if(typeof(config.dataIndex) != 'undefined'){
6767                 c.sort = config.dataIndex;
6768             }
6769             
6770            
6771             
6772             if(typeof(config.align) != 'undefined' && config.align.length){
6773                 c.style += ' text-align:' + config.align + ';';
6774             }
6775             
6776             if(typeof(config.width) != 'undefined'){
6777                 c.style += ' width:' + config.width + 'px;';
6778                 this.totalWidth += config.width;
6779             } else {
6780                 this.totalWidth += 100; // assume minimum of 100 per column?
6781             }
6782             
6783             if(typeof(config.cls) != 'undefined'){
6784                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6785             }
6786             
6787             ['xs','sm','md','lg'].map(function(size){
6788                 
6789                 if(typeof(config[size]) == 'undefined'){
6790                     return;
6791                 }
6792                 
6793                 if (!config[size]) { // 0 = hidden
6794                     c.cls += ' hidden-' + size;
6795                     return;
6796                 }
6797                 
6798                 c.cls += ' col-' + size + '-' + config[size];
6799
6800             });
6801             
6802             header.cn.push(c)
6803         }
6804         
6805         return header;
6806     },
6807     
6808     renderBody : function()
6809     {
6810         var body = {
6811             tag: 'tbody',
6812             cn : [
6813                 {
6814                     tag: 'tr',
6815                     cn : [
6816                         {
6817                             tag : 'td',
6818                             colspan :  this.cm.getColumnCount()
6819                         }
6820                     ]
6821                 }
6822             ]
6823         };
6824         
6825         return body;
6826     },
6827     
6828     renderFooter : function()
6829     {
6830         var footer = {
6831             tag: 'tfoot',
6832             cn : [
6833                 {
6834                     tag: 'tr',
6835                     cn : [
6836                         {
6837                             tag : 'td',
6838                             colspan :  this.cm.getColumnCount()
6839                         }
6840                     ]
6841                 }
6842             ]
6843         };
6844         
6845         return footer;
6846     },
6847     
6848     
6849     
6850     onLoad : function()
6851     {
6852 //        Roo.log('ds onload');
6853         this.clear();
6854         
6855         var _this = this;
6856         var cm = this.cm;
6857         var ds = this.store;
6858         
6859         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6860             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6861             if (_this.store.sortInfo) {
6862                     
6863                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6864                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6865                 }
6866                 
6867                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6868                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6869                 }
6870             }
6871         });
6872         
6873         var tbody =  this.mainBody;
6874               
6875         if(ds.getCount() > 0){
6876             ds.data.each(function(d,rowIndex){
6877                 var row =  this.renderRow(cm, ds, rowIndex);
6878                 
6879                 tbody.createChild(row);
6880                 
6881                 var _this = this;
6882                 
6883                 if(row.cellObjects.length){
6884                     Roo.each(row.cellObjects, function(r){
6885                         _this.renderCellObject(r);
6886                     })
6887                 }
6888                 
6889             }, this);
6890         }
6891         
6892         var tfoot = this.el.select('tfoot', true).first();
6893         
6894         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6895             
6896             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6897             
6898             var total = this.ds.getTotalCount();
6899             
6900             if(this.footer.pageSize < total){
6901                 this.mainFoot.show();
6902             }
6903         }
6904         
6905         Roo.each(this.el.select('tbody td', true).elements, function(e){
6906             e.on('mouseover', _this.onMouseover, _this);
6907         });
6908         
6909         Roo.each(this.el.select('tbody td', true).elements, function(e){
6910             e.on('mouseout', _this.onMouseout, _this);
6911         });
6912         this.fireEvent('rowsrendered', this);
6913         
6914         this.autoSize();
6915     },
6916     
6917     
6918     onUpdate : function(ds,record)
6919     {
6920         this.refreshRow(record);
6921         this.autoSize();
6922     },
6923     
6924     onRemove : function(ds, record, index, isUpdate){
6925         if(isUpdate !== true){
6926             this.fireEvent("beforerowremoved", this, index, record);
6927         }
6928         var bt = this.mainBody.dom;
6929         
6930         var rows = this.el.select('tbody > tr', true).elements;
6931         
6932         if(typeof(rows[index]) != 'undefined'){
6933             bt.removeChild(rows[index].dom);
6934         }
6935         
6936 //        if(bt.rows[index]){
6937 //            bt.removeChild(bt.rows[index]);
6938 //        }
6939         
6940         if(isUpdate !== true){
6941             //this.stripeRows(index);
6942             //this.syncRowHeights(index, index);
6943             //this.layout();
6944             this.fireEvent("rowremoved", this, index, record);
6945         }
6946     },
6947     
6948     onAdd : function(ds, records, rowIndex)
6949     {
6950         //Roo.log('on Add called');
6951         // - note this does not handle multiple adding very well..
6952         var bt = this.mainBody.dom;
6953         for (var i =0 ; i < records.length;i++) {
6954             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6955             //Roo.log(records[i]);
6956             //Roo.log(this.store.getAt(rowIndex+i));
6957             this.insertRow(this.store, rowIndex + i, false);
6958             return;
6959         }
6960         
6961     },
6962     
6963     
6964     refreshRow : function(record){
6965         var ds = this.store, index;
6966         if(typeof record == 'number'){
6967             index = record;
6968             record = ds.getAt(index);
6969         }else{
6970             index = ds.indexOf(record);
6971         }
6972         this.insertRow(ds, index, true);
6973         this.autoSize();
6974         this.onRemove(ds, record, index+1, true);
6975         this.autoSize();
6976         //this.syncRowHeights(index, index);
6977         //this.layout();
6978         this.fireEvent("rowupdated", this, index, record);
6979     },
6980     
6981     insertRow : function(dm, rowIndex, isUpdate){
6982         
6983         if(!isUpdate){
6984             this.fireEvent("beforerowsinserted", this, rowIndex);
6985         }
6986             //var s = this.getScrollState();
6987         var row = this.renderRow(this.cm, this.store, rowIndex);
6988         // insert before rowIndex..
6989         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6990         
6991         var _this = this;
6992                 
6993         if(row.cellObjects.length){
6994             Roo.each(row.cellObjects, function(r){
6995                 _this.renderCellObject(r);
6996             })
6997         }
6998             
6999         if(!isUpdate){
7000             this.fireEvent("rowsinserted", this, rowIndex);
7001             //this.syncRowHeights(firstRow, lastRow);
7002             //this.stripeRows(firstRow);
7003             //this.layout();
7004         }
7005         
7006     },
7007     
7008     
7009     getRowDom : function(rowIndex)
7010     {
7011         var rows = this.el.select('tbody > tr', true).elements;
7012         
7013         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7014         
7015     },
7016     // returns the object tree for a tr..
7017   
7018     
7019     renderRow : function(cm, ds, rowIndex) 
7020     {
7021         var d = ds.getAt(rowIndex);
7022         
7023         var row = {
7024             tag : 'tr',
7025             cls : 'x-row-' + rowIndex,
7026             cn : []
7027         };
7028             
7029         var cellObjects = [];
7030         
7031         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7032             var config = cm.config[i];
7033             
7034             var renderer = cm.getRenderer(i);
7035             var value = '';
7036             var id = false;
7037             
7038             if(typeof(renderer) !== 'undefined'){
7039                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7040             }
7041             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7042             // and are rendered into the cells after the row is rendered - using the id for the element.
7043             
7044             if(typeof(value) === 'object'){
7045                 id = Roo.id();
7046                 cellObjects.push({
7047                     container : id,
7048                     cfg : value 
7049                 })
7050             }
7051             
7052             var rowcfg = {
7053                 record: d,
7054                 rowIndex : rowIndex,
7055                 colIndex : i,
7056                 rowClass : ''
7057             };
7058
7059             this.fireEvent('rowclass', this, rowcfg);
7060             
7061             var td = {
7062                 tag: 'td',
7063                 cls : rowcfg.rowClass + ' x-col-' + i,
7064                 style: '',
7065                 html: (typeof(value) === 'object') ? '' : value
7066             };
7067             
7068             if (id) {
7069                 td.id = id;
7070             }
7071             
7072             if(typeof(config.colspan) != 'undefined'){
7073                 td.colspan = config.colspan;
7074             }
7075             
7076             if(typeof(config.hidden) != 'undefined' && config.hidden){
7077                 td.style += ' display:none;';
7078             }
7079             
7080             if(typeof(config.align) != 'undefined' && config.align.length){
7081                 td.style += ' text-align:' + config.align + ';';
7082             }
7083             if(typeof(config.valign) != 'undefined' && config.valign.length){
7084                 td.style += ' vertical-align:' + config.valign + ';';
7085             }
7086             
7087             if(typeof(config.width) != 'undefined'){
7088                 td.style += ' width:' +  config.width + 'px;';
7089             }
7090             
7091             if(typeof(config.cursor) != 'undefined'){
7092                 td.style += ' cursor:' +  config.cursor + ';';
7093             }
7094             
7095             if(typeof(config.cls) != 'undefined'){
7096                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7097             }
7098             
7099             ['xs','sm','md','lg'].map(function(size){
7100                 
7101                 if(typeof(config[size]) == 'undefined'){
7102                     return;
7103                 }
7104                 
7105                 if (!config[size]) { // 0 = hidden
7106                     td.cls += ' hidden-' + size;
7107                     return;
7108                 }
7109                 
7110                 td.cls += ' col-' + size + '-' + config[size];
7111
7112             });
7113             
7114             row.cn.push(td);
7115            
7116         }
7117         
7118         row.cellObjects = cellObjects;
7119         
7120         return row;
7121           
7122     },
7123     
7124     
7125     
7126     onBeforeLoad : function()
7127     {
7128         
7129     },
7130      /**
7131      * Remove all rows
7132      */
7133     clear : function()
7134     {
7135         this.el.select('tbody', true).first().dom.innerHTML = '';
7136     },
7137     /**
7138      * Show or hide a row.
7139      * @param {Number} rowIndex to show or hide
7140      * @param {Boolean} state hide
7141      */
7142     setRowVisibility : function(rowIndex, state)
7143     {
7144         var bt = this.mainBody.dom;
7145         
7146         var rows = this.el.select('tbody > tr', true).elements;
7147         
7148         if(typeof(rows[rowIndex]) == 'undefined'){
7149             return;
7150         }
7151         rows[rowIndex].dom.style.display = state ? '' : 'none';
7152     },
7153     
7154     
7155     getSelectionModel : function(){
7156         if(!this.selModel){
7157             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7158         }
7159         return this.selModel;
7160     },
7161     /*
7162      * Render the Roo.bootstrap object from renderder
7163      */
7164     renderCellObject : function(r)
7165     {
7166         var _this = this;
7167         
7168         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7169         
7170         var t = r.cfg.render(r.container);
7171         
7172         if(r.cfg.cn){
7173             Roo.each(r.cfg.cn, function(c){
7174                 var child = {
7175                     container: t.getChildContainer(),
7176                     cfg: c
7177                 };
7178                 _this.renderCellObject(child);
7179             })
7180         }
7181     },
7182     
7183     getRowIndex : function(row)
7184     {
7185         var rowIndex = -1;
7186         
7187         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7188             if(el != row){
7189                 return;
7190             }
7191             
7192             rowIndex = index;
7193         });
7194         
7195         return rowIndex;
7196     },
7197      /**
7198      * Returns the grid's underlying element = used by panel.Grid
7199      * @return {Element} The element
7200      */
7201     getGridEl : function(){
7202         return this.el;
7203     },
7204      /**
7205      * Forces a resize - used by panel.Grid
7206      * @return {Element} The element
7207      */
7208     autoSize : function()
7209     {
7210         //var ctr = Roo.get(this.container.dom.parentElement);
7211         var ctr = Roo.get(this.el.dom);
7212         
7213         var thd = this.getGridEl().select('thead',true).first();
7214         var tbd = this.getGridEl().select('tbody', true).first();
7215         var tfd = this.getGridEl().select('tfoot', true).first();
7216         
7217         var cw = ctr.getWidth();
7218         
7219         if (tbd) {
7220             
7221             tbd.setSize(ctr.getWidth(),
7222                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7223             );
7224             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7225             cw -= barsize;
7226         }
7227         cw = Math.max(cw, this.totalWidth);
7228         this.getGridEl().select('tr',true).setWidth(cw);
7229         // resize 'expandable coloumn?
7230         
7231         return; // we doe not have a view in this design..
7232         
7233     },
7234     onBodyScroll: function()
7235     {
7236         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7237         if(this.mainHead){
7238             this.mainHead.setStyle({
7239                 'position' : 'relative',
7240                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7241             });
7242         }
7243         
7244         if(this.lazyLoad){
7245             
7246             var scrollHeight = this.mainBody.dom.scrollHeight;
7247             
7248             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7249             
7250             var height = this.mainBody.getHeight();
7251             
7252             if(scrollHeight - height == scrollTop) {
7253                 
7254                 var total = this.ds.getTotalCount();
7255                 
7256                 if(this.footer.cursor + this.footer.pageSize < total){
7257                     
7258                     this.footer.ds.load({
7259                         params : {
7260                             start : this.footer.cursor + this.footer.pageSize,
7261                             limit : this.footer.pageSize
7262                         },
7263                         add : true
7264                     });
7265                 }
7266             }
7267             
7268         }
7269     },
7270     
7271     onHeaderChange : function()
7272     {
7273         var header = this.renderHeader();
7274         var table = this.el.select('table', true).first();
7275         
7276         this.mainHead.remove();
7277         this.mainHead = table.createChild(header, this.mainBody, false);
7278     },
7279     
7280     onHiddenChange : function(colModel, colIndex, hidden)
7281     {
7282         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7283         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7284         
7285         this.CSS.updateRule(thSelector, "display", "");
7286         this.CSS.updateRule(tdSelector, "display", "");
7287         
7288         if(hidden){
7289             this.CSS.updateRule(thSelector, "display", "none");
7290             this.CSS.updateRule(tdSelector, "display", "none");
7291         }
7292         
7293         this.onHeaderChange();
7294         this.onLoad();
7295     },
7296     
7297     setColumnWidth: function(col_index, width)
7298     {
7299         // width = "md-2 xs-2..."
7300         if(!this.colModel.config[col_index]) {
7301             return;
7302         }
7303         
7304         var w = width.split(" ");
7305         
7306         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7307         
7308         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7309         
7310         
7311         for(var j = 0; j < w.length; j++) {
7312             
7313             if(!w[j]) {
7314                 continue;
7315             }
7316             
7317             var size_cls = w[j].split("-");
7318             
7319             if(!Number.isInteger(size_cls[1] * 1)) {
7320                 continue;
7321             }
7322             
7323             if(!this.colModel.config[col_index][size_cls[0]]) {
7324                 continue;
7325             }
7326             
7327             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7328                 continue;
7329             }
7330             
7331             h_row[0].classList.replace(
7332                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7333                 "col-"+size_cls[0]+"-"+size_cls[1]
7334             );
7335             
7336             for(var i = 0; i < rows.length; i++) {
7337                 
7338                 var size_cls = w[j].split("-");
7339                 
7340                 if(!Number.isInteger(size_cls[1] * 1)) {
7341                     continue;
7342                 }
7343                 
7344                 if(!this.colModel.config[col_index][size_cls[0]]) {
7345                     continue;
7346                 }
7347                 
7348                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7349                     continue;
7350                 }
7351                 
7352                 rows[i].classList.replace(
7353                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7354                     "col-"+size_cls[0]+"-"+size_cls[1]
7355                 );
7356             }
7357             
7358             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7359         }
7360     }
7361 });
7362
7363  
7364
7365  /*
7366  * - LGPL
7367  *
7368  * table cell
7369  * 
7370  */
7371
7372 /**
7373  * @class Roo.bootstrap.TableCell
7374  * @extends Roo.bootstrap.Component
7375  * Bootstrap TableCell class
7376  * @cfg {String} html cell contain text
7377  * @cfg {String} cls cell class
7378  * @cfg {String} tag cell tag (td|th) default td
7379  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7380  * @cfg {String} align Aligns the content in a cell
7381  * @cfg {String} axis Categorizes cells
7382  * @cfg {String} bgcolor Specifies the background color of a cell
7383  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7384  * @cfg {Number} colspan Specifies the number of columns a cell should span
7385  * @cfg {String} headers Specifies one or more header cells a cell is related to
7386  * @cfg {Number} height Sets the height of a cell
7387  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7388  * @cfg {Number} rowspan Sets the number of rows a cell should span
7389  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7390  * @cfg {String} valign Vertical aligns the content in a cell
7391  * @cfg {Number} width Specifies the width of a cell
7392  * 
7393  * @constructor
7394  * Create a new TableCell
7395  * @param {Object} config The config object
7396  */
7397
7398 Roo.bootstrap.TableCell = function(config){
7399     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7400 };
7401
7402 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7403     
7404     html: false,
7405     cls: false,
7406     tag: false,
7407     abbr: false,
7408     align: false,
7409     axis: false,
7410     bgcolor: false,
7411     charoff: false,
7412     colspan: false,
7413     headers: false,
7414     height: false,
7415     nowrap: false,
7416     rowspan: false,
7417     scope: false,
7418     valign: false,
7419     width: false,
7420     
7421     
7422     getAutoCreate : function(){
7423         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7424         
7425         cfg = {
7426             tag: 'td'
7427         };
7428         
7429         if(this.tag){
7430             cfg.tag = this.tag;
7431         }
7432         
7433         if (this.html) {
7434             cfg.html=this.html
7435         }
7436         if (this.cls) {
7437             cfg.cls=this.cls
7438         }
7439         if (this.abbr) {
7440             cfg.abbr=this.abbr
7441         }
7442         if (this.align) {
7443             cfg.align=this.align
7444         }
7445         if (this.axis) {
7446             cfg.axis=this.axis
7447         }
7448         if (this.bgcolor) {
7449             cfg.bgcolor=this.bgcolor
7450         }
7451         if (this.charoff) {
7452             cfg.charoff=this.charoff
7453         }
7454         if (this.colspan) {
7455             cfg.colspan=this.colspan
7456         }
7457         if (this.headers) {
7458             cfg.headers=this.headers
7459         }
7460         if (this.height) {
7461             cfg.height=this.height
7462         }
7463         if (this.nowrap) {
7464             cfg.nowrap=this.nowrap
7465         }
7466         if (this.rowspan) {
7467             cfg.rowspan=this.rowspan
7468         }
7469         if (this.scope) {
7470             cfg.scope=this.scope
7471         }
7472         if (this.valign) {
7473             cfg.valign=this.valign
7474         }
7475         if (this.width) {
7476             cfg.width=this.width
7477         }
7478         
7479         
7480         return cfg;
7481     }
7482    
7483 });
7484
7485  
7486
7487  /*
7488  * - LGPL
7489  *
7490  * table row
7491  * 
7492  */
7493
7494 /**
7495  * @class Roo.bootstrap.TableRow
7496  * @extends Roo.bootstrap.Component
7497  * Bootstrap TableRow class
7498  * @cfg {String} cls row class
7499  * @cfg {String} align Aligns the content in a table row
7500  * @cfg {String} bgcolor Specifies a background color for a table row
7501  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7502  * @cfg {String} valign Vertical aligns the content in a table row
7503  * 
7504  * @constructor
7505  * Create a new TableRow
7506  * @param {Object} config The config object
7507  */
7508
7509 Roo.bootstrap.TableRow = function(config){
7510     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7511 };
7512
7513 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7514     
7515     cls: false,
7516     align: false,
7517     bgcolor: false,
7518     charoff: false,
7519     valign: false,
7520     
7521     getAutoCreate : function(){
7522         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7523         
7524         cfg = {
7525             tag: 'tr'
7526         };
7527             
7528         if(this.cls){
7529             cfg.cls = this.cls;
7530         }
7531         if(this.align){
7532             cfg.align = this.align;
7533         }
7534         if(this.bgcolor){
7535             cfg.bgcolor = this.bgcolor;
7536         }
7537         if(this.charoff){
7538             cfg.charoff = this.charoff;
7539         }
7540         if(this.valign){
7541             cfg.valign = this.valign;
7542         }
7543         
7544         return cfg;
7545     }
7546    
7547 });
7548
7549  
7550
7551  /*
7552  * - LGPL
7553  *
7554  * table body
7555  * 
7556  */
7557
7558 /**
7559  * @class Roo.bootstrap.TableBody
7560  * @extends Roo.bootstrap.Component
7561  * Bootstrap TableBody class
7562  * @cfg {String} cls element class
7563  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7564  * @cfg {String} align Aligns the content inside the element
7565  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7566  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7567  * 
7568  * @constructor
7569  * Create a new TableBody
7570  * @param {Object} config The config object
7571  */
7572
7573 Roo.bootstrap.TableBody = function(config){
7574     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7575 };
7576
7577 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7578     
7579     cls: false,
7580     tag: false,
7581     align: false,
7582     charoff: false,
7583     valign: false,
7584     
7585     getAutoCreate : function(){
7586         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7587         
7588         cfg = {
7589             tag: 'tbody'
7590         };
7591             
7592         if (this.cls) {
7593             cfg.cls=this.cls
7594         }
7595         if(this.tag){
7596             cfg.tag = this.tag;
7597         }
7598         
7599         if(this.align){
7600             cfg.align = this.align;
7601         }
7602         if(this.charoff){
7603             cfg.charoff = this.charoff;
7604         }
7605         if(this.valign){
7606             cfg.valign = this.valign;
7607         }
7608         
7609         return cfg;
7610     }
7611     
7612     
7613 //    initEvents : function()
7614 //    {
7615 //        
7616 //        if(!this.store){
7617 //            return;
7618 //        }
7619 //        
7620 //        this.store = Roo.factory(this.store, Roo.data);
7621 //        this.store.on('load', this.onLoad, this);
7622 //        
7623 //        this.store.load();
7624 //        
7625 //    },
7626 //    
7627 //    onLoad: function () 
7628 //    {   
7629 //        this.fireEvent('load', this);
7630 //    }
7631 //    
7632 //   
7633 });
7634
7635  
7636
7637  /*
7638  * Based on:
7639  * Ext JS Library 1.1.1
7640  * Copyright(c) 2006-2007, Ext JS, LLC.
7641  *
7642  * Originally Released Under LGPL - original licence link has changed is not relivant.
7643  *
7644  * Fork - LGPL
7645  * <script type="text/javascript">
7646  */
7647
7648 // as we use this in bootstrap.
7649 Roo.namespace('Roo.form');
7650  /**
7651  * @class Roo.form.Action
7652  * Internal Class used to handle form actions
7653  * @constructor
7654  * @param {Roo.form.BasicForm} el The form element or its id
7655  * @param {Object} config Configuration options
7656  */
7657
7658  
7659  
7660 // define the action interface
7661 Roo.form.Action = function(form, options){
7662     this.form = form;
7663     this.options = options || {};
7664 };
7665 /**
7666  * Client Validation Failed
7667  * @const 
7668  */
7669 Roo.form.Action.CLIENT_INVALID = 'client';
7670 /**
7671  * Server Validation Failed
7672  * @const 
7673  */
7674 Roo.form.Action.SERVER_INVALID = 'server';
7675  /**
7676  * Connect to Server Failed
7677  * @const 
7678  */
7679 Roo.form.Action.CONNECT_FAILURE = 'connect';
7680 /**
7681  * Reading Data from Server Failed
7682  * @const 
7683  */
7684 Roo.form.Action.LOAD_FAILURE = 'load';
7685
7686 Roo.form.Action.prototype = {
7687     type : 'default',
7688     failureType : undefined,
7689     response : undefined,
7690     result : undefined,
7691
7692     // interface method
7693     run : function(options){
7694
7695     },
7696
7697     // interface method
7698     success : function(response){
7699
7700     },
7701
7702     // interface method
7703     handleResponse : function(response){
7704
7705     },
7706
7707     // default connection failure
7708     failure : function(response){
7709         
7710         this.response = response;
7711         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7712         this.form.afterAction(this, false);
7713     },
7714
7715     processResponse : function(response){
7716         this.response = response;
7717         if(!response.responseText){
7718             return true;
7719         }
7720         this.result = this.handleResponse(response);
7721         return this.result;
7722     },
7723
7724     // utility functions used internally
7725     getUrl : function(appendParams){
7726         var url = this.options.url || this.form.url || this.form.el.dom.action;
7727         if(appendParams){
7728             var p = this.getParams();
7729             if(p){
7730                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7731             }
7732         }
7733         return url;
7734     },
7735
7736     getMethod : function(){
7737         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7738     },
7739
7740     getParams : function(){
7741         var bp = this.form.baseParams;
7742         var p = this.options.params;
7743         if(p){
7744             if(typeof p == "object"){
7745                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7746             }else if(typeof p == 'string' && bp){
7747                 p += '&' + Roo.urlEncode(bp);
7748             }
7749         }else if(bp){
7750             p = Roo.urlEncode(bp);
7751         }
7752         return p;
7753     },
7754
7755     createCallback : function(){
7756         return {
7757             success: this.success,
7758             failure: this.failure,
7759             scope: this,
7760             timeout: (this.form.timeout*1000),
7761             upload: this.form.fileUpload ? this.success : undefined
7762         };
7763     }
7764 };
7765
7766 Roo.form.Action.Submit = function(form, options){
7767     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7768 };
7769
7770 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7771     type : 'submit',
7772
7773     haveProgress : false,
7774     uploadComplete : false,
7775     
7776     // uploadProgress indicator.
7777     uploadProgress : function()
7778     {
7779         if (!this.form.progressUrl) {
7780             return;
7781         }
7782         
7783         if (!this.haveProgress) {
7784             Roo.MessageBox.progress("Uploading", "Uploading");
7785         }
7786         if (this.uploadComplete) {
7787            Roo.MessageBox.hide();
7788            return;
7789         }
7790         
7791         this.haveProgress = true;
7792    
7793         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7794         
7795         var c = new Roo.data.Connection();
7796         c.request({
7797             url : this.form.progressUrl,
7798             params: {
7799                 id : uid
7800             },
7801             method: 'GET',
7802             success : function(req){
7803                //console.log(data);
7804                 var rdata = false;
7805                 var edata;
7806                 try  {
7807                    rdata = Roo.decode(req.responseText)
7808                 } catch (e) {
7809                     Roo.log("Invalid data from server..");
7810                     Roo.log(edata);
7811                     return;
7812                 }
7813                 if (!rdata || !rdata.success) {
7814                     Roo.log(rdata);
7815                     Roo.MessageBox.alert(Roo.encode(rdata));
7816                     return;
7817                 }
7818                 var data = rdata.data;
7819                 
7820                 if (this.uploadComplete) {
7821                    Roo.MessageBox.hide();
7822                    return;
7823                 }
7824                    
7825                 if (data){
7826                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7827                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7828                     );
7829                 }
7830                 this.uploadProgress.defer(2000,this);
7831             },
7832        
7833             failure: function(data) {
7834                 Roo.log('progress url failed ');
7835                 Roo.log(data);
7836             },
7837             scope : this
7838         });
7839            
7840     },
7841     
7842     
7843     run : function()
7844     {
7845         // run get Values on the form, so it syncs any secondary forms.
7846         this.form.getValues();
7847         
7848         var o = this.options;
7849         var method = this.getMethod();
7850         var isPost = method == 'POST';
7851         if(o.clientValidation === false || this.form.isValid()){
7852             
7853             if (this.form.progressUrl) {
7854                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7855                     (new Date() * 1) + '' + Math.random());
7856                     
7857             } 
7858             
7859             
7860             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7861                 form:this.form.el.dom,
7862                 url:this.getUrl(!isPost),
7863                 method: method,
7864                 params:isPost ? this.getParams() : null,
7865                 isUpload: this.form.fileUpload
7866             }));
7867             
7868             this.uploadProgress();
7869
7870         }else if (o.clientValidation !== false){ // client validation failed
7871             this.failureType = Roo.form.Action.CLIENT_INVALID;
7872             this.form.afterAction(this, false);
7873         }
7874     },
7875
7876     success : function(response)
7877     {
7878         this.uploadComplete= true;
7879         if (this.haveProgress) {
7880             Roo.MessageBox.hide();
7881         }
7882         
7883         
7884         var result = this.processResponse(response);
7885         if(result === true || result.success){
7886             this.form.afterAction(this, true);
7887             return;
7888         }
7889         if(result.errors){
7890             this.form.markInvalid(result.errors);
7891             this.failureType = Roo.form.Action.SERVER_INVALID;
7892         }
7893         this.form.afterAction(this, false);
7894     },
7895     failure : function(response)
7896     {
7897         this.uploadComplete= true;
7898         if (this.haveProgress) {
7899             Roo.MessageBox.hide();
7900         }
7901         
7902         this.response = response;
7903         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7904         this.form.afterAction(this, false);
7905     },
7906     
7907     handleResponse : function(response){
7908         if(this.form.errorReader){
7909             var rs = this.form.errorReader.read(response);
7910             var errors = [];
7911             if(rs.records){
7912                 for(var i = 0, len = rs.records.length; i < len; i++) {
7913                     var r = rs.records[i];
7914                     errors[i] = r.data;
7915                 }
7916             }
7917             if(errors.length < 1){
7918                 errors = null;
7919             }
7920             return {
7921                 success : rs.success,
7922                 errors : errors
7923             };
7924         }
7925         var ret = false;
7926         try {
7927             ret = Roo.decode(response.responseText);
7928         } catch (e) {
7929             ret = {
7930                 success: false,
7931                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7932                 errors : []
7933             };
7934         }
7935         return ret;
7936         
7937     }
7938 });
7939
7940
7941 Roo.form.Action.Load = function(form, options){
7942     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7943     this.reader = this.form.reader;
7944 };
7945
7946 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7947     type : 'load',
7948
7949     run : function(){
7950         
7951         Roo.Ajax.request(Roo.apply(
7952                 this.createCallback(), {
7953                     method:this.getMethod(),
7954                     url:this.getUrl(false),
7955                     params:this.getParams()
7956         }));
7957     },
7958
7959     success : function(response){
7960         
7961         var result = this.processResponse(response);
7962         if(result === true || !result.success || !result.data){
7963             this.failureType = Roo.form.Action.LOAD_FAILURE;
7964             this.form.afterAction(this, false);
7965             return;
7966         }
7967         this.form.clearInvalid();
7968         this.form.setValues(result.data);
7969         this.form.afterAction(this, true);
7970     },
7971
7972     handleResponse : function(response){
7973         if(this.form.reader){
7974             var rs = this.form.reader.read(response);
7975             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7976             return {
7977                 success : rs.success,
7978                 data : data
7979             };
7980         }
7981         return Roo.decode(response.responseText);
7982     }
7983 });
7984
7985 Roo.form.Action.ACTION_TYPES = {
7986     'load' : Roo.form.Action.Load,
7987     'submit' : Roo.form.Action.Submit
7988 };/*
7989  * - LGPL
7990  *
7991  * form
7992  *
7993  */
7994
7995 /**
7996  * @class Roo.bootstrap.Form
7997  * @extends Roo.bootstrap.Component
7998  * Bootstrap Form class
7999  * @cfg {String} method  GET | POST (default POST)
8000  * @cfg {String} labelAlign top | left (default top)
8001  * @cfg {String} align left  | right - for navbars
8002  * @cfg {Boolean} loadMask load mask when submit (default true)
8003
8004  *
8005  * @constructor
8006  * Create a new Form
8007  * @param {Object} config The config object
8008  */
8009
8010
8011 Roo.bootstrap.Form = function(config){
8012     
8013     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8014     
8015     Roo.bootstrap.Form.popover.apply();
8016     
8017     this.addEvents({
8018         /**
8019          * @event clientvalidation
8020          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8021          * @param {Form} this
8022          * @param {Boolean} valid true if the form has passed client-side validation
8023          */
8024         clientvalidation: true,
8025         /**
8026          * @event beforeaction
8027          * Fires before any action is performed. Return false to cancel the action.
8028          * @param {Form} this
8029          * @param {Action} action The action to be performed
8030          */
8031         beforeaction: true,
8032         /**
8033          * @event actionfailed
8034          * Fires when an action fails.
8035          * @param {Form} this
8036          * @param {Action} action The action that failed
8037          */
8038         actionfailed : true,
8039         /**
8040          * @event actioncomplete
8041          * Fires when an action is completed.
8042          * @param {Form} this
8043          * @param {Action} action The action that completed
8044          */
8045         actioncomplete : true
8046     });
8047 };
8048
8049 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8050
8051      /**
8052      * @cfg {String} method
8053      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8054      */
8055     method : 'POST',
8056     /**
8057      * @cfg {String} url
8058      * The URL to use for form actions if one isn't supplied in the action options.
8059      */
8060     /**
8061      * @cfg {Boolean} fileUpload
8062      * Set to true if this form is a file upload.
8063      */
8064
8065     /**
8066      * @cfg {Object} baseParams
8067      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8068      */
8069
8070     /**
8071      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8072      */
8073     timeout: 30,
8074     /**
8075      * @cfg {Sting} align (left|right) for navbar forms
8076      */
8077     align : 'left',
8078
8079     // private
8080     activeAction : null,
8081
8082     /**
8083      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8084      * element by passing it or its id or mask the form itself by passing in true.
8085      * @type Mixed
8086      */
8087     waitMsgTarget : false,
8088
8089     loadMask : true,
8090     
8091     /**
8092      * @cfg {Boolean} errorMask (true|false) default false
8093      */
8094     errorMask : false,
8095     
8096     /**
8097      * @cfg {Number} maskOffset Default 100
8098      */
8099     maskOffset : 100,
8100     
8101     /**
8102      * @cfg {Boolean} maskBody
8103      */
8104     maskBody : false,
8105
8106     getAutoCreate : function(){
8107
8108         var cfg = {
8109             tag: 'form',
8110             method : this.method || 'POST',
8111             id : this.id || Roo.id(),
8112             cls : ''
8113         };
8114         if (this.parent().xtype.match(/^Nav/)) {
8115             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8116
8117         }
8118
8119         if (this.labelAlign == 'left' ) {
8120             cfg.cls += ' form-horizontal';
8121         }
8122
8123
8124         return cfg;
8125     },
8126     initEvents : function()
8127     {
8128         this.el.on('submit', this.onSubmit, this);
8129         // this was added as random key presses on the form where triggering form submit.
8130         this.el.on('keypress', function(e) {
8131             if (e.getCharCode() != 13) {
8132                 return true;
8133             }
8134             // we might need to allow it for textareas.. and some other items.
8135             // check e.getTarget().
8136
8137             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8138                 return true;
8139             }
8140
8141             Roo.log("keypress blocked");
8142
8143             e.preventDefault();
8144             return false;
8145         });
8146         
8147     },
8148     // private
8149     onSubmit : function(e){
8150         e.stopEvent();
8151     },
8152
8153      /**
8154      * Returns true if client-side validation on the form is successful.
8155      * @return Boolean
8156      */
8157     isValid : function(){
8158         var items = this.getItems();
8159         var valid = true;
8160         var target = false;
8161         
8162         items.each(function(f){
8163             
8164             if(f.validate()){
8165                 return;
8166             }
8167             
8168             Roo.log('invalid field: ' + f.name);
8169             
8170             valid = false;
8171
8172             if(!target && f.el.isVisible(true)){
8173                 target = f;
8174             }
8175            
8176         });
8177         
8178         if(this.errorMask && !valid){
8179             Roo.bootstrap.Form.popover.mask(this, target);
8180         }
8181         
8182         return valid;
8183     },
8184     
8185     /**
8186      * Returns true if any fields in this form have changed since their original load.
8187      * @return Boolean
8188      */
8189     isDirty : function(){
8190         var dirty = false;
8191         var items = this.getItems();
8192         items.each(function(f){
8193            if(f.isDirty()){
8194                dirty = true;
8195                return false;
8196            }
8197            return true;
8198         });
8199         return dirty;
8200     },
8201      /**
8202      * Performs a predefined action (submit or load) or custom actions you define on this form.
8203      * @param {String} actionName The name of the action type
8204      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8205      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8206      * accept other config options):
8207      * <pre>
8208 Property          Type             Description
8209 ----------------  ---------------  ----------------------------------------------------------------------------------
8210 url               String           The url for the action (defaults to the form's url)
8211 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8212 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8213 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8214                                    validate the form on the client (defaults to false)
8215      * </pre>
8216      * @return {BasicForm} this
8217      */
8218     doAction : function(action, options){
8219         if(typeof action == 'string'){
8220             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8221         }
8222         if(this.fireEvent('beforeaction', this, action) !== false){
8223             this.beforeAction(action);
8224             action.run.defer(100, action);
8225         }
8226         return this;
8227     },
8228
8229     // private
8230     beforeAction : function(action){
8231         var o = action.options;
8232         
8233         if(this.loadMask){
8234             
8235             if(this.maskBody){
8236                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8237             } else {
8238                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8239             }
8240         }
8241         // not really supported yet.. ??
8242
8243         //if(this.waitMsgTarget === true){
8244         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8245         //}else if(this.waitMsgTarget){
8246         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8247         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8248         //}else {
8249         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8250        // }
8251
8252     },
8253
8254     // private
8255     afterAction : function(action, success){
8256         this.activeAction = null;
8257         var o = action.options;
8258
8259         if(this.loadMask){
8260             
8261             if(this.maskBody){
8262                 Roo.get(document.body).unmask();
8263             } else {
8264                 this.el.unmask();
8265             }
8266         }
8267         
8268         //if(this.waitMsgTarget === true){
8269 //            this.el.unmask();
8270         //}else if(this.waitMsgTarget){
8271         //    this.waitMsgTarget.unmask();
8272         //}else{
8273         //    Roo.MessageBox.updateProgress(1);
8274         //    Roo.MessageBox.hide();
8275        // }
8276         //
8277         if(success){
8278             if(o.reset){
8279                 this.reset();
8280             }
8281             Roo.callback(o.success, o.scope, [this, action]);
8282             this.fireEvent('actioncomplete', this, action);
8283
8284         }else{
8285
8286             // failure condition..
8287             // we have a scenario where updates need confirming.
8288             // eg. if a locking scenario exists..
8289             // we look for { errors : { needs_confirm : true }} in the response.
8290             if (
8291                 (typeof(action.result) != 'undefined')  &&
8292                 (typeof(action.result.errors) != 'undefined')  &&
8293                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8294            ){
8295                 var _t = this;
8296                 Roo.log("not supported yet");
8297                  /*
8298
8299                 Roo.MessageBox.confirm(
8300                     "Change requires confirmation",
8301                     action.result.errorMsg,
8302                     function(r) {
8303                         if (r != 'yes') {
8304                             return;
8305                         }
8306                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8307                     }
8308
8309                 );
8310                 */
8311
8312
8313                 return;
8314             }
8315
8316             Roo.callback(o.failure, o.scope, [this, action]);
8317             // show an error message if no failed handler is set..
8318             if (!this.hasListener('actionfailed')) {
8319                 Roo.log("need to add dialog support");
8320                 /*
8321                 Roo.MessageBox.alert("Error",
8322                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8323                         action.result.errorMsg :
8324                         "Saving Failed, please check your entries or try again"
8325                 );
8326                 */
8327             }
8328
8329             this.fireEvent('actionfailed', this, action);
8330         }
8331
8332     },
8333     /**
8334      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8335      * @param {String} id The value to search for
8336      * @return Field
8337      */
8338     findField : function(id){
8339         var items = this.getItems();
8340         var field = items.get(id);
8341         if(!field){
8342              items.each(function(f){
8343                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8344                     field = f;
8345                     return false;
8346                 }
8347                 return true;
8348             });
8349         }
8350         return field || null;
8351     },
8352      /**
8353      * Mark fields in this form invalid in bulk.
8354      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8355      * @return {BasicForm} this
8356      */
8357     markInvalid : function(errors){
8358         if(errors instanceof Array){
8359             for(var i = 0, len = errors.length; i < len; i++){
8360                 var fieldError = errors[i];
8361                 var f = this.findField(fieldError.id);
8362                 if(f){
8363                     f.markInvalid(fieldError.msg);
8364                 }
8365             }
8366         }else{
8367             var field, id;
8368             for(id in errors){
8369                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8370                     field.markInvalid(errors[id]);
8371                 }
8372             }
8373         }
8374         //Roo.each(this.childForms || [], function (f) {
8375         //    f.markInvalid(errors);
8376         //});
8377
8378         return this;
8379     },
8380
8381     /**
8382      * Set values for fields in this form in bulk.
8383      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8384      * @return {BasicForm} this
8385      */
8386     setValues : function(values){
8387         if(values instanceof Array){ // array of objects
8388             for(var i = 0, len = values.length; i < len; i++){
8389                 var v = values[i];
8390                 var f = this.findField(v.id);
8391                 if(f){
8392                     f.setValue(v.value);
8393                     if(this.trackResetOnLoad){
8394                         f.originalValue = f.getValue();
8395                     }
8396                 }
8397             }
8398         }else{ // object hash
8399             var field, id;
8400             for(id in values){
8401                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8402
8403                     if (field.setFromData &&
8404                         field.valueField &&
8405                         field.displayField &&
8406                         // combos' with local stores can
8407                         // be queried via setValue()
8408                         // to set their value..
8409                         (field.store && !field.store.isLocal)
8410                         ) {
8411                         // it's a combo
8412                         var sd = { };
8413                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8414                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8415                         field.setFromData(sd);
8416
8417                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8418                         
8419                         field.setFromData(values);
8420                         
8421                     } else {
8422                         field.setValue(values[id]);
8423                     }
8424
8425
8426                     if(this.trackResetOnLoad){
8427                         field.originalValue = field.getValue();
8428                     }
8429                 }
8430             }
8431         }
8432
8433         //Roo.each(this.childForms || [], function (f) {
8434         //    f.setValues(values);
8435         //});
8436
8437         return this;
8438     },
8439
8440     /**
8441      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8442      * they are returned as an array.
8443      * @param {Boolean} asString
8444      * @return {Object}
8445      */
8446     getValues : function(asString){
8447         //if (this.childForms) {
8448             // copy values from the child forms
8449         //    Roo.each(this.childForms, function (f) {
8450         //        this.setValues(f.getValues());
8451         //    }, this);
8452         //}
8453
8454
8455
8456         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8457         if(asString === true){
8458             return fs;
8459         }
8460         return Roo.urlDecode(fs);
8461     },
8462
8463     /**
8464      * Returns the fields in this form as an object with key/value pairs.
8465      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8466      * @return {Object}
8467      */
8468     getFieldValues : function(with_hidden)
8469     {
8470         var items = this.getItems();
8471         var ret = {};
8472         items.each(function(f){
8473             
8474             if (!f.getName()) {
8475                 return;
8476             }
8477             
8478             var v = f.getValue();
8479             
8480             if (f.inputType =='radio') {
8481                 if (typeof(ret[f.getName()]) == 'undefined') {
8482                     ret[f.getName()] = ''; // empty..
8483                 }
8484
8485                 if (!f.el.dom.checked) {
8486                     return;
8487
8488                 }
8489                 v = f.el.dom.value;
8490
8491             }
8492             
8493             if(f.xtype == 'MoneyField'){
8494                 ret[f.currencyName] = f.getCurrency();
8495             }
8496
8497             // not sure if this supported any more..
8498             if ((typeof(v) == 'object') && f.getRawValue) {
8499                 v = f.getRawValue() ; // dates..
8500             }
8501             // combo boxes where name != hiddenName...
8502             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8503                 ret[f.name] = f.getRawValue();
8504             }
8505             ret[f.getName()] = v;
8506         });
8507
8508         return ret;
8509     },
8510
8511     /**
8512      * Clears all invalid messages in this form.
8513      * @return {BasicForm} this
8514      */
8515     clearInvalid : function(){
8516         var items = this.getItems();
8517
8518         items.each(function(f){
8519            f.clearInvalid();
8520         });
8521
8522         return this;
8523     },
8524
8525     /**
8526      * Resets this form.
8527      * @return {BasicForm} this
8528      */
8529     reset : function(){
8530         var items = this.getItems();
8531         items.each(function(f){
8532             f.reset();
8533         });
8534
8535         Roo.each(this.childForms || [], function (f) {
8536             f.reset();
8537         });
8538
8539
8540         return this;
8541     },
8542     
8543     getItems : function()
8544     {
8545         var r=new Roo.util.MixedCollection(false, function(o){
8546             return o.id || (o.id = Roo.id());
8547         });
8548         var iter = function(el) {
8549             if (el.inputEl) {
8550                 r.add(el);
8551             }
8552             if (!el.items) {
8553                 return;
8554             }
8555             Roo.each(el.items,function(e) {
8556                 iter(e);
8557             });
8558         };
8559
8560         iter(this);
8561         return r;
8562     },
8563     
8564     hideFields : function(items)
8565     {
8566         Roo.each(items, function(i){
8567             
8568             var f = this.findField(i);
8569             
8570             if(!f){
8571                 return;
8572             }
8573             
8574             f.hide();
8575             
8576         }, this);
8577     },
8578     
8579     showFields : function(items)
8580     {
8581         Roo.each(items, function(i){
8582             
8583             var f = this.findField(i);
8584             
8585             if(!f){
8586                 return;
8587             }
8588             
8589             f.show();
8590             
8591         }, this);
8592     }
8593
8594 });
8595
8596 Roo.apply(Roo.bootstrap.Form, {
8597     
8598     popover : {
8599         
8600         padding : 5,
8601         
8602         isApplied : false,
8603         
8604         isMasked : false,
8605         
8606         form : false,
8607         
8608         target : false,
8609         
8610         toolTip : false,
8611         
8612         intervalID : false,
8613         
8614         maskEl : false,
8615         
8616         apply : function()
8617         {
8618             if(this.isApplied){
8619                 return;
8620             }
8621             
8622             this.maskEl = {
8623                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8624                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8625                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8626                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8627             };
8628             
8629             this.maskEl.top.enableDisplayMode("block");
8630             this.maskEl.left.enableDisplayMode("block");
8631             this.maskEl.bottom.enableDisplayMode("block");
8632             this.maskEl.right.enableDisplayMode("block");
8633             
8634             this.toolTip = new Roo.bootstrap.Tooltip({
8635                 cls : 'roo-form-error-popover',
8636                 alignment : {
8637                     'left' : ['r-l', [-2,0], 'right'],
8638                     'right' : ['l-r', [2,0], 'left'],
8639                     'bottom' : ['tl-bl', [0,2], 'top'],
8640                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8641                 }
8642             });
8643             
8644             this.toolTip.render(Roo.get(document.body));
8645
8646             this.toolTip.el.enableDisplayMode("block");
8647             
8648             Roo.get(document.body).on('click', function(){
8649                 this.unmask();
8650             }, this);
8651             
8652             Roo.get(document.body).on('touchstart', function(){
8653                 this.unmask();
8654             }, this);
8655             
8656             this.isApplied = true
8657         },
8658         
8659         mask : function(form, target)
8660         {
8661             this.form = form;
8662             
8663             this.target = target;
8664             
8665             if(!this.form.errorMask || !target.el){
8666                 return;
8667             }
8668             
8669             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8670             
8671             Roo.log(scrollable);
8672             
8673             var ot = this.target.el.calcOffsetsTo(scrollable);
8674             
8675             var scrollTo = ot[1] - this.form.maskOffset;
8676             
8677             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8678             
8679             scrollable.scrollTo('top', scrollTo);
8680             
8681             var box = this.target.el.getBox();
8682             Roo.log(box);
8683             var zIndex = Roo.bootstrap.Modal.zIndex++;
8684
8685             
8686             this.maskEl.top.setStyle('position', 'absolute');
8687             this.maskEl.top.setStyle('z-index', zIndex);
8688             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8689             this.maskEl.top.setLeft(0);
8690             this.maskEl.top.setTop(0);
8691             this.maskEl.top.show();
8692             
8693             this.maskEl.left.setStyle('position', 'absolute');
8694             this.maskEl.left.setStyle('z-index', zIndex);
8695             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8696             this.maskEl.left.setLeft(0);
8697             this.maskEl.left.setTop(box.y - this.padding);
8698             this.maskEl.left.show();
8699
8700             this.maskEl.bottom.setStyle('position', 'absolute');
8701             this.maskEl.bottom.setStyle('z-index', zIndex);
8702             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8703             this.maskEl.bottom.setLeft(0);
8704             this.maskEl.bottom.setTop(box.bottom + this.padding);
8705             this.maskEl.bottom.show();
8706
8707             this.maskEl.right.setStyle('position', 'absolute');
8708             this.maskEl.right.setStyle('z-index', zIndex);
8709             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8710             this.maskEl.right.setLeft(box.right + this.padding);
8711             this.maskEl.right.setTop(box.y - this.padding);
8712             this.maskEl.right.show();
8713
8714             this.toolTip.bindEl = this.target.el;
8715
8716             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8717
8718             var tip = this.target.blankText;
8719
8720             if(this.target.getValue() !== '' ) {
8721                 
8722                 if (this.target.invalidText.length) {
8723                     tip = this.target.invalidText;
8724                 } else if (this.target.regexText.length){
8725                     tip = this.target.regexText;
8726                 }
8727             }
8728
8729             this.toolTip.show(tip);
8730
8731             this.intervalID = window.setInterval(function() {
8732                 Roo.bootstrap.Form.popover.unmask();
8733             }, 10000);
8734
8735             window.onwheel = function(){ return false;};
8736             
8737             (function(){ this.isMasked = true; }).defer(500, this);
8738             
8739         },
8740         
8741         unmask : function()
8742         {
8743             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8744                 return;
8745             }
8746             
8747             this.maskEl.top.setStyle('position', 'absolute');
8748             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8749             this.maskEl.top.hide();
8750
8751             this.maskEl.left.setStyle('position', 'absolute');
8752             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8753             this.maskEl.left.hide();
8754
8755             this.maskEl.bottom.setStyle('position', 'absolute');
8756             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8757             this.maskEl.bottom.hide();
8758
8759             this.maskEl.right.setStyle('position', 'absolute');
8760             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8761             this.maskEl.right.hide();
8762             
8763             this.toolTip.hide();
8764             
8765             this.toolTip.el.hide();
8766             
8767             window.onwheel = function(){ return true;};
8768             
8769             if(this.intervalID){
8770                 window.clearInterval(this.intervalID);
8771                 this.intervalID = false;
8772             }
8773             
8774             this.isMasked = false;
8775             
8776         }
8777         
8778     }
8779     
8780 });
8781
8782 /*
8783  * Based on:
8784  * Ext JS Library 1.1.1
8785  * Copyright(c) 2006-2007, Ext JS, LLC.
8786  *
8787  * Originally Released Under LGPL - original licence link has changed is not relivant.
8788  *
8789  * Fork - LGPL
8790  * <script type="text/javascript">
8791  */
8792 /**
8793  * @class Roo.form.VTypes
8794  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8795  * @singleton
8796  */
8797 Roo.form.VTypes = function(){
8798     // closure these in so they are only created once.
8799     var alpha = /^[a-zA-Z_]+$/;
8800     var alphanum = /^[a-zA-Z0-9_]+$/;
8801     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8802     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8803
8804     // All these messages and functions are configurable
8805     return {
8806         /**
8807          * The function used to validate email addresses
8808          * @param {String} value The email address
8809          */
8810         'email' : function(v){
8811             return email.test(v);
8812         },
8813         /**
8814          * The error text to display when the email validation function returns false
8815          * @type String
8816          */
8817         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8818         /**
8819          * The keystroke filter mask to be applied on email input
8820          * @type RegExp
8821          */
8822         'emailMask' : /[a-z0-9_\.\-@]/i,
8823
8824         /**
8825          * The function used to validate URLs
8826          * @param {String} value The URL
8827          */
8828         'url' : function(v){
8829             return url.test(v);
8830         },
8831         /**
8832          * The error text to display when the url validation function returns false
8833          * @type String
8834          */
8835         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8836         
8837         /**
8838          * The function used to validate alpha values
8839          * @param {String} value The value
8840          */
8841         'alpha' : function(v){
8842             return alpha.test(v);
8843         },
8844         /**
8845          * The error text to display when the alpha validation function returns false
8846          * @type String
8847          */
8848         'alphaText' : 'This field should only contain letters and _',
8849         /**
8850          * The keystroke filter mask to be applied on alpha input
8851          * @type RegExp
8852          */
8853         'alphaMask' : /[a-z_]/i,
8854
8855         /**
8856          * The function used to validate alphanumeric values
8857          * @param {String} value The value
8858          */
8859         'alphanum' : function(v){
8860             return alphanum.test(v);
8861         },
8862         /**
8863          * The error text to display when the alphanumeric validation function returns false
8864          * @type String
8865          */
8866         'alphanumText' : 'This field should only contain letters, numbers and _',
8867         /**
8868          * The keystroke filter mask to be applied on alphanumeric input
8869          * @type RegExp
8870          */
8871         'alphanumMask' : /[a-z0-9_]/i
8872     };
8873 }();/*
8874  * - LGPL
8875  *
8876  * Input
8877  * 
8878  */
8879
8880 /**
8881  * @class Roo.bootstrap.Input
8882  * @extends Roo.bootstrap.Component
8883  * Bootstrap Input class
8884  * @cfg {Boolean} disabled is it disabled
8885  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8886  * @cfg {String} name name of the input
8887  * @cfg {string} fieldLabel - the label associated
8888  * @cfg {string} placeholder - placeholder to put in text.
8889  * @cfg {string}  before - input group add on before
8890  * @cfg {string} after - input group add on after
8891  * @cfg {string} size - (lg|sm) or leave empty..
8892  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8893  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8894  * @cfg {Number} md colspan out of 12 for computer-sized screens
8895  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8896  * @cfg {string} value default value of the input
8897  * @cfg {Number} labelWidth set the width of label 
8898  * @cfg {Number} labellg set the width of label (1-12)
8899  * @cfg {Number} labelmd set the width of label (1-12)
8900  * @cfg {Number} labelsm set the width of label (1-12)
8901  * @cfg {Number} labelxs set the width of label (1-12)
8902  * @cfg {String} labelAlign (top|left)
8903  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8904  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8905  * @cfg {String} indicatorpos (left|right) default left
8906  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8907  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8908
8909  * @cfg {String} align (left|center|right) Default left
8910  * @cfg {Boolean} forceFeedback (true|false) Default false
8911  * 
8912  * @constructor
8913  * Create a new Input
8914  * @param {Object} config The config object
8915  */
8916
8917 Roo.bootstrap.Input = function(config){
8918     
8919     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8920     
8921     this.addEvents({
8922         /**
8923          * @event focus
8924          * Fires when this field receives input focus.
8925          * @param {Roo.form.Field} this
8926          */
8927         focus : true,
8928         /**
8929          * @event blur
8930          * Fires when this field loses input focus.
8931          * @param {Roo.form.Field} this
8932          */
8933         blur : true,
8934         /**
8935          * @event specialkey
8936          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8937          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8938          * @param {Roo.form.Field} this
8939          * @param {Roo.EventObject} e The event object
8940          */
8941         specialkey : true,
8942         /**
8943          * @event change
8944          * Fires just before the field blurs if the field value has changed.
8945          * @param {Roo.form.Field} this
8946          * @param {Mixed} newValue The new value
8947          * @param {Mixed} oldValue The original value
8948          */
8949         change : true,
8950         /**
8951          * @event invalid
8952          * Fires after the field has been marked as invalid.
8953          * @param {Roo.form.Field} this
8954          * @param {String} msg The validation message
8955          */
8956         invalid : true,
8957         /**
8958          * @event valid
8959          * Fires after the field has been validated with no errors.
8960          * @param {Roo.form.Field} this
8961          */
8962         valid : true,
8963          /**
8964          * @event keyup
8965          * Fires after the key up
8966          * @param {Roo.form.Field} this
8967          * @param {Roo.EventObject}  e The event Object
8968          */
8969         keyup : true
8970     });
8971 };
8972
8973 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8974      /**
8975      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8976       automatic validation (defaults to "keyup").
8977      */
8978     validationEvent : "keyup",
8979      /**
8980      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8981      */
8982     validateOnBlur : true,
8983     /**
8984      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8985      */
8986     validationDelay : 250,
8987      /**
8988      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8989      */
8990     focusClass : "x-form-focus",  // not needed???
8991     
8992        
8993     /**
8994      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8995      */
8996     invalidClass : "has-warning",
8997     
8998     /**
8999      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
9000      */
9001     validClass : "has-success",
9002     
9003     /**
9004      * @cfg {Boolean} hasFeedback (true|false) default true
9005      */
9006     hasFeedback : true,
9007     
9008     /**
9009      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9010      */
9011     invalidFeedbackClass : "glyphicon-warning-sign",
9012     
9013     /**
9014      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9015      */
9016     validFeedbackClass : "glyphicon-ok",
9017     
9018     /**
9019      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9020      */
9021     selectOnFocus : false,
9022     
9023      /**
9024      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9025      */
9026     maskRe : null,
9027        /**
9028      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9029      */
9030     vtype : null,
9031     
9032       /**
9033      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9034      */
9035     disableKeyFilter : false,
9036     
9037        /**
9038      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9039      */
9040     disabled : false,
9041      /**
9042      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9043      */
9044     allowBlank : true,
9045     /**
9046      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9047      */
9048     blankText : "Please complete this mandatory field",
9049     
9050      /**
9051      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9052      */
9053     minLength : 0,
9054     /**
9055      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9056      */
9057     maxLength : Number.MAX_VALUE,
9058     /**
9059      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9060      */
9061     minLengthText : "The minimum length for this field is {0}",
9062     /**
9063      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9064      */
9065     maxLengthText : "The maximum length for this field is {0}",
9066   
9067     
9068     /**
9069      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9070      * If available, this function will be called only after the basic validators all return true, and will be passed the
9071      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9072      */
9073     validator : null,
9074     /**
9075      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9076      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9077      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9078      */
9079     regex : null,
9080     /**
9081      * @cfg {String} regexText -- Depricated - use Invalid Text
9082      */
9083     regexText : "",
9084     
9085     /**
9086      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9087      */
9088     invalidText : "",
9089     
9090     
9091     
9092     autocomplete: false,
9093     
9094     
9095     fieldLabel : '',
9096     inputType : 'text',
9097     
9098     name : false,
9099     placeholder: false,
9100     before : false,
9101     after : false,
9102     size : false,
9103     hasFocus : false,
9104     preventMark: false,
9105     isFormField : true,
9106     value : '',
9107     labelWidth : 2,
9108     labelAlign : false,
9109     readOnly : false,
9110     align : false,
9111     formatedValue : false,
9112     forceFeedback : false,
9113     
9114     indicatorpos : 'left',
9115     
9116     labellg : 0,
9117     labelmd : 0,
9118     labelsm : 0,
9119     labelxs : 0,
9120     
9121     capture : '',
9122     accept : '',
9123     
9124     parentLabelAlign : function()
9125     {
9126         var parent = this;
9127         while (parent.parent()) {
9128             parent = parent.parent();
9129             if (typeof(parent.labelAlign) !='undefined') {
9130                 return parent.labelAlign;
9131             }
9132         }
9133         return 'left';
9134         
9135     },
9136     
9137     getAutoCreate : function()
9138     {
9139         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9140         
9141         var id = Roo.id();
9142         
9143         var cfg = {};
9144         
9145         if(this.inputType != 'hidden'){
9146             cfg.cls = 'form-group' //input-group
9147         }
9148         
9149         var input =  {
9150             tag: 'input',
9151             id : id,
9152             type : this.inputType,
9153             value : this.value,
9154             cls : 'form-control',
9155             placeholder : this.placeholder || '',
9156             autocomplete : this.autocomplete || 'new-password'
9157         };
9158         
9159         if(this.capture.length){
9160             input.capture = this.capture;
9161         }
9162         
9163         if(this.accept.length){
9164             input.accept = this.accept + "/*";
9165         }
9166         
9167         if(this.align){
9168             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9169         }
9170         
9171         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9172             input.maxLength = this.maxLength;
9173         }
9174         
9175         if (this.disabled) {
9176             input.disabled=true;
9177         }
9178         
9179         if (this.readOnly) {
9180             input.readonly=true;
9181         }
9182         
9183         if (this.name) {
9184             input.name = this.name;
9185         }
9186         
9187         if (this.size) {
9188             input.cls += ' input-' + this.size;
9189         }
9190         
9191         var settings=this;
9192         ['xs','sm','md','lg'].map(function(size){
9193             if (settings[size]) {
9194                 cfg.cls += ' col-' + size + '-' + settings[size];
9195             }
9196         });
9197         
9198         var inputblock = input;
9199         
9200         var feedback = {
9201             tag: 'span',
9202             cls: 'glyphicon form-control-feedback'
9203         };
9204             
9205         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9206             
9207             inputblock = {
9208                 cls : 'has-feedback',
9209                 cn :  [
9210                     input,
9211                     feedback
9212                 ] 
9213             };  
9214         }
9215         
9216         if (this.before || this.after) {
9217             
9218             inputblock = {
9219                 cls : 'input-group',
9220                 cn :  [] 
9221             };
9222             
9223             if (this.before && typeof(this.before) == 'string') {
9224                 
9225                 inputblock.cn.push({
9226                     tag :'span',
9227                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9228                     html : this.before
9229                 });
9230             }
9231             if (this.before && typeof(this.before) == 'object') {
9232                 this.before = Roo.factory(this.before);
9233                 
9234                 inputblock.cn.push({
9235                     tag :'span',
9236                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9237                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9238                 });
9239             }
9240             
9241             inputblock.cn.push(input);
9242             
9243             if (this.after && typeof(this.after) == 'string') {
9244                 inputblock.cn.push({
9245                     tag :'span',
9246                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9247                     html : this.after
9248                 });
9249             }
9250             if (this.after && typeof(this.after) == 'object') {
9251                 this.after = Roo.factory(this.after);
9252                 
9253                 inputblock.cn.push({
9254                     tag :'span',
9255                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9256                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9257                 });
9258             }
9259             
9260             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9261                 inputblock.cls += ' has-feedback';
9262                 inputblock.cn.push(feedback);
9263             }
9264         };
9265         var indicator = {
9266             tag : 'i',
9267             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9268             tooltip : 'This field is required'
9269         };
9270         if (Roo.bootstrap.version == 4) {
9271             indicator = {
9272                 tag : 'i',
9273                 style : 'display-none'
9274             };
9275         }
9276         if (align ==='left' && this.fieldLabel.length) {
9277             
9278             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9279             
9280             cfg.cn = [
9281                 indicator,
9282                 {
9283                     tag: 'label',
9284                     'for' :  id,
9285                     cls : 'control-label col-form-label',
9286                     html : this.fieldLabel
9287
9288                 },
9289                 {
9290                     cls : "", 
9291                     cn: [
9292                         inputblock
9293                     ]
9294                 }
9295             ];
9296             
9297             var labelCfg = cfg.cn[1];
9298             var contentCfg = cfg.cn[2];
9299             
9300             if(this.indicatorpos == 'right'){
9301                 cfg.cn = [
9302                     {
9303                         tag: 'label',
9304                         'for' :  id,
9305                         cls : 'control-label col-form-label',
9306                         cn : [
9307                             {
9308                                 tag : 'span',
9309                                 html : this.fieldLabel
9310                             },
9311                             indicator
9312                         ]
9313                     },
9314                     {
9315                         cls : "",
9316                         cn: [
9317                             inputblock
9318                         ]
9319                     }
9320
9321                 ];
9322                 
9323                 labelCfg = cfg.cn[0];
9324                 contentCfg = cfg.cn[1];
9325             
9326             }
9327             
9328             if(this.labelWidth > 12){
9329                 labelCfg.style = "width: " + this.labelWidth + 'px';
9330             }
9331             
9332             if(this.labelWidth < 13 && this.labelmd == 0){
9333                 this.labelmd = this.labelWidth;
9334             }
9335             
9336             if(this.labellg > 0){
9337                 labelCfg.cls += ' col-lg-' + this.labellg;
9338                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9339             }
9340             
9341             if(this.labelmd > 0){
9342                 labelCfg.cls += ' col-md-' + this.labelmd;
9343                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9344             }
9345             
9346             if(this.labelsm > 0){
9347                 labelCfg.cls += ' col-sm-' + this.labelsm;
9348                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9349             }
9350             
9351             if(this.labelxs > 0){
9352                 labelCfg.cls += ' col-xs-' + this.labelxs;
9353                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9354             }
9355             
9356             
9357         } else if ( this.fieldLabel.length) {
9358                 
9359             cfg.cn = [
9360                 {
9361                     tag : 'i',
9362                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9363                     tooltip : 'This field is required'
9364                 },
9365                 {
9366                     tag: 'label',
9367                    //cls : 'input-group-addon',
9368                     html : this.fieldLabel
9369
9370                 },
9371
9372                inputblock
9373
9374            ];
9375            
9376            if(this.indicatorpos == 'right'){
9377                 
9378                 cfg.cn = [
9379                     {
9380                         tag: 'label',
9381                        //cls : 'input-group-addon',
9382                         html : this.fieldLabel
9383
9384                     },
9385                     {
9386                         tag : 'i',
9387                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9388                         tooltip : 'This field is required'
9389                     },
9390
9391                    inputblock
9392
9393                ];
9394
9395             }
9396
9397         } else {
9398             
9399             cfg.cn = [
9400
9401                     inputblock
9402
9403             ];
9404                 
9405                 
9406         };
9407         
9408         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9409            cfg.cls += ' navbar-form';
9410         }
9411         
9412         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9413             // on BS4 we do this only if not form 
9414             cfg.cls += ' navbar-form';
9415             cfg.tag = 'li';
9416         }
9417         
9418         return cfg;
9419         
9420     },
9421     /**
9422      * return the real input element.
9423      */
9424     inputEl: function ()
9425     {
9426         return this.el.select('input.form-control',true).first();
9427     },
9428     
9429     tooltipEl : function()
9430     {
9431         return this.inputEl();
9432     },
9433     
9434     indicatorEl : function()
9435     {
9436         if (Roo.bootstrap.version == 4) {
9437             return false; // not enabled in v4 yet.
9438         }
9439         
9440         var indicator = this.el.select('i.roo-required-indicator',true).first();
9441         
9442         if(!indicator){
9443             return false;
9444         }
9445         
9446         return indicator;
9447         
9448     },
9449     
9450     setDisabled : function(v)
9451     {
9452         var i  = this.inputEl().dom;
9453         if (!v) {
9454             i.removeAttribute('disabled');
9455             return;
9456             
9457         }
9458         i.setAttribute('disabled','true');
9459     },
9460     initEvents : function()
9461     {
9462           
9463         this.inputEl().on("keydown" , this.fireKey,  this);
9464         this.inputEl().on("focus", this.onFocus,  this);
9465         this.inputEl().on("blur", this.onBlur,  this);
9466         
9467         this.inputEl().relayEvent('keyup', this);
9468         
9469         this.indicator = this.indicatorEl();
9470         
9471         if(this.indicator){
9472             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9473         }
9474  
9475         // reference to original value for reset
9476         this.originalValue = this.getValue();
9477         //Roo.form.TextField.superclass.initEvents.call(this);
9478         if(this.validationEvent == 'keyup'){
9479             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9480             this.inputEl().on('keyup', this.filterValidation, this);
9481         }
9482         else if(this.validationEvent !== false){
9483             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9484         }
9485         
9486         if(this.selectOnFocus){
9487             this.on("focus", this.preFocus, this);
9488             
9489         }
9490         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9491             this.inputEl().on("keypress", this.filterKeys, this);
9492         } else {
9493             this.inputEl().relayEvent('keypress', this);
9494         }
9495        /* if(this.grow){
9496             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9497             this.el.on("click", this.autoSize,  this);
9498         }
9499         */
9500         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9501             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9502         }
9503         
9504         if (typeof(this.before) == 'object') {
9505             this.before.render(this.el.select('.roo-input-before',true).first());
9506         }
9507         if (typeof(this.after) == 'object') {
9508             this.after.render(this.el.select('.roo-input-after',true).first());
9509         }
9510         
9511         this.inputEl().on('change', this.onChange, this);
9512         
9513     },
9514     filterValidation : function(e){
9515         if(!e.isNavKeyPress()){
9516             this.validationTask.delay(this.validationDelay);
9517         }
9518     },
9519      /**
9520      * Validates the field value
9521      * @return {Boolean} True if the value is valid, else false
9522      */
9523     validate : function(){
9524         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9525         if(this.disabled || this.validateValue(this.getRawValue())){
9526             this.markValid();
9527             return true;
9528         }
9529         
9530         this.markInvalid();
9531         return false;
9532     },
9533     
9534     
9535     /**
9536      * Validates a value according to the field's validation rules and marks the field as invalid
9537      * if the validation fails
9538      * @param {Mixed} value The value to validate
9539      * @return {Boolean} True if the value is valid, else false
9540      */
9541     validateValue : function(value)
9542     {
9543         if(this.getVisibilityEl().hasClass('hidden')){
9544             return true;
9545         }
9546         
9547         if(value.length < 1)  { // if it's blank
9548             if(this.allowBlank){
9549                 return true;
9550             }
9551             return false;
9552         }
9553         
9554         if(value.length < this.minLength){
9555             return false;
9556         }
9557         if(value.length > this.maxLength){
9558             return false;
9559         }
9560         if(this.vtype){
9561             var vt = Roo.form.VTypes;
9562             if(!vt[this.vtype](value, this)){
9563                 return false;
9564             }
9565         }
9566         if(typeof this.validator == "function"){
9567             var msg = this.validator(value);
9568             if(msg !== true){
9569                 return false;
9570             }
9571             if (typeof(msg) == 'string') {
9572                 this.invalidText = msg;
9573             }
9574         }
9575         
9576         if(this.regex && !this.regex.test(value)){
9577             return false;
9578         }
9579         
9580         return true;
9581     },
9582     
9583      // private
9584     fireKey : function(e){
9585         //Roo.log('field ' + e.getKey());
9586         if(e.isNavKeyPress()){
9587             this.fireEvent("specialkey", this, e);
9588         }
9589     },
9590     focus : function (selectText){
9591         if(this.rendered){
9592             this.inputEl().focus();
9593             if(selectText === true){
9594                 this.inputEl().dom.select();
9595             }
9596         }
9597         return this;
9598     } ,
9599     
9600     onFocus : function(){
9601         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9602            // this.el.addClass(this.focusClass);
9603         }
9604         if(!this.hasFocus){
9605             this.hasFocus = true;
9606             this.startValue = this.getValue();
9607             this.fireEvent("focus", this);
9608         }
9609     },
9610     
9611     beforeBlur : Roo.emptyFn,
9612
9613     
9614     // private
9615     onBlur : function(){
9616         this.beforeBlur();
9617         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9618             //this.el.removeClass(this.focusClass);
9619         }
9620         this.hasFocus = false;
9621         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9622             this.validate();
9623         }
9624         var v = this.getValue();
9625         if(String(v) !== String(this.startValue)){
9626             this.fireEvent('change', this, v, this.startValue);
9627         }
9628         this.fireEvent("blur", this);
9629     },
9630     
9631     onChange : function(e)
9632     {
9633         var v = this.getValue();
9634         if(String(v) !== String(this.startValue)){
9635             this.fireEvent('change', this, v, this.startValue);
9636         }
9637         
9638     },
9639     
9640     /**
9641      * Resets the current field value to the originally loaded value and clears any validation messages
9642      */
9643     reset : function(){
9644         this.setValue(this.originalValue);
9645         this.validate();
9646     },
9647      /**
9648      * Returns the name of the field
9649      * @return {Mixed} name The name field
9650      */
9651     getName: function(){
9652         return this.name;
9653     },
9654      /**
9655      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9656      * @return {Mixed} value The field value
9657      */
9658     getValue : function(){
9659         
9660         var v = this.inputEl().getValue();
9661         
9662         return v;
9663     },
9664     /**
9665      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9666      * @return {Mixed} value The field value
9667      */
9668     getRawValue : function(){
9669         var v = this.inputEl().getValue();
9670         
9671         return v;
9672     },
9673     
9674     /**
9675      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9676      * @param {Mixed} value The value to set
9677      */
9678     setRawValue : function(v){
9679         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9680     },
9681     
9682     selectText : function(start, end){
9683         var v = this.getRawValue();
9684         if(v.length > 0){
9685             start = start === undefined ? 0 : start;
9686             end = end === undefined ? v.length : end;
9687             var d = this.inputEl().dom;
9688             if(d.setSelectionRange){
9689                 d.setSelectionRange(start, end);
9690             }else if(d.createTextRange){
9691                 var range = d.createTextRange();
9692                 range.moveStart("character", start);
9693                 range.moveEnd("character", v.length-end);
9694                 range.select();
9695             }
9696         }
9697     },
9698     
9699     /**
9700      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9701      * @param {Mixed} value The value to set
9702      */
9703     setValue : function(v){
9704         this.value = v;
9705         if(this.rendered){
9706             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9707             this.validate();
9708         }
9709     },
9710     
9711     /*
9712     processValue : function(value){
9713         if(this.stripCharsRe){
9714             var newValue = value.replace(this.stripCharsRe, '');
9715             if(newValue !== value){
9716                 this.setRawValue(newValue);
9717                 return newValue;
9718             }
9719         }
9720         return value;
9721     },
9722   */
9723     preFocus : function(){
9724         
9725         if(this.selectOnFocus){
9726             this.inputEl().dom.select();
9727         }
9728     },
9729     filterKeys : function(e){
9730         var k = e.getKey();
9731         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9732             return;
9733         }
9734         var c = e.getCharCode(), cc = String.fromCharCode(c);
9735         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9736             return;
9737         }
9738         if(!this.maskRe.test(cc)){
9739             e.stopEvent();
9740         }
9741     },
9742      /**
9743      * Clear any invalid styles/messages for this field
9744      */
9745     clearInvalid : function(){
9746         
9747         if(!this.el || this.preventMark){ // not rendered
9748             return;
9749         }
9750         
9751      
9752         this.el.removeClass(this.invalidClass);
9753         
9754         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9755             
9756             var feedback = this.el.select('.form-control-feedback', true).first();
9757             
9758             if(feedback){
9759                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9760             }
9761             
9762         }
9763         
9764         if(this.indicator){
9765             this.indicator.removeClass('visible');
9766             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9767         }
9768         
9769         this.fireEvent('valid', this);
9770     },
9771     
9772      /**
9773      * Mark this field as valid
9774      */
9775     markValid : function()
9776     {
9777         if(!this.el  || this.preventMark){ // not rendered...
9778             return;
9779         }
9780         
9781         this.el.removeClass([this.invalidClass, this.validClass]);
9782         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9783
9784         var feedback = this.el.select('.form-control-feedback', true).first();
9785             
9786         if(feedback){
9787             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9788         }
9789         
9790         if(this.indicator){
9791             this.indicator.removeClass('visible');
9792             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9793         }
9794         
9795         if(this.disabled){
9796             return;
9797         }
9798         
9799         if(this.allowBlank && !this.getRawValue().length){
9800             return;
9801         }
9802         if (Roo.bootstrap.version == 3) {
9803             this.el.addClass(this.validClass);
9804         }
9805         
9806         this.inputEl().addClass('is-valid');
9807
9808         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9809             
9810             var feedback = this.el.select('.form-control-feedback', true).first();
9811             
9812             if(feedback){
9813                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9814                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9815             }
9816             
9817         }
9818         
9819         this.fireEvent('valid', this);
9820     },
9821     
9822      /**
9823      * Mark this field as invalid
9824      * @param {String} msg The validation message
9825      */
9826     markInvalid : function(msg)
9827     {
9828         if(!this.el  || this.preventMark){ // not rendered
9829             return;
9830         }
9831         
9832         this.el.removeClass([this.invalidClass, this.validClass]);
9833         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9834         
9835         var feedback = this.el.select('.form-control-feedback', true).first();
9836             
9837         if(feedback){
9838             this.el.select('.form-control-feedback', true).first().removeClass(
9839                     [this.invalidFeedbackClass, this.validFeedbackClass]);
9840         }
9841
9842         if(this.disabled){
9843             return;
9844         }
9845         
9846         if(this.allowBlank && !this.getRawValue().length){
9847             return;
9848         }
9849         
9850         if(this.indicator){
9851             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9852             this.indicator.addClass('visible');
9853         }
9854         if (Roo.bootstrap.version == 3) {
9855             this.el.addClass(this.invalidClass);
9856         }
9857         
9858         this.inputEl().addClass('is-invalid');
9859         
9860         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9861             
9862             var feedback = this.el.select('.form-control-feedback', true).first();
9863             
9864             if(feedback){
9865                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9866                 
9867                 if(this.getValue().length || this.forceFeedback){
9868                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9869                 }
9870                 
9871             }
9872             
9873         }
9874         
9875         this.fireEvent('invalid', this, msg);
9876     },
9877     // private
9878     SafariOnKeyDown : function(event)
9879     {
9880         // this is a workaround for a password hang bug on chrome/ webkit.
9881         if (this.inputEl().dom.type != 'password') {
9882             return;
9883         }
9884         
9885         var isSelectAll = false;
9886         
9887         if(this.inputEl().dom.selectionEnd > 0){
9888             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9889         }
9890         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9891             event.preventDefault();
9892             this.setValue('');
9893             return;
9894         }
9895         
9896         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9897             
9898             event.preventDefault();
9899             // this is very hacky as keydown always get's upper case.
9900             //
9901             var cc = String.fromCharCode(event.getCharCode());
9902             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9903             
9904         }
9905     },
9906     adjustWidth : function(tag, w){
9907         tag = tag.toLowerCase();
9908         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9909             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9910                 if(tag == 'input'){
9911                     return w + 2;
9912                 }
9913                 if(tag == 'textarea'){
9914                     return w-2;
9915                 }
9916             }else if(Roo.isOpera){
9917                 if(tag == 'input'){
9918                     return w + 2;
9919                 }
9920                 if(tag == 'textarea'){
9921                     return w-2;
9922                 }
9923             }
9924         }
9925         return w;
9926     },
9927     
9928     setFieldLabel : function(v)
9929     {
9930         if(!this.rendered){
9931             return;
9932         }
9933         
9934         if(this.indicatorEl()){
9935             var ar = this.el.select('label > span',true);
9936             
9937             if (ar.elements.length) {
9938                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9939                 this.fieldLabel = v;
9940                 return;
9941             }
9942             
9943             var br = this.el.select('label',true);
9944             
9945             if(br.elements.length) {
9946                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9947                 this.fieldLabel = v;
9948                 return;
9949             }
9950             
9951             Roo.log('Cannot Found any of label > span || label in input');
9952             return;
9953         }
9954         
9955         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9956         this.fieldLabel = v;
9957         
9958         
9959     }
9960 });
9961
9962  
9963 /*
9964  * - LGPL
9965  *
9966  * Input
9967  * 
9968  */
9969
9970 /**
9971  * @class Roo.bootstrap.TextArea
9972  * @extends Roo.bootstrap.Input
9973  * Bootstrap TextArea class
9974  * @cfg {Number} cols Specifies the visible width of a text area
9975  * @cfg {Number} rows Specifies the visible number of lines in a text area
9976  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9977  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9978  * @cfg {string} html text
9979  * 
9980  * @constructor
9981  * Create a new TextArea
9982  * @param {Object} config The config object
9983  */
9984
9985 Roo.bootstrap.TextArea = function(config){
9986     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9987    
9988 };
9989
9990 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9991      
9992     cols : false,
9993     rows : 5,
9994     readOnly : false,
9995     warp : 'soft',
9996     resize : false,
9997     value: false,
9998     html: false,
9999     
10000     getAutoCreate : function(){
10001         
10002         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10003         
10004         var id = Roo.id();
10005         
10006         var cfg = {};
10007         
10008         if(this.inputType != 'hidden'){
10009             cfg.cls = 'form-group' //input-group
10010         }
10011         
10012         var input =  {
10013             tag: 'textarea',
10014             id : id,
10015             warp : this.warp,
10016             rows : this.rows,
10017             value : this.value || '',
10018             html: this.html || '',
10019             cls : 'form-control',
10020             placeholder : this.placeholder || '' 
10021             
10022         };
10023         
10024         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10025             input.maxLength = this.maxLength;
10026         }
10027         
10028         if(this.resize){
10029             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10030         }
10031         
10032         if(this.cols){
10033             input.cols = this.cols;
10034         }
10035         
10036         if (this.readOnly) {
10037             input.readonly = true;
10038         }
10039         
10040         if (this.name) {
10041             input.name = this.name;
10042         }
10043         
10044         if (this.size) {
10045             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10046         }
10047         
10048         var settings=this;
10049         ['xs','sm','md','lg'].map(function(size){
10050             if (settings[size]) {
10051                 cfg.cls += ' col-' + size + '-' + settings[size];
10052             }
10053         });
10054         
10055         var inputblock = input;
10056         
10057         if(this.hasFeedback && !this.allowBlank){
10058             
10059             var feedback = {
10060                 tag: 'span',
10061                 cls: 'glyphicon form-control-feedback'
10062             };
10063
10064             inputblock = {
10065                 cls : 'has-feedback',
10066                 cn :  [
10067                     input,
10068                     feedback
10069                 ] 
10070             };  
10071         }
10072         
10073         
10074         if (this.before || this.after) {
10075             
10076             inputblock = {
10077                 cls : 'input-group',
10078                 cn :  [] 
10079             };
10080             if (this.before) {
10081                 inputblock.cn.push({
10082                     tag :'span',
10083                     cls : 'input-group-addon',
10084                     html : this.before
10085                 });
10086             }
10087             
10088             inputblock.cn.push(input);
10089             
10090             if(this.hasFeedback && !this.allowBlank){
10091                 inputblock.cls += ' has-feedback';
10092                 inputblock.cn.push(feedback);
10093             }
10094             
10095             if (this.after) {
10096                 inputblock.cn.push({
10097                     tag :'span',
10098                     cls : 'input-group-addon',
10099                     html : this.after
10100                 });
10101             }
10102             
10103         }
10104         
10105         if (align ==='left' && this.fieldLabel.length) {
10106             cfg.cn = [
10107                 {
10108                     tag: 'label',
10109                     'for' :  id,
10110                     cls : 'control-label',
10111                     html : this.fieldLabel
10112                 },
10113                 {
10114                     cls : "",
10115                     cn: [
10116                         inputblock
10117                     ]
10118                 }
10119
10120             ];
10121             
10122             if(this.labelWidth > 12){
10123                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10124             }
10125
10126             if(this.labelWidth < 13 && this.labelmd == 0){
10127                 this.labelmd = this.labelWidth;
10128             }
10129
10130             if(this.labellg > 0){
10131                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10132                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10133             }
10134
10135             if(this.labelmd > 0){
10136                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10137                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10138             }
10139
10140             if(this.labelsm > 0){
10141                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10142                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10143             }
10144
10145             if(this.labelxs > 0){
10146                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10147                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10148             }
10149             
10150         } else if ( this.fieldLabel.length) {
10151             cfg.cn = [
10152
10153                {
10154                    tag: 'label',
10155                    //cls : 'input-group-addon',
10156                    html : this.fieldLabel
10157
10158                },
10159
10160                inputblock
10161
10162            ];
10163
10164         } else {
10165
10166             cfg.cn = [
10167
10168                 inputblock
10169
10170             ];
10171                 
10172         }
10173         
10174         if (this.disabled) {
10175             input.disabled=true;
10176         }
10177         
10178         return cfg;
10179         
10180     },
10181     /**
10182      * return the real textarea element.
10183      */
10184     inputEl: function ()
10185     {
10186         return this.el.select('textarea.form-control',true).first();
10187     },
10188     
10189     /**
10190      * Clear any invalid styles/messages for this field
10191      */
10192     clearInvalid : function()
10193     {
10194         
10195         if(!this.el || this.preventMark){ // not rendered
10196             return;
10197         }
10198         
10199         var label = this.el.select('label', true).first();
10200         var icon = this.el.select('i.fa-star', true).first();
10201         
10202         if(label && icon){
10203             icon.remove();
10204         }
10205         
10206         this.el.removeClass(this.invalidClass);
10207         
10208         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10209             
10210             var feedback = this.el.select('.form-control-feedback', true).first();
10211             
10212             if(feedback){
10213                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10214             }
10215             
10216         }
10217         
10218         this.fireEvent('valid', this);
10219     },
10220     
10221      /**
10222      * Mark this field as valid
10223      */
10224     markValid : function()
10225     {
10226         if(!this.el  || this.preventMark){ // not rendered
10227             return;
10228         }
10229         
10230         this.el.removeClass([this.invalidClass, this.validClass]);
10231         
10232         var feedback = this.el.select('.form-control-feedback', true).first();
10233             
10234         if(feedback){
10235             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10236         }
10237
10238         if(this.disabled || this.allowBlank){
10239             return;
10240         }
10241         
10242         var label = this.el.select('label', true).first();
10243         var icon = this.el.select('i.fa-star', true).first();
10244         
10245         if(label && icon){
10246             icon.remove();
10247         }
10248         
10249         this.el.addClass(this.validClass);
10250         
10251         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10252             
10253             var feedback = this.el.select('.form-control-feedback', true).first();
10254             
10255             if(feedback){
10256                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10257                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10258             }
10259             
10260         }
10261         
10262         this.fireEvent('valid', this);
10263     },
10264     
10265      /**
10266      * Mark this field as invalid
10267      * @param {String} msg The validation message
10268      */
10269     markInvalid : function(msg)
10270     {
10271         if(!this.el  || this.preventMark){ // not rendered
10272             return;
10273         }
10274         
10275         this.el.removeClass([this.invalidClass, this.validClass]);
10276         
10277         var feedback = this.el.select('.form-control-feedback', true).first();
10278             
10279         if(feedback){
10280             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10281         }
10282
10283         if(this.disabled || this.allowBlank){
10284             return;
10285         }
10286         
10287         var label = this.el.select('label', true).first();
10288         var icon = this.el.select('i.fa-star', true).first();
10289         
10290         if(!this.getValue().length && label && !icon){
10291             this.el.createChild({
10292                 tag : 'i',
10293                 cls : 'text-danger fa fa-lg fa-star',
10294                 tooltip : 'This field is required',
10295                 style : 'margin-right:5px;'
10296             }, label, true);
10297         }
10298
10299         this.el.addClass(this.invalidClass);
10300         
10301         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10302             
10303             var feedback = this.el.select('.form-control-feedback', true).first();
10304             
10305             if(feedback){
10306                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10307                 
10308                 if(this.getValue().length || this.forceFeedback){
10309                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10310                 }
10311                 
10312             }
10313             
10314         }
10315         
10316         this.fireEvent('invalid', this, msg);
10317     }
10318 });
10319
10320  
10321 /*
10322  * - LGPL
10323  *
10324  * trigger field - base class for combo..
10325  * 
10326  */
10327  
10328 /**
10329  * @class Roo.bootstrap.TriggerField
10330  * @extends Roo.bootstrap.Input
10331  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10332  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10333  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10334  * for which you can provide a custom implementation.  For example:
10335  * <pre><code>
10336 var trigger = new Roo.bootstrap.TriggerField();
10337 trigger.onTriggerClick = myTriggerFn;
10338 trigger.applyTo('my-field');
10339 </code></pre>
10340  *
10341  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10342  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10343  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10344  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10345  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10346
10347  * @constructor
10348  * Create a new TriggerField.
10349  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10350  * to the base TextField)
10351  */
10352 Roo.bootstrap.TriggerField = function(config){
10353     this.mimicing = false;
10354     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10355 };
10356
10357 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10358     /**
10359      * @cfg {String} triggerClass A CSS class to apply to the trigger
10360      */
10361      /**
10362      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10363      */
10364     hideTrigger:false,
10365
10366     /**
10367      * @cfg {Boolean} removable (true|false) special filter default false
10368      */
10369     removable : false,
10370     
10371     /** @cfg {Boolean} grow @hide */
10372     /** @cfg {Number} growMin @hide */
10373     /** @cfg {Number} growMax @hide */
10374
10375     /**
10376      * @hide 
10377      * @method
10378      */
10379     autoSize: Roo.emptyFn,
10380     // private
10381     monitorTab : true,
10382     // private
10383     deferHeight : true,
10384
10385     
10386     actionMode : 'wrap',
10387     
10388     caret : false,
10389     
10390     
10391     getAutoCreate : function(){
10392        
10393         var align = this.labelAlign || this.parentLabelAlign();
10394         
10395         var id = Roo.id();
10396         
10397         var cfg = {
10398             cls: 'form-group' //input-group
10399         };
10400         
10401         
10402         var input =  {
10403             tag: 'input',
10404             id : id,
10405             type : this.inputType,
10406             cls : 'form-control',
10407             autocomplete: 'new-password',
10408             placeholder : this.placeholder || '' 
10409             
10410         };
10411         if (this.name) {
10412             input.name = this.name;
10413         }
10414         if (this.size) {
10415             input.cls += ' input-' + this.size;
10416         }
10417         
10418         if (this.disabled) {
10419             input.disabled=true;
10420         }
10421         
10422         var inputblock = input;
10423         
10424         if(this.hasFeedback && !this.allowBlank){
10425             
10426             var feedback = {
10427                 tag: 'span',
10428                 cls: 'glyphicon form-control-feedback'
10429             };
10430             
10431             if(this.removable && !this.editable && !this.tickable){
10432                 inputblock = {
10433                     cls : 'has-feedback',
10434                     cn :  [
10435                         inputblock,
10436                         {
10437                             tag: 'button',
10438                             html : 'x',
10439                             cls : 'roo-combo-removable-btn close'
10440                         },
10441                         feedback
10442                     ] 
10443                 };
10444             } else {
10445                 inputblock = {
10446                     cls : 'has-feedback',
10447                     cn :  [
10448                         inputblock,
10449                         feedback
10450                     ] 
10451                 };
10452             }
10453
10454         } else {
10455             if(this.removable && !this.editable && !this.tickable){
10456                 inputblock = {
10457                     cls : 'roo-removable',
10458                     cn :  [
10459                         inputblock,
10460                         {
10461                             tag: 'button',
10462                             html : 'x',
10463                             cls : 'roo-combo-removable-btn close'
10464                         }
10465                     ] 
10466                 };
10467             }
10468         }
10469         
10470         if (this.before || this.after) {
10471             
10472             inputblock = {
10473                 cls : 'input-group',
10474                 cn :  [] 
10475             };
10476             if (this.before) {
10477                 inputblock.cn.push({
10478                     tag :'span',
10479                     cls : 'input-group-addon input-group-prepend input-group-text',
10480                     html : this.before
10481                 });
10482             }
10483             
10484             inputblock.cn.push(input);
10485             
10486             if(this.hasFeedback && !this.allowBlank){
10487                 inputblock.cls += ' has-feedback';
10488                 inputblock.cn.push(feedback);
10489             }
10490             
10491             if (this.after) {
10492                 inputblock.cn.push({
10493                     tag :'span',
10494                     cls : 'input-group-addon input-group-append input-group-text',
10495                     html : this.after
10496                 });
10497             }
10498             
10499         };
10500         
10501       
10502         
10503         var ibwrap = inputblock;
10504         
10505         if(this.multiple){
10506             ibwrap = {
10507                 tag: 'ul',
10508                 cls: 'roo-select2-choices',
10509                 cn:[
10510                     {
10511                         tag: 'li',
10512                         cls: 'roo-select2-search-field',
10513                         cn: [
10514
10515                             inputblock
10516                         ]
10517                     }
10518                 ]
10519             };
10520                 
10521         }
10522         
10523         var combobox = {
10524             cls: 'roo-select2-container input-group',
10525             cn: [
10526                  {
10527                     tag: 'input',
10528                     type : 'hidden',
10529                     cls: 'form-hidden-field'
10530                 },
10531                 ibwrap
10532             ]
10533         };
10534         
10535         if(!this.multiple && this.showToggleBtn){
10536             
10537             var caret = {
10538                         tag: 'span',
10539                         cls: 'caret'
10540              };
10541             if (this.caret != false) {
10542                 caret = {
10543                      tag: 'i',
10544                      cls: 'fa fa-' + this.caret
10545                 };
10546                 
10547             }
10548             
10549             combobox.cn.push({
10550                 tag :'span',
10551                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10552                 cn : [
10553                     caret,
10554                     {
10555                         tag: 'span',
10556                         cls: 'combobox-clear',
10557                         cn  : [
10558                             {
10559                                 tag : 'i',
10560                                 cls: 'icon-remove'
10561                             }
10562                         ]
10563                     }
10564                 ]
10565
10566             })
10567         }
10568         
10569         if(this.multiple){
10570             combobox.cls += ' roo-select2-container-multi';
10571         }
10572          var indicator = {
10573             tag : 'i',
10574             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10575             tooltip : 'This field is required'
10576         };
10577         if (Roo.bootstrap.version == 4) {
10578             indicator = {
10579                 tag : 'i',
10580                 style : 'display:none'
10581             };
10582         }
10583         
10584         
10585         if (align ==='left' && this.fieldLabel.length) {
10586             
10587             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10588
10589             cfg.cn = [
10590                 indicator,
10591                 {
10592                     tag: 'label',
10593                     'for' :  id,
10594                     cls : 'control-label',
10595                     html : this.fieldLabel
10596
10597                 },
10598                 {
10599                     cls : "", 
10600                     cn: [
10601                         combobox
10602                     ]
10603                 }
10604
10605             ];
10606             
10607             var labelCfg = cfg.cn[1];
10608             var contentCfg = cfg.cn[2];
10609             
10610             if(this.indicatorpos == 'right'){
10611                 cfg.cn = [
10612                     {
10613                         tag: 'label',
10614                         'for' :  id,
10615                         cls : 'control-label',
10616                         cn : [
10617                             {
10618                                 tag : 'span',
10619                                 html : this.fieldLabel
10620                             },
10621                             indicator
10622                         ]
10623                     },
10624                     {
10625                         cls : "", 
10626                         cn: [
10627                             combobox
10628                         ]
10629                     }
10630
10631                 ];
10632                 
10633                 labelCfg = cfg.cn[0];
10634                 contentCfg = cfg.cn[1];
10635             }
10636             
10637             if(this.labelWidth > 12){
10638                 labelCfg.style = "width: " + this.labelWidth + 'px';
10639             }
10640             
10641             if(this.labelWidth < 13 && this.labelmd == 0){
10642                 this.labelmd = this.labelWidth;
10643             }
10644             
10645             if(this.labellg > 0){
10646                 labelCfg.cls += ' col-lg-' + this.labellg;
10647                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10648             }
10649             
10650             if(this.labelmd > 0){
10651                 labelCfg.cls += ' col-md-' + this.labelmd;
10652                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10653             }
10654             
10655             if(this.labelsm > 0){
10656                 labelCfg.cls += ' col-sm-' + this.labelsm;
10657                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10658             }
10659             
10660             if(this.labelxs > 0){
10661                 labelCfg.cls += ' col-xs-' + this.labelxs;
10662                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10663             }
10664             
10665         } else if ( this.fieldLabel.length) {
10666 //                Roo.log(" label");
10667             cfg.cn = [
10668                 indicator,
10669                {
10670                    tag: 'label',
10671                    //cls : 'input-group-addon',
10672                    html : this.fieldLabel
10673
10674                },
10675
10676                combobox
10677
10678             ];
10679             
10680             if(this.indicatorpos == 'right'){
10681                 
10682                 cfg.cn = [
10683                     {
10684                        tag: 'label',
10685                        cn : [
10686                            {
10687                                tag : 'span',
10688                                html : this.fieldLabel
10689                            },
10690                            indicator
10691                        ]
10692
10693                     },
10694                     combobox
10695
10696                 ];
10697
10698             }
10699
10700         } else {
10701             
10702 //                Roo.log(" no label && no align");
10703                 cfg = combobox
10704                      
10705                 
10706         }
10707         
10708         var settings=this;
10709         ['xs','sm','md','lg'].map(function(size){
10710             if (settings[size]) {
10711                 cfg.cls += ' col-' + size + '-' + settings[size];
10712             }
10713         });
10714         
10715         return cfg;
10716         
10717     },
10718     
10719     
10720     
10721     // private
10722     onResize : function(w, h){
10723 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10724 //        if(typeof w == 'number'){
10725 //            var x = w - this.trigger.getWidth();
10726 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10727 //            this.trigger.setStyle('left', x+'px');
10728 //        }
10729     },
10730
10731     // private
10732     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10733
10734     // private
10735     getResizeEl : function(){
10736         return this.inputEl();
10737     },
10738
10739     // private
10740     getPositionEl : function(){
10741         return this.inputEl();
10742     },
10743
10744     // private
10745     alignErrorIcon : function(){
10746         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10747     },
10748
10749     // private
10750     initEvents : function(){
10751         
10752         this.createList();
10753         
10754         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10755         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10756         if(!this.multiple && this.showToggleBtn){
10757             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10758             if(this.hideTrigger){
10759                 this.trigger.setDisplayed(false);
10760             }
10761             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10762         }
10763         
10764         if(this.multiple){
10765             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10766         }
10767         
10768         if(this.removable && !this.editable && !this.tickable){
10769             var close = this.closeTriggerEl();
10770             
10771             if(close){
10772                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10773                 close.on('click', this.removeBtnClick, this, close);
10774             }
10775         }
10776         
10777         //this.trigger.addClassOnOver('x-form-trigger-over');
10778         //this.trigger.addClassOnClick('x-form-trigger-click');
10779         
10780         //if(!this.width){
10781         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10782         //}
10783     },
10784     
10785     closeTriggerEl : function()
10786     {
10787         var close = this.el.select('.roo-combo-removable-btn', true).first();
10788         return close ? close : false;
10789     },
10790     
10791     removeBtnClick : function(e, h, el)
10792     {
10793         e.preventDefault();
10794         
10795         if(this.fireEvent("remove", this) !== false){
10796             this.reset();
10797             this.fireEvent("afterremove", this)
10798         }
10799     },
10800     
10801     createList : function()
10802     {
10803         this.list = Roo.get(document.body).createChild({
10804             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10805             cls: 'typeahead typeahead-long dropdown-menu',
10806             style: 'display:none'
10807         });
10808         
10809         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10810         
10811     },
10812
10813     // private
10814     initTrigger : function(){
10815        
10816     },
10817
10818     // private
10819     onDestroy : function(){
10820         if(this.trigger){
10821             this.trigger.removeAllListeners();
10822           //  this.trigger.remove();
10823         }
10824         //if(this.wrap){
10825         //    this.wrap.remove();
10826         //}
10827         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10828     },
10829
10830     // private
10831     onFocus : function(){
10832         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10833         /*
10834         if(!this.mimicing){
10835             this.wrap.addClass('x-trigger-wrap-focus');
10836             this.mimicing = true;
10837             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10838             if(this.monitorTab){
10839                 this.el.on("keydown", this.checkTab, this);
10840             }
10841         }
10842         */
10843     },
10844
10845     // private
10846     checkTab : function(e){
10847         if(e.getKey() == e.TAB){
10848             this.triggerBlur();
10849         }
10850     },
10851
10852     // private
10853     onBlur : function(){
10854         // do nothing
10855     },
10856
10857     // private
10858     mimicBlur : function(e, t){
10859         /*
10860         if(!this.wrap.contains(t) && this.validateBlur()){
10861             this.triggerBlur();
10862         }
10863         */
10864     },
10865
10866     // private
10867     triggerBlur : function(){
10868         this.mimicing = false;
10869         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10870         if(this.monitorTab){
10871             this.el.un("keydown", this.checkTab, this);
10872         }
10873         //this.wrap.removeClass('x-trigger-wrap-focus');
10874         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10875     },
10876
10877     // private
10878     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10879     validateBlur : function(e, t){
10880         return true;
10881     },
10882
10883     // private
10884     onDisable : function(){
10885         this.inputEl().dom.disabled = true;
10886         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10887         //if(this.wrap){
10888         //    this.wrap.addClass('x-item-disabled');
10889         //}
10890     },
10891
10892     // private
10893     onEnable : function(){
10894         this.inputEl().dom.disabled = false;
10895         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10896         //if(this.wrap){
10897         //    this.el.removeClass('x-item-disabled');
10898         //}
10899     },
10900
10901     // private
10902     onShow : function(){
10903         var ae = this.getActionEl();
10904         
10905         if(ae){
10906             ae.dom.style.display = '';
10907             ae.dom.style.visibility = 'visible';
10908         }
10909     },
10910
10911     // private
10912     
10913     onHide : function(){
10914         var ae = this.getActionEl();
10915         ae.dom.style.display = 'none';
10916     },
10917
10918     /**
10919      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10920      * by an implementing function.
10921      * @method
10922      * @param {EventObject} e
10923      */
10924     onTriggerClick : Roo.emptyFn
10925 });
10926  /*
10927  * Based on:
10928  * Ext JS Library 1.1.1
10929  * Copyright(c) 2006-2007, Ext JS, LLC.
10930  *
10931  * Originally Released Under LGPL - original licence link has changed is not relivant.
10932  *
10933  * Fork - LGPL
10934  * <script type="text/javascript">
10935  */
10936
10937
10938 /**
10939  * @class Roo.data.SortTypes
10940  * @singleton
10941  * Defines the default sorting (casting?) comparison functions used when sorting data.
10942  */
10943 Roo.data.SortTypes = {
10944     /**
10945      * Default sort that does nothing
10946      * @param {Mixed} s The value being converted
10947      * @return {Mixed} The comparison value
10948      */
10949     none : function(s){
10950         return s;
10951     },
10952     
10953     /**
10954      * The regular expression used to strip tags
10955      * @type {RegExp}
10956      * @property
10957      */
10958     stripTagsRE : /<\/?[^>]+>/gi,
10959     
10960     /**
10961      * Strips all HTML tags to sort on text only
10962      * @param {Mixed} s The value being converted
10963      * @return {String} The comparison value
10964      */
10965     asText : function(s){
10966         return String(s).replace(this.stripTagsRE, "");
10967     },
10968     
10969     /**
10970      * Strips all HTML tags to sort on text only - Case insensitive
10971      * @param {Mixed} s The value being converted
10972      * @return {String} The comparison value
10973      */
10974     asUCText : function(s){
10975         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10976     },
10977     
10978     /**
10979      * Case insensitive string
10980      * @param {Mixed} s The value being converted
10981      * @return {String} The comparison value
10982      */
10983     asUCString : function(s) {
10984         return String(s).toUpperCase();
10985     },
10986     
10987     /**
10988      * Date sorting
10989      * @param {Mixed} s The value being converted
10990      * @return {Number} The comparison value
10991      */
10992     asDate : function(s) {
10993         if(!s){
10994             return 0;
10995         }
10996         if(s instanceof Date){
10997             return s.getTime();
10998         }
10999         return Date.parse(String(s));
11000     },
11001     
11002     /**
11003      * Float sorting
11004      * @param {Mixed} s The value being converted
11005      * @return {Float} The comparison value
11006      */
11007     asFloat : function(s) {
11008         var val = parseFloat(String(s).replace(/,/g, ""));
11009         if(isNaN(val)) {
11010             val = 0;
11011         }
11012         return val;
11013     },
11014     
11015     /**
11016      * Integer sorting
11017      * @param {Mixed} s The value being converted
11018      * @return {Number} The comparison value
11019      */
11020     asInt : function(s) {
11021         var val = parseInt(String(s).replace(/,/g, ""));
11022         if(isNaN(val)) {
11023             val = 0;
11024         }
11025         return val;
11026     }
11027 };/*
11028  * Based on:
11029  * Ext JS Library 1.1.1
11030  * Copyright(c) 2006-2007, Ext JS, LLC.
11031  *
11032  * Originally Released Under LGPL - original licence link has changed is not relivant.
11033  *
11034  * Fork - LGPL
11035  * <script type="text/javascript">
11036  */
11037
11038 /**
11039 * @class Roo.data.Record
11040  * Instances of this class encapsulate both record <em>definition</em> information, and record
11041  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11042  * to access Records cached in an {@link Roo.data.Store} object.<br>
11043  * <p>
11044  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11045  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11046  * objects.<br>
11047  * <p>
11048  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11049  * @constructor
11050  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11051  * {@link #create}. The parameters are the same.
11052  * @param {Array} data An associative Array of data values keyed by the field name.
11053  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11054  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11055  * not specified an integer id is generated.
11056  */
11057 Roo.data.Record = function(data, id){
11058     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11059     this.data = data;
11060 };
11061
11062 /**
11063  * Generate a constructor for a specific record layout.
11064  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11065  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11066  * Each field definition object may contain the following properties: <ul>
11067  * <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,
11068  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11069  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11070  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11071  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11072  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11073  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11074  * this may be omitted.</p></li>
11075  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11076  * <ul><li>auto (Default, implies no conversion)</li>
11077  * <li>string</li>
11078  * <li>int</li>
11079  * <li>float</li>
11080  * <li>boolean</li>
11081  * <li>date</li></ul></p></li>
11082  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11083  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11084  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11085  * by the Reader into an object that will be stored in the Record. It is passed the
11086  * following parameters:<ul>
11087  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11088  * </ul></p></li>
11089  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11090  * </ul>
11091  * <br>usage:<br><pre><code>
11092 var TopicRecord = Roo.data.Record.create(
11093     {name: 'title', mapping: 'topic_title'},
11094     {name: 'author', mapping: 'username'},
11095     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11096     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11097     {name: 'lastPoster', mapping: 'user2'},
11098     {name: 'excerpt', mapping: 'post_text'}
11099 );
11100
11101 var myNewRecord = new TopicRecord({
11102     title: 'Do my job please',
11103     author: 'noobie',
11104     totalPosts: 1,
11105     lastPost: new Date(),
11106     lastPoster: 'Animal',
11107     excerpt: 'No way dude!'
11108 });
11109 myStore.add(myNewRecord);
11110 </code></pre>
11111  * @method create
11112  * @static
11113  */
11114 Roo.data.Record.create = function(o){
11115     var f = function(){
11116         f.superclass.constructor.apply(this, arguments);
11117     };
11118     Roo.extend(f, Roo.data.Record);
11119     var p = f.prototype;
11120     p.fields = new Roo.util.MixedCollection(false, function(field){
11121         return field.name;
11122     });
11123     for(var i = 0, len = o.length; i < len; i++){
11124         p.fields.add(new Roo.data.Field(o[i]));
11125     }
11126     f.getField = function(name){
11127         return p.fields.get(name);  
11128     };
11129     return f;
11130 };
11131
11132 Roo.data.Record.AUTO_ID = 1000;
11133 Roo.data.Record.EDIT = 'edit';
11134 Roo.data.Record.REJECT = 'reject';
11135 Roo.data.Record.COMMIT = 'commit';
11136
11137 Roo.data.Record.prototype = {
11138     /**
11139      * Readonly flag - true if this record has been modified.
11140      * @type Boolean
11141      */
11142     dirty : false,
11143     editing : false,
11144     error: null,
11145     modified: null,
11146
11147     // private
11148     join : function(store){
11149         this.store = store;
11150     },
11151
11152     /**
11153      * Set the named field to the specified value.
11154      * @param {String} name The name of the field to set.
11155      * @param {Object} value The value to set the field to.
11156      */
11157     set : function(name, value){
11158         if(this.data[name] == value){
11159             return;
11160         }
11161         this.dirty = true;
11162         if(!this.modified){
11163             this.modified = {};
11164         }
11165         if(typeof this.modified[name] == 'undefined'){
11166             this.modified[name] = this.data[name];
11167         }
11168         this.data[name] = value;
11169         if(!this.editing && this.store){
11170             this.store.afterEdit(this);
11171         }       
11172     },
11173
11174     /**
11175      * Get the value of the named field.
11176      * @param {String} name The name of the field to get the value of.
11177      * @return {Object} The value of the field.
11178      */
11179     get : function(name){
11180         return this.data[name]; 
11181     },
11182
11183     // private
11184     beginEdit : function(){
11185         this.editing = true;
11186         this.modified = {}; 
11187     },
11188
11189     // private
11190     cancelEdit : function(){
11191         this.editing = false;
11192         delete this.modified;
11193     },
11194
11195     // private
11196     endEdit : function(){
11197         this.editing = false;
11198         if(this.dirty && this.store){
11199             this.store.afterEdit(this);
11200         }
11201     },
11202
11203     /**
11204      * Usually called by the {@link Roo.data.Store} which owns the Record.
11205      * Rejects all changes made to the Record since either creation, or the last commit operation.
11206      * Modified fields are reverted to their original values.
11207      * <p>
11208      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11209      * of reject operations.
11210      */
11211     reject : function(){
11212         var m = this.modified;
11213         for(var n in m){
11214             if(typeof m[n] != "function"){
11215                 this.data[n] = m[n];
11216             }
11217         }
11218         this.dirty = false;
11219         delete this.modified;
11220         this.editing = false;
11221         if(this.store){
11222             this.store.afterReject(this);
11223         }
11224     },
11225
11226     /**
11227      * Usually called by the {@link Roo.data.Store} which owns the Record.
11228      * Commits all changes made to the Record since either creation, or the last commit operation.
11229      * <p>
11230      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11231      * of commit operations.
11232      */
11233     commit : function(){
11234         this.dirty = false;
11235         delete this.modified;
11236         this.editing = false;
11237         if(this.store){
11238             this.store.afterCommit(this);
11239         }
11240     },
11241
11242     // private
11243     hasError : function(){
11244         return this.error != null;
11245     },
11246
11247     // private
11248     clearError : function(){
11249         this.error = null;
11250     },
11251
11252     /**
11253      * Creates a copy of this record.
11254      * @param {String} id (optional) A new record id if you don't want to use this record's id
11255      * @return {Record}
11256      */
11257     copy : function(newId) {
11258         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11259     }
11260 };/*
11261  * Based on:
11262  * Ext JS Library 1.1.1
11263  * Copyright(c) 2006-2007, Ext JS, LLC.
11264  *
11265  * Originally Released Under LGPL - original licence link has changed is not relivant.
11266  *
11267  * Fork - LGPL
11268  * <script type="text/javascript">
11269  */
11270
11271
11272
11273 /**
11274  * @class Roo.data.Store
11275  * @extends Roo.util.Observable
11276  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11277  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11278  * <p>
11279  * 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
11280  * has no knowledge of the format of the data returned by the Proxy.<br>
11281  * <p>
11282  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11283  * instances from the data object. These records are cached and made available through accessor functions.
11284  * @constructor
11285  * Creates a new Store.
11286  * @param {Object} config A config object containing the objects needed for the Store to access data,
11287  * and read the data into Records.
11288  */
11289 Roo.data.Store = function(config){
11290     this.data = new Roo.util.MixedCollection(false);
11291     this.data.getKey = function(o){
11292         return o.id;
11293     };
11294     this.baseParams = {};
11295     // private
11296     this.paramNames = {
11297         "start" : "start",
11298         "limit" : "limit",
11299         "sort" : "sort",
11300         "dir" : "dir",
11301         "multisort" : "_multisort"
11302     };
11303
11304     if(config && config.data){
11305         this.inlineData = config.data;
11306         delete config.data;
11307     }
11308
11309     Roo.apply(this, config);
11310     
11311     if(this.reader){ // reader passed
11312         this.reader = Roo.factory(this.reader, Roo.data);
11313         this.reader.xmodule = this.xmodule || false;
11314         if(!this.recordType){
11315             this.recordType = this.reader.recordType;
11316         }
11317         if(this.reader.onMetaChange){
11318             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11319         }
11320     }
11321
11322     if(this.recordType){
11323         this.fields = this.recordType.prototype.fields;
11324     }
11325     this.modified = [];
11326
11327     this.addEvents({
11328         /**
11329          * @event datachanged
11330          * Fires when the data cache has changed, and a widget which is using this Store
11331          * as a Record cache should refresh its view.
11332          * @param {Store} this
11333          */
11334         datachanged : true,
11335         /**
11336          * @event metachange
11337          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11338          * @param {Store} this
11339          * @param {Object} meta The JSON metadata
11340          */
11341         metachange : true,
11342         /**
11343          * @event add
11344          * Fires when Records have been added to the Store
11345          * @param {Store} this
11346          * @param {Roo.data.Record[]} records The array of Records added
11347          * @param {Number} index The index at which the record(s) were added
11348          */
11349         add : true,
11350         /**
11351          * @event remove
11352          * Fires when a Record has been removed from the Store
11353          * @param {Store} this
11354          * @param {Roo.data.Record} record The Record that was removed
11355          * @param {Number} index The index at which the record was removed
11356          */
11357         remove : true,
11358         /**
11359          * @event update
11360          * Fires when a Record has been updated
11361          * @param {Store} this
11362          * @param {Roo.data.Record} record The Record that was updated
11363          * @param {String} operation The update operation being performed.  Value may be one of:
11364          * <pre><code>
11365  Roo.data.Record.EDIT
11366  Roo.data.Record.REJECT
11367  Roo.data.Record.COMMIT
11368          * </code></pre>
11369          */
11370         update : true,
11371         /**
11372          * @event clear
11373          * Fires when the data cache has been cleared.
11374          * @param {Store} this
11375          */
11376         clear : true,
11377         /**
11378          * @event beforeload
11379          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11380          * the load action will be canceled.
11381          * @param {Store} this
11382          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11383          */
11384         beforeload : true,
11385         /**
11386          * @event beforeloadadd
11387          * Fires after a new set of Records has been loaded.
11388          * @param {Store} this
11389          * @param {Roo.data.Record[]} records The Records that were loaded
11390          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11391          */
11392         beforeloadadd : true,
11393         /**
11394          * @event load
11395          * Fires after a new set of Records has been loaded, before they are added to the store.
11396          * @param {Store} this
11397          * @param {Roo.data.Record[]} records The Records that were loaded
11398          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11399          * @params {Object} return from reader
11400          */
11401         load : true,
11402         /**
11403          * @event loadexception
11404          * Fires if an exception occurs in the Proxy during loading.
11405          * Called with the signature of the Proxy's "loadexception" event.
11406          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11407          * 
11408          * @param {Proxy} 
11409          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11410          * @param {Object} load options 
11411          * @param {Object} jsonData from your request (normally this contains the Exception)
11412          */
11413         loadexception : true
11414     });
11415     
11416     if(this.proxy){
11417         this.proxy = Roo.factory(this.proxy, Roo.data);
11418         this.proxy.xmodule = this.xmodule || false;
11419         this.relayEvents(this.proxy,  ["loadexception"]);
11420     }
11421     this.sortToggle = {};
11422     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11423
11424     Roo.data.Store.superclass.constructor.call(this);
11425
11426     if(this.inlineData){
11427         this.loadData(this.inlineData);
11428         delete this.inlineData;
11429     }
11430 };
11431
11432 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11433      /**
11434     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11435     * without a remote query - used by combo/forms at present.
11436     */
11437     
11438     /**
11439     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11440     */
11441     /**
11442     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11443     */
11444     /**
11445     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11446     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11447     */
11448     /**
11449     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11450     * on any HTTP request
11451     */
11452     /**
11453     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11454     */
11455     /**
11456     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11457     */
11458     multiSort: false,
11459     /**
11460     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11461     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11462     */
11463     remoteSort : false,
11464
11465     /**
11466     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11467      * loaded or when a record is removed. (defaults to false).
11468     */
11469     pruneModifiedRecords : false,
11470
11471     // private
11472     lastOptions : null,
11473
11474     /**
11475      * Add Records to the Store and fires the add event.
11476      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11477      */
11478     add : function(records){
11479         records = [].concat(records);
11480         for(var i = 0, len = records.length; i < len; i++){
11481             records[i].join(this);
11482         }
11483         var index = this.data.length;
11484         this.data.addAll(records);
11485         this.fireEvent("add", this, records, index);
11486     },
11487
11488     /**
11489      * Remove a Record from the Store and fires the remove event.
11490      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11491      */
11492     remove : function(record){
11493         var index = this.data.indexOf(record);
11494         this.data.removeAt(index);
11495  
11496         if(this.pruneModifiedRecords){
11497             this.modified.remove(record);
11498         }
11499         this.fireEvent("remove", this, record, index);
11500     },
11501
11502     /**
11503      * Remove all Records from the Store and fires the clear event.
11504      */
11505     removeAll : function(){
11506         this.data.clear();
11507         if(this.pruneModifiedRecords){
11508             this.modified = [];
11509         }
11510         this.fireEvent("clear", this);
11511     },
11512
11513     /**
11514      * Inserts Records to the Store at the given index and fires the add event.
11515      * @param {Number} index The start index at which to insert the passed Records.
11516      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11517      */
11518     insert : function(index, records){
11519         records = [].concat(records);
11520         for(var i = 0, len = records.length; i < len; i++){
11521             this.data.insert(index, records[i]);
11522             records[i].join(this);
11523         }
11524         this.fireEvent("add", this, records, index);
11525     },
11526
11527     /**
11528      * Get the index within the cache of the passed Record.
11529      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11530      * @return {Number} The index of the passed Record. Returns -1 if not found.
11531      */
11532     indexOf : function(record){
11533         return this.data.indexOf(record);
11534     },
11535
11536     /**
11537      * Get the index within the cache of the Record with the passed id.
11538      * @param {String} id The id of the Record to find.
11539      * @return {Number} The index of the Record. Returns -1 if not found.
11540      */
11541     indexOfId : function(id){
11542         return this.data.indexOfKey(id);
11543     },
11544
11545     /**
11546      * Get the Record with the specified id.
11547      * @param {String} id The id of the Record to find.
11548      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11549      */
11550     getById : function(id){
11551         return this.data.key(id);
11552     },
11553
11554     /**
11555      * Get the Record at the specified index.
11556      * @param {Number} index The index of the Record to find.
11557      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11558      */
11559     getAt : function(index){
11560         return this.data.itemAt(index);
11561     },
11562
11563     /**
11564      * Returns a range of Records between specified indices.
11565      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11566      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11567      * @return {Roo.data.Record[]} An array of Records
11568      */
11569     getRange : function(start, end){
11570         return this.data.getRange(start, end);
11571     },
11572
11573     // private
11574     storeOptions : function(o){
11575         o = Roo.apply({}, o);
11576         delete o.callback;
11577         delete o.scope;
11578         this.lastOptions = o;
11579     },
11580
11581     /**
11582      * Loads the Record cache from the configured Proxy using the configured Reader.
11583      * <p>
11584      * If using remote paging, then the first load call must specify the <em>start</em>
11585      * and <em>limit</em> properties in the options.params property to establish the initial
11586      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11587      * <p>
11588      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11589      * and this call will return before the new data has been loaded. Perform any post-processing
11590      * in a callback function, or in a "load" event handler.</strong>
11591      * <p>
11592      * @param {Object} options An object containing properties which control loading options:<ul>
11593      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11594      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11595      * passed the following arguments:<ul>
11596      * <li>r : Roo.data.Record[]</li>
11597      * <li>options: Options object from the load call</li>
11598      * <li>success: Boolean success indicator</li></ul></li>
11599      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11600      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11601      * </ul>
11602      */
11603     load : function(options){
11604         options = options || {};
11605         if(this.fireEvent("beforeload", this, options) !== false){
11606             this.storeOptions(options);
11607             var p = Roo.apply(options.params || {}, this.baseParams);
11608             // if meta was not loaded from remote source.. try requesting it.
11609             if (!this.reader.metaFromRemote) {
11610                 p._requestMeta = 1;
11611             }
11612             if(this.sortInfo && this.remoteSort){
11613                 var pn = this.paramNames;
11614                 p[pn["sort"]] = this.sortInfo.field;
11615                 p[pn["dir"]] = this.sortInfo.direction;
11616             }
11617             if (this.multiSort) {
11618                 var pn = this.paramNames;
11619                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11620             }
11621             
11622             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11623         }
11624     },
11625
11626     /**
11627      * Reloads the Record cache from the configured Proxy using the configured Reader and
11628      * the options from the last load operation performed.
11629      * @param {Object} options (optional) An object containing properties which may override the options
11630      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11631      * the most recently used options are reused).
11632      */
11633     reload : function(options){
11634         this.load(Roo.applyIf(options||{}, this.lastOptions));
11635     },
11636
11637     // private
11638     // Called as a callback by the Reader during a load operation.
11639     loadRecords : function(o, options, success){
11640         if(!o || success === false){
11641             if(success !== false){
11642                 this.fireEvent("load", this, [], options, o);
11643             }
11644             if(options.callback){
11645                 options.callback.call(options.scope || this, [], options, false);
11646             }
11647             return;
11648         }
11649         // if data returned failure - throw an exception.
11650         if (o.success === false) {
11651             // show a message if no listener is registered.
11652             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11653                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11654             }
11655             // loadmask wil be hooked into this..
11656             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11657             return;
11658         }
11659         var r = o.records, t = o.totalRecords || r.length;
11660         
11661         this.fireEvent("beforeloadadd", this, r, options, o);
11662         
11663         if(!options || options.add !== true){
11664             if(this.pruneModifiedRecords){
11665                 this.modified = [];
11666             }
11667             for(var i = 0, len = r.length; i < len; i++){
11668                 r[i].join(this);
11669             }
11670             if(this.snapshot){
11671                 this.data = this.snapshot;
11672                 delete this.snapshot;
11673             }
11674             this.data.clear();
11675             this.data.addAll(r);
11676             this.totalLength = t;
11677             this.applySort();
11678             this.fireEvent("datachanged", this);
11679         }else{
11680             this.totalLength = Math.max(t, this.data.length+r.length);
11681             this.add(r);
11682         }
11683         
11684         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11685                 
11686             var e = new Roo.data.Record({});
11687
11688             e.set(this.parent.displayField, this.parent.emptyTitle);
11689             e.set(this.parent.valueField, '');
11690
11691             this.insert(0, e);
11692         }
11693             
11694         this.fireEvent("load", this, r, options, o);
11695         if(options.callback){
11696             options.callback.call(options.scope || this, r, options, true);
11697         }
11698     },
11699
11700
11701     /**
11702      * Loads data from a passed data block. A Reader which understands the format of the data
11703      * must have been configured in the constructor.
11704      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11705      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11706      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11707      */
11708     loadData : function(o, append){
11709         var r = this.reader.readRecords(o);
11710         this.loadRecords(r, {add: append}, true);
11711     },
11712
11713     /**
11714      * Gets the number of cached records.
11715      * <p>
11716      * <em>If using paging, this may not be the total size of the dataset. If the data object
11717      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11718      * the data set size</em>
11719      */
11720     getCount : function(){
11721         return this.data.length || 0;
11722     },
11723
11724     /**
11725      * Gets the total number of records in the dataset as returned by the server.
11726      * <p>
11727      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11728      * the dataset size</em>
11729      */
11730     getTotalCount : function(){
11731         return this.totalLength || 0;
11732     },
11733
11734     /**
11735      * Returns the sort state of the Store as an object with two properties:
11736      * <pre><code>
11737  field {String} The name of the field by which the Records are sorted
11738  direction {String} The sort order, "ASC" or "DESC"
11739      * </code></pre>
11740      */
11741     getSortState : function(){
11742         return this.sortInfo;
11743     },
11744
11745     // private
11746     applySort : function(){
11747         if(this.sortInfo && !this.remoteSort){
11748             var s = this.sortInfo, f = s.field;
11749             var st = this.fields.get(f).sortType;
11750             var fn = function(r1, r2){
11751                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11752                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11753             };
11754             this.data.sort(s.direction, fn);
11755             if(this.snapshot && this.snapshot != this.data){
11756                 this.snapshot.sort(s.direction, fn);
11757             }
11758         }
11759     },
11760
11761     /**
11762      * Sets the default sort column and order to be used by the next load operation.
11763      * @param {String} fieldName The name of the field to sort by.
11764      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11765      */
11766     setDefaultSort : function(field, dir){
11767         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11768     },
11769
11770     /**
11771      * Sort the Records.
11772      * If remote sorting is used, the sort is performed on the server, and the cache is
11773      * reloaded. If local sorting is used, the cache is sorted internally.
11774      * @param {String} fieldName The name of the field to sort by.
11775      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11776      */
11777     sort : function(fieldName, dir){
11778         var f = this.fields.get(fieldName);
11779         if(!dir){
11780             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11781             
11782             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11783                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11784             }else{
11785                 dir = f.sortDir;
11786             }
11787         }
11788         this.sortToggle[f.name] = dir;
11789         this.sortInfo = {field: f.name, direction: dir};
11790         if(!this.remoteSort){
11791             this.applySort();
11792             this.fireEvent("datachanged", this);
11793         }else{
11794             this.load(this.lastOptions);
11795         }
11796     },
11797
11798     /**
11799      * Calls the specified function for each of the Records in the cache.
11800      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11801      * Returning <em>false</em> aborts and exits the iteration.
11802      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11803      */
11804     each : function(fn, scope){
11805         this.data.each(fn, scope);
11806     },
11807
11808     /**
11809      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11810      * (e.g., during paging).
11811      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11812      */
11813     getModifiedRecords : function(){
11814         return this.modified;
11815     },
11816
11817     // private
11818     createFilterFn : function(property, value, anyMatch){
11819         if(!value.exec){ // not a regex
11820             value = String(value);
11821             if(value.length == 0){
11822                 return false;
11823             }
11824             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11825         }
11826         return function(r){
11827             return value.test(r.data[property]);
11828         };
11829     },
11830
11831     /**
11832      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11833      * @param {String} property A field on your records
11834      * @param {Number} start The record index to start at (defaults to 0)
11835      * @param {Number} end The last record index to include (defaults to length - 1)
11836      * @return {Number} The sum
11837      */
11838     sum : function(property, start, end){
11839         var rs = this.data.items, v = 0;
11840         start = start || 0;
11841         end = (end || end === 0) ? end : rs.length-1;
11842
11843         for(var i = start; i <= end; i++){
11844             v += (rs[i].data[property] || 0);
11845         }
11846         return v;
11847     },
11848
11849     /**
11850      * Filter the records by a specified property.
11851      * @param {String} field A field on your records
11852      * @param {String/RegExp} value Either a string that the field
11853      * should start with or a RegExp to test against the field
11854      * @param {Boolean} anyMatch True to match any part not just the beginning
11855      */
11856     filter : function(property, value, anyMatch){
11857         var fn = this.createFilterFn(property, value, anyMatch);
11858         return fn ? this.filterBy(fn) : this.clearFilter();
11859     },
11860
11861     /**
11862      * Filter by a function. The specified function will be called with each
11863      * record in this data source. If the function returns true the record is included,
11864      * otherwise it is filtered.
11865      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11866      * @param {Object} scope (optional) The scope of the function (defaults to this)
11867      */
11868     filterBy : function(fn, scope){
11869         this.snapshot = this.snapshot || this.data;
11870         this.data = this.queryBy(fn, scope||this);
11871         this.fireEvent("datachanged", this);
11872     },
11873
11874     /**
11875      * Query the records by a specified property.
11876      * @param {String} field A field on your records
11877      * @param {String/RegExp} value Either a string that the field
11878      * should start with or a RegExp to test against the field
11879      * @param {Boolean} anyMatch True to match any part not just the beginning
11880      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11881      */
11882     query : function(property, value, anyMatch){
11883         var fn = this.createFilterFn(property, value, anyMatch);
11884         return fn ? this.queryBy(fn) : this.data.clone();
11885     },
11886
11887     /**
11888      * Query by a function. The specified function will be called with each
11889      * record in this data source. If the function returns true the record is included
11890      * in the results.
11891      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11892      * @param {Object} scope (optional) The scope of the function (defaults to this)
11893       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11894      **/
11895     queryBy : function(fn, scope){
11896         var data = this.snapshot || this.data;
11897         return data.filterBy(fn, scope||this);
11898     },
11899
11900     /**
11901      * Collects unique values for a particular dataIndex from this store.
11902      * @param {String} dataIndex The property to collect
11903      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11904      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11905      * @return {Array} An array of the unique values
11906      **/
11907     collect : function(dataIndex, allowNull, bypassFilter){
11908         var d = (bypassFilter === true && this.snapshot) ?
11909                 this.snapshot.items : this.data.items;
11910         var v, sv, r = [], l = {};
11911         for(var i = 0, len = d.length; i < len; i++){
11912             v = d[i].data[dataIndex];
11913             sv = String(v);
11914             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11915                 l[sv] = true;
11916                 r[r.length] = v;
11917             }
11918         }
11919         return r;
11920     },
11921
11922     /**
11923      * Revert to a view of the Record cache with no filtering applied.
11924      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11925      */
11926     clearFilter : function(suppressEvent){
11927         if(this.snapshot && this.snapshot != this.data){
11928             this.data = this.snapshot;
11929             delete this.snapshot;
11930             if(suppressEvent !== true){
11931                 this.fireEvent("datachanged", this);
11932             }
11933         }
11934     },
11935
11936     // private
11937     afterEdit : function(record){
11938         if(this.modified.indexOf(record) == -1){
11939             this.modified.push(record);
11940         }
11941         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11942     },
11943     
11944     // private
11945     afterReject : function(record){
11946         this.modified.remove(record);
11947         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11948     },
11949
11950     // private
11951     afterCommit : function(record){
11952         this.modified.remove(record);
11953         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11954     },
11955
11956     /**
11957      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11958      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11959      */
11960     commitChanges : function(){
11961         var m = this.modified.slice(0);
11962         this.modified = [];
11963         for(var i = 0, len = m.length; i < len; i++){
11964             m[i].commit();
11965         }
11966     },
11967
11968     /**
11969      * Cancel outstanding changes on all changed records.
11970      */
11971     rejectChanges : function(){
11972         var m = this.modified.slice(0);
11973         this.modified = [];
11974         for(var i = 0, len = m.length; i < len; i++){
11975             m[i].reject();
11976         }
11977     },
11978
11979     onMetaChange : function(meta, rtype, o){
11980         this.recordType = rtype;
11981         this.fields = rtype.prototype.fields;
11982         delete this.snapshot;
11983         this.sortInfo = meta.sortInfo || this.sortInfo;
11984         this.modified = [];
11985         this.fireEvent('metachange', this, this.reader.meta);
11986     },
11987     
11988     moveIndex : function(data, type)
11989     {
11990         var index = this.indexOf(data);
11991         
11992         var newIndex = index + type;
11993         
11994         this.remove(data);
11995         
11996         this.insert(newIndex, data);
11997         
11998     }
11999 });/*
12000  * Based on:
12001  * Ext JS Library 1.1.1
12002  * Copyright(c) 2006-2007, Ext JS, LLC.
12003  *
12004  * Originally Released Under LGPL - original licence link has changed is not relivant.
12005  *
12006  * Fork - LGPL
12007  * <script type="text/javascript">
12008  */
12009
12010 /**
12011  * @class Roo.data.SimpleStore
12012  * @extends Roo.data.Store
12013  * Small helper class to make creating Stores from Array data easier.
12014  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12015  * @cfg {Array} fields An array of field definition objects, or field name strings.
12016  * @cfg {Array} data The multi-dimensional array of data
12017  * @constructor
12018  * @param {Object} config
12019  */
12020 Roo.data.SimpleStore = function(config){
12021     Roo.data.SimpleStore.superclass.constructor.call(this, {
12022         isLocal : true,
12023         reader: new Roo.data.ArrayReader({
12024                 id: config.id
12025             },
12026             Roo.data.Record.create(config.fields)
12027         ),
12028         proxy : new Roo.data.MemoryProxy(config.data)
12029     });
12030     this.load();
12031 };
12032 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12033  * Based on:
12034  * Ext JS Library 1.1.1
12035  * Copyright(c) 2006-2007, Ext JS, LLC.
12036  *
12037  * Originally Released Under LGPL - original licence link has changed is not relivant.
12038  *
12039  * Fork - LGPL
12040  * <script type="text/javascript">
12041  */
12042
12043 /**
12044 /**
12045  * @extends Roo.data.Store
12046  * @class Roo.data.JsonStore
12047  * Small helper class to make creating Stores for JSON data easier. <br/>
12048 <pre><code>
12049 var store = new Roo.data.JsonStore({
12050     url: 'get-images.php',
12051     root: 'images',
12052     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12053 });
12054 </code></pre>
12055  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12056  * JsonReader and HttpProxy (unless inline data is provided).</b>
12057  * @cfg {Array} fields An array of field definition objects, or field name strings.
12058  * @constructor
12059  * @param {Object} config
12060  */
12061 Roo.data.JsonStore = function(c){
12062     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12063         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12064         reader: new Roo.data.JsonReader(c, c.fields)
12065     }));
12066 };
12067 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12068  * Based on:
12069  * Ext JS Library 1.1.1
12070  * Copyright(c) 2006-2007, Ext JS, LLC.
12071  *
12072  * Originally Released Under LGPL - original licence link has changed is not relivant.
12073  *
12074  * Fork - LGPL
12075  * <script type="text/javascript">
12076  */
12077
12078  
12079 Roo.data.Field = function(config){
12080     if(typeof config == "string"){
12081         config = {name: config};
12082     }
12083     Roo.apply(this, config);
12084     
12085     if(!this.type){
12086         this.type = "auto";
12087     }
12088     
12089     var st = Roo.data.SortTypes;
12090     // named sortTypes are supported, here we look them up
12091     if(typeof this.sortType == "string"){
12092         this.sortType = st[this.sortType];
12093     }
12094     
12095     // set default sortType for strings and dates
12096     if(!this.sortType){
12097         switch(this.type){
12098             case "string":
12099                 this.sortType = st.asUCString;
12100                 break;
12101             case "date":
12102                 this.sortType = st.asDate;
12103                 break;
12104             default:
12105                 this.sortType = st.none;
12106         }
12107     }
12108
12109     // define once
12110     var stripRe = /[\$,%]/g;
12111
12112     // prebuilt conversion function for this field, instead of
12113     // switching every time we're reading a value
12114     if(!this.convert){
12115         var cv, dateFormat = this.dateFormat;
12116         switch(this.type){
12117             case "":
12118             case "auto":
12119             case undefined:
12120                 cv = function(v){ return v; };
12121                 break;
12122             case "string":
12123                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12124                 break;
12125             case "int":
12126                 cv = function(v){
12127                     return v !== undefined && v !== null && v !== '' ?
12128                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12129                     };
12130                 break;
12131             case "float":
12132                 cv = function(v){
12133                     return v !== undefined && v !== null && v !== '' ?
12134                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12135                     };
12136                 break;
12137             case "bool":
12138             case "boolean":
12139                 cv = function(v){ return v === true || v === "true" || v == 1; };
12140                 break;
12141             case "date":
12142                 cv = function(v){
12143                     if(!v){
12144                         return '';
12145                     }
12146                     if(v instanceof Date){
12147                         return v;
12148                     }
12149                     if(dateFormat){
12150                         if(dateFormat == "timestamp"){
12151                             return new Date(v*1000);
12152                         }
12153                         return Date.parseDate(v, dateFormat);
12154                     }
12155                     var parsed = Date.parse(v);
12156                     return parsed ? new Date(parsed) : null;
12157                 };
12158              break;
12159             
12160         }
12161         this.convert = cv;
12162     }
12163 };
12164
12165 Roo.data.Field.prototype = {
12166     dateFormat: null,
12167     defaultValue: "",
12168     mapping: null,
12169     sortType : null,
12170     sortDir : "ASC"
12171 };/*
12172  * Based on:
12173  * Ext JS Library 1.1.1
12174  * Copyright(c) 2006-2007, Ext JS, LLC.
12175  *
12176  * Originally Released Under LGPL - original licence link has changed is not relivant.
12177  *
12178  * Fork - LGPL
12179  * <script type="text/javascript">
12180  */
12181  
12182 // Base class for reading structured data from a data source.  This class is intended to be
12183 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12184
12185 /**
12186  * @class Roo.data.DataReader
12187  * Base class for reading structured data from a data source.  This class is intended to be
12188  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12189  */
12190
12191 Roo.data.DataReader = function(meta, recordType){
12192     
12193     this.meta = meta;
12194     
12195     this.recordType = recordType instanceof Array ? 
12196         Roo.data.Record.create(recordType) : recordType;
12197 };
12198
12199 Roo.data.DataReader.prototype = {
12200      /**
12201      * Create an empty record
12202      * @param {Object} data (optional) - overlay some values
12203      * @return {Roo.data.Record} record created.
12204      */
12205     newRow :  function(d) {
12206         var da =  {};
12207         this.recordType.prototype.fields.each(function(c) {
12208             switch( c.type) {
12209                 case 'int' : da[c.name] = 0; break;
12210                 case 'date' : da[c.name] = new Date(); break;
12211                 case 'float' : da[c.name] = 0.0; break;
12212                 case 'boolean' : da[c.name] = false; break;
12213                 default : da[c.name] = ""; break;
12214             }
12215             
12216         });
12217         return new this.recordType(Roo.apply(da, d));
12218     }
12219     
12220 };/*
12221  * Based on:
12222  * Ext JS Library 1.1.1
12223  * Copyright(c) 2006-2007, Ext JS, LLC.
12224  *
12225  * Originally Released Under LGPL - original licence link has changed is not relivant.
12226  *
12227  * Fork - LGPL
12228  * <script type="text/javascript">
12229  */
12230
12231 /**
12232  * @class Roo.data.DataProxy
12233  * @extends Roo.data.Observable
12234  * This class is an abstract base class for implementations which provide retrieval of
12235  * unformatted data objects.<br>
12236  * <p>
12237  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12238  * (of the appropriate type which knows how to parse the data object) to provide a block of
12239  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12240  * <p>
12241  * Custom implementations must implement the load method as described in
12242  * {@link Roo.data.HttpProxy#load}.
12243  */
12244 Roo.data.DataProxy = function(){
12245     this.addEvents({
12246         /**
12247          * @event beforeload
12248          * Fires before a network request is made to retrieve a data object.
12249          * @param {Object} This DataProxy object.
12250          * @param {Object} params The params parameter to the load function.
12251          */
12252         beforeload : true,
12253         /**
12254          * @event load
12255          * Fires before the load method's callback is called.
12256          * @param {Object} This DataProxy object.
12257          * @param {Object} o The data object.
12258          * @param {Object} arg The callback argument object passed to the load function.
12259          */
12260         load : true,
12261         /**
12262          * @event loadexception
12263          * Fires if an Exception occurs during data retrieval.
12264          * @param {Object} This DataProxy object.
12265          * @param {Object} o The data object.
12266          * @param {Object} arg The callback argument object passed to the load function.
12267          * @param {Object} e The Exception.
12268          */
12269         loadexception : true
12270     });
12271     Roo.data.DataProxy.superclass.constructor.call(this);
12272 };
12273
12274 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12275
12276     /**
12277      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12278      */
12279 /*
12280  * Based on:
12281  * Ext JS Library 1.1.1
12282  * Copyright(c) 2006-2007, Ext JS, LLC.
12283  *
12284  * Originally Released Under LGPL - original licence link has changed is not relivant.
12285  *
12286  * Fork - LGPL
12287  * <script type="text/javascript">
12288  */
12289 /**
12290  * @class Roo.data.MemoryProxy
12291  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12292  * to the Reader when its load method is called.
12293  * @constructor
12294  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12295  */
12296 Roo.data.MemoryProxy = function(data){
12297     if (data.data) {
12298         data = data.data;
12299     }
12300     Roo.data.MemoryProxy.superclass.constructor.call(this);
12301     this.data = data;
12302 };
12303
12304 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12305     
12306     /**
12307      * Load data from the requested source (in this case an in-memory
12308      * data object passed to the constructor), read the data object into
12309      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12310      * process that block using the passed callback.
12311      * @param {Object} params This parameter is not used by the MemoryProxy class.
12312      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12313      * object into a block of Roo.data.Records.
12314      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12315      * The function must be passed <ul>
12316      * <li>The Record block object</li>
12317      * <li>The "arg" argument from the load function</li>
12318      * <li>A boolean success indicator</li>
12319      * </ul>
12320      * @param {Object} scope The scope in which to call the callback
12321      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12322      */
12323     load : function(params, reader, callback, scope, arg){
12324         params = params || {};
12325         var result;
12326         try {
12327             result = reader.readRecords(this.data);
12328         }catch(e){
12329             this.fireEvent("loadexception", this, arg, null, e);
12330             callback.call(scope, null, arg, false);
12331             return;
12332         }
12333         callback.call(scope, result, arg, true);
12334     },
12335     
12336     // private
12337     update : function(params, records){
12338         
12339     }
12340 });/*
12341  * Based on:
12342  * Ext JS Library 1.1.1
12343  * Copyright(c) 2006-2007, Ext JS, LLC.
12344  *
12345  * Originally Released Under LGPL - original licence link has changed is not relivant.
12346  *
12347  * Fork - LGPL
12348  * <script type="text/javascript">
12349  */
12350 /**
12351  * @class Roo.data.HttpProxy
12352  * @extends Roo.data.DataProxy
12353  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12354  * configured to reference a certain URL.<br><br>
12355  * <p>
12356  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12357  * from which the running page was served.<br><br>
12358  * <p>
12359  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12360  * <p>
12361  * Be aware that to enable the browser to parse an XML document, the server must set
12362  * the Content-Type header in the HTTP response to "text/xml".
12363  * @constructor
12364  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12365  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12366  * will be used to make the request.
12367  */
12368 Roo.data.HttpProxy = function(conn){
12369     Roo.data.HttpProxy.superclass.constructor.call(this);
12370     // is conn a conn config or a real conn?
12371     this.conn = conn;
12372     this.useAjax = !conn || !conn.events;
12373   
12374 };
12375
12376 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12377     // thse are take from connection...
12378     
12379     /**
12380      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12381      */
12382     /**
12383      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12384      * extra parameters to each request made by this object. (defaults to undefined)
12385      */
12386     /**
12387      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12388      *  to each request made by this object. (defaults to undefined)
12389      */
12390     /**
12391      * @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)
12392      */
12393     /**
12394      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12395      */
12396      /**
12397      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12398      * @type Boolean
12399      */
12400   
12401
12402     /**
12403      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12404      * @type Boolean
12405      */
12406     /**
12407      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12408      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12409      * a finer-grained basis than the DataProxy events.
12410      */
12411     getConnection : function(){
12412         return this.useAjax ? Roo.Ajax : this.conn;
12413     },
12414
12415     /**
12416      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12417      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12418      * process that block using the passed callback.
12419      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12420      * for the request to the remote server.
12421      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12422      * object into a block of Roo.data.Records.
12423      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12424      * The function must be passed <ul>
12425      * <li>The Record block object</li>
12426      * <li>The "arg" argument from the load function</li>
12427      * <li>A boolean success indicator</li>
12428      * </ul>
12429      * @param {Object} scope The scope in which to call the callback
12430      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12431      */
12432     load : function(params, reader, callback, scope, arg){
12433         if(this.fireEvent("beforeload", this, params) !== false){
12434             var  o = {
12435                 params : params || {},
12436                 request: {
12437                     callback : callback,
12438                     scope : scope,
12439                     arg : arg
12440                 },
12441                 reader: reader,
12442                 callback : this.loadResponse,
12443                 scope: this
12444             };
12445             if(this.useAjax){
12446                 Roo.applyIf(o, this.conn);
12447                 if(this.activeRequest){
12448                     Roo.Ajax.abort(this.activeRequest);
12449                 }
12450                 this.activeRequest = Roo.Ajax.request(o);
12451             }else{
12452                 this.conn.request(o);
12453             }
12454         }else{
12455             callback.call(scope||this, null, arg, false);
12456         }
12457     },
12458
12459     // private
12460     loadResponse : function(o, success, response){
12461         delete this.activeRequest;
12462         if(!success){
12463             this.fireEvent("loadexception", this, o, response);
12464             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12465             return;
12466         }
12467         var result;
12468         try {
12469             result = o.reader.read(response);
12470         }catch(e){
12471             this.fireEvent("loadexception", this, o, response, e);
12472             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12473             return;
12474         }
12475         
12476         this.fireEvent("load", this, o, o.request.arg);
12477         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12478     },
12479
12480     // private
12481     update : function(dataSet){
12482
12483     },
12484
12485     // private
12486     updateResponse : function(dataSet){
12487
12488     }
12489 });/*
12490  * Based on:
12491  * Ext JS Library 1.1.1
12492  * Copyright(c) 2006-2007, Ext JS, LLC.
12493  *
12494  * Originally Released Under LGPL - original licence link has changed is not relivant.
12495  *
12496  * Fork - LGPL
12497  * <script type="text/javascript">
12498  */
12499
12500 /**
12501  * @class Roo.data.ScriptTagProxy
12502  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12503  * other than the originating domain of the running page.<br><br>
12504  * <p>
12505  * <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
12506  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12507  * <p>
12508  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12509  * source code that is used as the source inside a &lt;script> tag.<br><br>
12510  * <p>
12511  * In order for the browser to process the returned data, the server must wrap the data object
12512  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12513  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12514  * depending on whether the callback name was passed:
12515  * <p>
12516  * <pre><code>
12517 boolean scriptTag = false;
12518 String cb = request.getParameter("callback");
12519 if (cb != null) {
12520     scriptTag = true;
12521     response.setContentType("text/javascript");
12522 } else {
12523     response.setContentType("application/x-json");
12524 }
12525 Writer out = response.getWriter();
12526 if (scriptTag) {
12527     out.write(cb + "(");
12528 }
12529 out.print(dataBlock.toJsonString());
12530 if (scriptTag) {
12531     out.write(");");
12532 }
12533 </pre></code>
12534  *
12535  * @constructor
12536  * @param {Object} config A configuration object.
12537  */
12538 Roo.data.ScriptTagProxy = function(config){
12539     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12540     Roo.apply(this, config);
12541     this.head = document.getElementsByTagName("head")[0];
12542 };
12543
12544 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12545
12546 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12547     /**
12548      * @cfg {String} url The URL from which to request the data object.
12549      */
12550     /**
12551      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12552      */
12553     timeout : 30000,
12554     /**
12555      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12556      * the server the name of the callback function set up by the load call to process the returned data object.
12557      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12558      * javascript output which calls this named function passing the data object as its only parameter.
12559      */
12560     callbackParam : "callback",
12561     /**
12562      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12563      * name to the request.
12564      */
12565     nocache : true,
12566
12567     /**
12568      * Load data from the configured URL, read the data object into
12569      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12570      * process that block using the passed callback.
12571      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12572      * for the request to the remote server.
12573      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12574      * object into a block of Roo.data.Records.
12575      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12576      * The function must be passed <ul>
12577      * <li>The Record block object</li>
12578      * <li>The "arg" argument from the load function</li>
12579      * <li>A boolean success indicator</li>
12580      * </ul>
12581      * @param {Object} scope The scope in which to call the callback
12582      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12583      */
12584     load : function(params, reader, callback, scope, arg){
12585         if(this.fireEvent("beforeload", this, params) !== false){
12586
12587             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12588
12589             var url = this.url;
12590             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12591             if(this.nocache){
12592                 url += "&_dc=" + (new Date().getTime());
12593             }
12594             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12595             var trans = {
12596                 id : transId,
12597                 cb : "stcCallback"+transId,
12598                 scriptId : "stcScript"+transId,
12599                 params : params,
12600                 arg : arg,
12601                 url : url,
12602                 callback : callback,
12603                 scope : scope,
12604                 reader : reader
12605             };
12606             var conn = this;
12607
12608             window[trans.cb] = function(o){
12609                 conn.handleResponse(o, trans);
12610             };
12611
12612             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12613
12614             if(this.autoAbort !== false){
12615                 this.abort();
12616             }
12617
12618             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12619
12620             var script = document.createElement("script");
12621             script.setAttribute("src", url);
12622             script.setAttribute("type", "text/javascript");
12623             script.setAttribute("id", trans.scriptId);
12624             this.head.appendChild(script);
12625
12626             this.trans = trans;
12627         }else{
12628             callback.call(scope||this, null, arg, false);
12629         }
12630     },
12631
12632     // private
12633     isLoading : function(){
12634         return this.trans ? true : false;
12635     },
12636
12637     /**
12638      * Abort the current server request.
12639      */
12640     abort : function(){
12641         if(this.isLoading()){
12642             this.destroyTrans(this.trans);
12643         }
12644     },
12645
12646     // private
12647     destroyTrans : function(trans, isLoaded){
12648         this.head.removeChild(document.getElementById(trans.scriptId));
12649         clearTimeout(trans.timeoutId);
12650         if(isLoaded){
12651             window[trans.cb] = undefined;
12652             try{
12653                 delete window[trans.cb];
12654             }catch(e){}
12655         }else{
12656             // if hasn't been loaded, wait for load to remove it to prevent script error
12657             window[trans.cb] = function(){
12658                 window[trans.cb] = undefined;
12659                 try{
12660                     delete window[trans.cb];
12661                 }catch(e){}
12662             };
12663         }
12664     },
12665
12666     // private
12667     handleResponse : function(o, trans){
12668         this.trans = false;
12669         this.destroyTrans(trans, true);
12670         var result;
12671         try {
12672             result = trans.reader.readRecords(o);
12673         }catch(e){
12674             this.fireEvent("loadexception", this, o, trans.arg, e);
12675             trans.callback.call(trans.scope||window, null, trans.arg, false);
12676             return;
12677         }
12678         this.fireEvent("load", this, o, trans.arg);
12679         trans.callback.call(trans.scope||window, result, trans.arg, true);
12680     },
12681
12682     // private
12683     handleFailure : function(trans){
12684         this.trans = false;
12685         this.destroyTrans(trans, false);
12686         this.fireEvent("loadexception", this, null, trans.arg);
12687         trans.callback.call(trans.scope||window, null, trans.arg, false);
12688     }
12689 });/*
12690  * Based on:
12691  * Ext JS Library 1.1.1
12692  * Copyright(c) 2006-2007, Ext JS, LLC.
12693  *
12694  * Originally Released Under LGPL - original licence link has changed is not relivant.
12695  *
12696  * Fork - LGPL
12697  * <script type="text/javascript">
12698  */
12699
12700 /**
12701  * @class Roo.data.JsonReader
12702  * @extends Roo.data.DataReader
12703  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12704  * based on mappings in a provided Roo.data.Record constructor.
12705  * 
12706  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12707  * in the reply previously. 
12708  * 
12709  * <p>
12710  * Example code:
12711  * <pre><code>
12712 var RecordDef = Roo.data.Record.create([
12713     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12714     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12715 ]);
12716 var myReader = new Roo.data.JsonReader({
12717     totalProperty: "results",    // The property which contains the total dataset size (optional)
12718     root: "rows",                // The property which contains an Array of row objects
12719     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12720 }, RecordDef);
12721 </code></pre>
12722  * <p>
12723  * This would consume a JSON file like this:
12724  * <pre><code>
12725 { 'results': 2, 'rows': [
12726     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12727     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12728 }
12729 </code></pre>
12730  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12731  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12732  * paged from the remote server.
12733  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12734  * @cfg {String} root name of the property which contains the Array of row objects.
12735  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12736  * @cfg {Array} fields Array of field definition objects
12737  * @constructor
12738  * Create a new JsonReader
12739  * @param {Object} meta Metadata configuration options
12740  * @param {Object} recordType Either an Array of field definition objects,
12741  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12742  */
12743 Roo.data.JsonReader = function(meta, recordType){
12744     
12745     meta = meta || {};
12746     // set some defaults:
12747     Roo.applyIf(meta, {
12748         totalProperty: 'total',
12749         successProperty : 'success',
12750         root : 'data',
12751         id : 'id'
12752     });
12753     
12754     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12755 };
12756 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12757     
12758     /**
12759      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12760      * Used by Store query builder to append _requestMeta to params.
12761      * 
12762      */
12763     metaFromRemote : false,
12764     /**
12765      * This method is only used by a DataProxy which has retrieved data from a remote server.
12766      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12767      * @return {Object} data A data block which is used by an Roo.data.Store object as
12768      * a cache of Roo.data.Records.
12769      */
12770     read : function(response){
12771         var json = response.responseText;
12772        
12773         var o = /* eval:var:o */ eval("("+json+")");
12774         if(!o) {
12775             throw {message: "JsonReader.read: Json object not found"};
12776         }
12777         
12778         if(o.metaData){
12779             
12780             delete this.ef;
12781             this.metaFromRemote = true;
12782             this.meta = o.metaData;
12783             this.recordType = Roo.data.Record.create(o.metaData.fields);
12784             this.onMetaChange(this.meta, this.recordType, o);
12785         }
12786         return this.readRecords(o);
12787     },
12788
12789     // private function a store will implement
12790     onMetaChange : function(meta, recordType, o){
12791
12792     },
12793
12794     /**
12795          * @ignore
12796          */
12797     simpleAccess: function(obj, subsc) {
12798         return obj[subsc];
12799     },
12800
12801         /**
12802          * @ignore
12803          */
12804     getJsonAccessor: function(){
12805         var re = /[\[\.]/;
12806         return function(expr) {
12807             try {
12808                 return(re.test(expr))
12809                     ? new Function("obj", "return obj." + expr)
12810                     : function(obj){
12811                         return obj[expr];
12812                     };
12813             } catch(e){}
12814             return Roo.emptyFn;
12815         };
12816     }(),
12817
12818     /**
12819      * Create a data block containing Roo.data.Records from an XML document.
12820      * @param {Object} o An object which contains an Array of row objects in the property specified
12821      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12822      * which contains the total size of the dataset.
12823      * @return {Object} data A data block which is used by an Roo.data.Store object as
12824      * a cache of Roo.data.Records.
12825      */
12826     readRecords : function(o){
12827         /**
12828          * After any data loads, the raw JSON data is available for further custom processing.
12829          * @type Object
12830          */
12831         this.o = o;
12832         var s = this.meta, Record = this.recordType,
12833             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12834
12835 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12836         if (!this.ef) {
12837             if(s.totalProperty) {
12838                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12839                 }
12840                 if(s.successProperty) {
12841                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12842                 }
12843                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12844                 if (s.id) {
12845                         var g = this.getJsonAccessor(s.id);
12846                         this.getId = function(rec) {
12847                                 var r = g(rec);  
12848                                 return (r === undefined || r === "") ? null : r;
12849                         };
12850                 } else {
12851                         this.getId = function(){return null;};
12852                 }
12853             this.ef = [];
12854             for(var jj = 0; jj < fl; jj++){
12855                 f = fi[jj];
12856                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12857                 this.ef[jj] = this.getJsonAccessor(map);
12858             }
12859         }
12860
12861         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12862         if(s.totalProperty){
12863             var vt = parseInt(this.getTotal(o), 10);
12864             if(!isNaN(vt)){
12865                 totalRecords = vt;
12866             }
12867         }
12868         if(s.successProperty){
12869             var vs = this.getSuccess(o);
12870             if(vs === false || vs === 'false'){
12871                 success = false;
12872             }
12873         }
12874         var records = [];
12875         for(var i = 0; i < c; i++){
12876                 var n = root[i];
12877             var values = {};
12878             var id = this.getId(n);
12879             for(var j = 0; j < fl; j++){
12880                 f = fi[j];
12881             var v = this.ef[j](n);
12882             if (!f.convert) {
12883                 Roo.log('missing convert for ' + f.name);
12884                 Roo.log(f);
12885                 continue;
12886             }
12887             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12888             }
12889             var record = new Record(values, id);
12890             record.json = n;
12891             records[i] = record;
12892         }
12893         return {
12894             raw : o,
12895             success : success,
12896             records : records,
12897             totalRecords : totalRecords
12898         };
12899     }
12900 });/*
12901  * Based on:
12902  * Ext JS Library 1.1.1
12903  * Copyright(c) 2006-2007, Ext JS, LLC.
12904  *
12905  * Originally Released Under LGPL - original licence link has changed is not relivant.
12906  *
12907  * Fork - LGPL
12908  * <script type="text/javascript">
12909  */
12910
12911 /**
12912  * @class Roo.data.ArrayReader
12913  * @extends Roo.data.DataReader
12914  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12915  * Each element of that Array represents a row of data fields. The
12916  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12917  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12918  * <p>
12919  * Example code:.
12920  * <pre><code>
12921 var RecordDef = Roo.data.Record.create([
12922     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12923     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12924 ]);
12925 var myReader = new Roo.data.ArrayReader({
12926     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12927 }, RecordDef);
12928 </code></pre>
12929  * <p>
12930  * This would consume an Array like this:
12931  * <pre><code>
12932 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12933   </code></pre>
12934  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12935  * @constructor
12936  * Create a new JsonReader
12937  * @param {Object} meta Metadata configuration options.
12938  * @param {Object} recordType Either an Array of field definition objects
12939  * as specified to {@link Roo.data.Record#create},
12940  * or an {@link Roo.data.Record} object
12941  * created using {@link Roo.data.Record#create}.
12942  */
12943 Roo.data.ArrayReader = function(meta, recordType){
12944     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12945 };
12946
12947 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12948     /**
12949      * Create a data block containing Roo.data.Records from an XML document.
12950      * @param {Object} o An Array of row objects which represents the dataset.
12951      * @return {Object} data A data block which is used by an Roo.data.Store object as
12952      * a cache of Roo.data.Records.
12953      */
12954     readRecords : function(o){
12955         var sid = this.meta ? this.meta.id : null;
12956         var recordType = this.recordType, fields = recordType.prototype.fields;
12957         var records = [];
12958         var root = o;
12959             for(var i = 0; i < root.length; i++){
12960                     var n = root[i];
12961                 var values = {};
12962                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12963                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12964                 var f = fields.items[j];
12965                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12966                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12967                 v = f.convert(v);
12968                 values[f.name] = v;
12969             }
12970                 var record = new recordType(values, id);
12971                 record.json = n;
12972                 records[records.length] = record;
12973             }
12974             return {
12975                 records : records,
12976                 totalRecords : records.length
12977             };
12978     }
12979 });/*
12980  * - LGPL
12981  * * 
12982  */
12983
12984 /**
12985  * @class Roo.bootstrap.ComboBox
12986  * @extends Roo.bootstrap.TriggerField
12987  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12988  * @cfg {Boolean} append (true|false) default false
12989  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12990  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12991  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12992  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12993  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12994  * @cfg {Boolean} animate default true
12995  * @cfg {Boolean} emptyResultText only for touch device
12996  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12997  * @cfg {String} emptyTitle default ''
12998  * @constructor
12999  * Create a new ComboBox.
13000  * @param {Object} config Configuration options
13001  */
13002 Roo.bootstrap.ComboBox = function(config){
13003     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13004     this.addEvents({
13005         /**
13006          * @event expand
13007          * Fires when the dropdown list is expanded
13008         * @param {Roo.bootstrap.ComboBox} combo This combo box
13009         */
13010         'expand' : true,
13011         /**
13012          * @event collapse
13013          * Fires when the dropdown list is collapsed
13014         * @param {Roo.bootstrap.ComboBox} combo This combo box
13015         */
13016         'collapse' : true,
13017         /**
13018          * @event beforeselect
13019          * Fires before a list item is selected. Return false to cancel the selection.
13020         * @param {Roo.bootstrap.ComboBox} combo This combo box
13021         * @param {Roo.data.Record} record The data record returned from the underlying store
13022         * @param {Number} index The index of the selected item in the dropdown list
13023         */
13024         'beforeselect' : true,
13025         /**
13026          * @event select
13027          * Fires when a list item is selected
13028         * @param {Roo.bootstrap.ComboBox} combo This combo box
13029         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13030         * @param {Number} index The index of the selected item in the dropdown list
13031         */
13032         'select' : true,
13033         /**
13034          * @event beforequery
13035          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13036          * The event object passed has these properties:
13037         * @param {Roo.bootstrap.ComboBox} combo This combo box
13038         * @param {String} query The query
13039         * @param {Boolean} forceAll true to force "all" query
13040         * @param {Boolean} cancel true to cancel the query
13041         * @param {Object} e The query event object
13042         */
13043         'beforequery': true,
13044          /**
13045          * @event add
13046          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13047         * @param {Roo.bootstrap.ComboBox} combo This combo box
13048         */
13049         'add' : true,
13050         /**
13051          * @event edit
13052          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13053         * @param {Roo.bootstrap.ComboBox} combo This combo box
13054         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13055         */
13056         'edit' : true,
13057         /**
13058          * @event remove
13059          * Fires when the remove value from the combobox array
13060         * @param {Roo.bootstrap.ComboBox} combo This combo box
13061         */
13062         'remove' : true,
13063         /**
13064          * @event afterremove
13065          * Fires when the remove value from the combobox array
13066         * @param {Roo.bootstrap.ComboBox} combo This combo box
13067         */
13068         'afterremove' : true,
13069         /**
13070          * @event specialfilter
13071          * Fires when specialfilter
13072             * @param {Roo.bootstrap.ComboBox} combo This combo box
13073             */
13074         'specialfilter' : true,
13075         /**
13076          * @event tick
13077          * Fires when tick the element
13078             * @param {Roo.bootstrap.ComboBox} combo This combo box
13079             */
13080         'tick' : true,
13081         /**
13082          * @event touchviewdisplay
13083          * Fires when touch view require special display (default is using displayField)
13084             * @param {Roo.bootstrap.ComboBox} combo This combo box
13085             * @param {Object} cfg set html .
13086             */
13087         'touchviewdisplay' : true
13088         
13089     });
13090     
13091     this.item = [];
13092     this.tickItems = [];
13093     
13094     this.selectedIndex = -1;
13095     if(this.mode == 'local'){
13096         if(config.queryDelay === undefined){
13097             this.queryDelay = 10;
13098         }
13099         if(config.minChars === undefined){
13100             this.minChars = 0;
13101         }
13102     }
13103 };
13104
13105 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13106      
13107     /**
13108      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13109      * rendering into an Roo.Editor, defaults to false)
13110      */
13111     /**
13112      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13113      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13114      */
13115     /**
13116      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13117      */
13118     /**
13119      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13120      * the dropdown list (defaults to undefined, with no header element)
13121      */
13122
13123      /**
13124      * @cfg {String/Roo.Template} tpl The template to use to render the output
13125      */
13126      
13127      /**
13128      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13129      */
13130     listWidth: undefined,
13131     /**
13132      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13133      * mode = 'remote' or 'text' if mode = 'local')
13134      */
13135     displayField: undefined,
13136     
13137     /**
13138      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13139      * mode = 'remote' or 'value' if mode = 'local'). 
13140      * Note: use of a valueField requires the user make a selection
13141      * in order for a value to be mapped.
13142      */
13143     valueField: undefined,
13144     /**
13145      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13146      */
13147     modalTitle : '',
13148     
13149     /**
13150      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13151      * field's data value (defaults to the underlying DOM element's name)
13152      */
13153     hiddenName: undefined,
13154     /**
13155      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13156      */
13157     listClass: '',
13158     /**
13159      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13160      */
13161     selectedClass: 'active',
13162     
13163     /**
13164      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13165      */
13166     shadow:'sides',
13167     /**
13168      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13169      * anchor positions (defaults to 'tl-bl')
13170      */
13171     listAlign: 'tl-bl?',
13172     /**
13173      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13174      */
13175     maxHeight: 300,
13176     /**
13177      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13178      * query specified by the allQuery config option (defaults to 'query')
13179      */
13180     triggerAction: 'query',
13181     /**
13182      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13183      * (defaults to 4, does not apply if editable = false)
13184      */
13185     minChars : 4,
13186     /**
13187      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13188      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13189      */
13190     typeAhead: false,
13191     /**
13192      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13193      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13194      */
13195     queryDelay: 500,
13196     /**
13197      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13198      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13199      */
13200     pageSize: 0,
13201     /**
13202      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13203      * when editable = true (defaults to false)
13204      */
13205     selectOnFocus:false,
13206     /**
13207      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13208      */
13209     queryParam: 'query',
13210     /**
13211      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13212      * when mode = 'remote' (defaults to 'Loading...')
13213      */
13214     loadingText: 'Loading...',
13215     /**
13216      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13217      */
13218     resizable: false,
13219     /**
13220      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13221      */
13222     handleHeight : 8,
13223     /**
13224      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13225      * traditional select (defaults to true)
13226      */
13227     editable: true,
13228     /**
13229      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13230      */
13231     allQuery: '',
13232     /**
13233      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13234      */
13235     mode: 'remote',
13236     /**
13237      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13238      * listWidth has a higher value)
13239      */
13240     minListWidth : 70,
13241     /**
13242      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13243      * allow the user to set arbitrary text into the field (defaults to false)
13244      */
13245     forceSelection:false,
13246     /**
13247      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13248      * if typeAhead = true (defaults to 250)
13249      */
13250     typeAheadDelay : 250,
13251     /**
13252      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13253      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13254      */
13255     valueNotFoundText : undefined,
13256     /**
13257      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13258      */
13259     blockFocus : false,
13260     
13261     /**
13262      * @cfg {Boolean} disableClear Disable showing of clear button.
13263      */
13264     disableClear : false,
13265     /**
13266      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13267      */
13268     alwaysQuery : false,
13269     
13270     /**
13271      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13272      */
13273     multiple : false,
13274     
13275     /**
13276      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13277      */
13278     invalidClass : "has-warning",
13279     
13280     /**
13281      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13282      */
13283     validClass : "has-success",
13284     
13285     /**
13286      * @cfg {Boolean} specialFilter (true|false) special filter default false
13287      */
13288     specialFilter : false,
13289     
13290     /**
13291      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13292      */
13293     mobileTouchView : true,
13294     
13295     /**
13296      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13297      */
13298     useNativeIOS : false,
13299     
13300     /**
13301      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13302      */
13303     mobile_restrict_height : false,
13304     
13305     ios_options : false,
13306     
13307     //private
13308     addicon : false,
13309     editicon: false,
13310     
13311     page: 0,
13312     hasQuery: false,
13313     append: false,
13314     loadNext: false,
13315     autoFocus : true,
13316     tickable : false,
13317     btnPosition : 'right',
13318     triggerList : true,
13319     showToggleBtn : true,
13320     animate : true,
13321     emptyResultText: 'Empty',
13322     triggerText : 'Select',
13323     emptyTitle : '',
13324     
13325     // element that contains real text value.. (when hidden is used..)
13326     
13327     getAutoCreate : function()
13328     {   
13329         var cfg = false;
13330         //render
13331         /*
13332          * Render classic select for iso
13333          */
13334         
13335         if(Roo.isIOS && this.useNativeIOS){
13336             cfg = this.getAutoCreateNativeIOS();
13337             return cfg;
13338         }
13339         
13340         /*
13341          * Touch Devices
13342          */
13343         
13344         if(Roo.isTouch && this.mobileTouchView){
13345             cfg = this.getAutoCreateTouchView();
13346             return cfg;;
13347         }
13348         
13349         /*
13350          *  Normal ComboBox
13351          */
13352         if(!this.tickable){
13353             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13354             return cfg;
13355         }
13356         
13357         /*
13358          *  ComboBox with tickable selections
13359          */
13360              
13361         var align = this.labelAlign || this.parentLabelAlign();
13362         
13363         cfg = {
13364             cls : 'form-group roo-combobox-tickable' //input-group
13365         };
13366         
13367         var btn_text_select = '';
13368         var btn_text_done = '';
13369         var btn_text_cancel = '';
13370         
13371         if (this.btn_text_show) {
13372             btn_text_select = 'Select';
13373             btn_text_done = 'Done';
13374             btn_text_cancel = 'Cancel'; 
13375         }
13376         
13377         var buttons = {
13378             tag : 'div',
13379             cls : 'tickable-buttons',
13380             cn : [
13381                 {
13382                     tag : 'button',
13383                     type : 'button',
13384                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13385                     //html : this.triggerText
13386                     html: btn_text_select
13387                 },
13388                 {
13389                     tag : 'button',
13390                     type : 'button',
13391                     name : 'ok',
13392                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13393                     //html : 'Done'
13394                     html: btn_text_done
13395                 },
13396                 {
13397                     tag : 'button',
13398                     type : 'button',
13399                     name : 'cancel',
13400                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13401                     //html : 'Cancel'
13402                     html: btn_text_cancel
13403                 }
13404             ]
13405         };
13406         
13407         if(this.editable){
13408             buttons.cn.unshift({
13409                 tag: 'input',
13410                 cls: 'roo-select2-search-field-input'
13411             });
13412         }
13413         
13414         var _this = this;
13415         
13416         Roo.each(buttons.cn, function(c){
13417             if (_this.size) {
13418                 c.cls += ' btn-' + _this.size;
13419             }
13420
13421             if (_this.disabled) {
13422                 c.disabled = true;
13423             }
13424         });
13425         
13426         var box = {
13427             tag: 'div',
13428             style : 'display: contents',
13429             cn: [
13430                 {
13431                     tag: 'input',
13432                     type : 'hidden',
13433                     cls: 'form-hidden-field'
13434                 },
13435                 {
13436                     tag: 'ul',
13437                     cls: 'roo-select2-choices',
13438                     cn:[
13439                         {
13440                             tag: 'li',
13441                             cls: 'roo-select2-search-field',
13442                             cn: [
13443                                 buttons
13444                             ]
13445                         }
13446                     ]
13447                 }
13448             ]
13449         };
13450         
13451         var combobox = {
13452             cls: 'roo-select2-container input-group roo-select2-container-multi',
13453             cn: [
13454                 
13455                 box
13456 //                {
13457 //                    tag: 'ul',
13458 //                    cls: 'typeahead typeahead-long dropdown-menu',
13459 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13460 //                }
13461             ]
13462         };
13463         
13464         if(this.hasFeedback && !this.allowBlank){
13465             
13466             var feedback = {
13467                 tag: 'span',
13468                 cls: 'glyphicon form-control-feedback'
13469             };
13470
13471             combobox.cn.push(feedback);
13472         }
13473         
13474         var indicator = {
13475             tag : 'i',
13476             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13477             tooltip : 'This field is required'
13478         };
13479         if (Roo.bootstrap.version == 4) {
13480             indicator = {
13481                 tag : 'i',
13482                 style : 'display:none'
13483             };
13484         }
13485         if (align ==='left' && this.fieldLabel.length) {
13486             
13487             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13488             
13489             cfg.cn = [
13490                 indicator,
13491                 {
13492                     tag: 'label',
13493                     'for' :  id,
13494                     cls : 'control-label col-form-label',
13495                     html : this.fieldLabel
13496
13497                 },
13498                 {
13499                     cls : "", 
13500                     cn: [
13501                         combobox
13502                     ]
13503                 }
13504
13505             ];
13506             
13507             var labelCfg = cfg.cn[1];
13508             var contentCfg = cfg.cn[2];
13509             
13510
13511             if(this.indicatorpos == 'right'){
13512                 
13513                 cfg.cn = [
13514                     {
13515                         tag: 'label',
13516                         'for' :  id,
13517                         cls : 'control-label col-form-label',
13518                         cn : [
13519                             {
13520                                 tag : 'span',
13521                                 html : this.fieldLabel
13522                             },
13523                             indicator
13524                         ]
13525                     },
13526                     {
13527                         cls : "",
13528                         cn: [
13529                             combobox
13530                         ]
13531                     }
13532
13533                 ];
13534                 
13535                 
13536                 
13537                 labelCfg = cfg.cn[0];
13538                 contentCfg = cfg.cn[1];
13539             
13540             }
13541             
13542             if(this.labelWidth > 12){
13543                 labelCfg.style = "width: " + this.labelWidth + 'px';
13544             }
13545             
13546             if(this.labelWidth < 13 && this.labelmd == 0){
13547                 this.labelmd = this.labelWidth;
13548             }
13549             
13550             if(this.labellg > 0){
13551                 labelCfg.cls += ' col-lg-' + this.labellg;
13552                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13553             }
13554             
13555             if(this.labelmd > 0){
13556                 labelCfg.cls += ' col-md-' + this.labelmd;
13557                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13558             }
13559             
13560             if(this.labelsm > 0){
13561                 labelCfg.cls += ' col-sm-' + this.labelsm;
13562                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13563             }
13564             
13565             if(this.labelxs > 0){
13566                 labelCfg.cls += ' col-xs-' + this.labelxs;
13567                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13568             }
13569                 
13570                 
13571         } else if ( this.fieldLabel.length) {
13572 //                Roo.log(" label");
13573                  cfg.cn = [
13574                    indicator,
13575                     {
13576                         tag: 'label',
13577                         //cls : 'input-group-addon',
13578                         html : this.fieldLabel
13579                     },
13580                     combobox
13581                 ];
13582                 
13583                 if(this.indicatorpos == 'right'){
13584                     cfg.cn = [
13585                         {
13586                             tag: 'label',
13587                             //cls : 'input-group-addon',
13588                             html : this.fieldLabel
13589                         },
13590                         indicator,
13591                         combobox
13592                     ];
13593                     
13594                 }
13595
13596         } else {
13597             
13598 //                Roo.log(" no label && no align");
13599                 cfg = combobox
13600                      
13601                 
13602         }
13603          
13604         var settings=this;
13605         ['xs','sm','md','lg'].map(function(size){
13606             if (settings[size]) {
13607                 cfg.cls += ' col-' + size + '-' + settings[size];
13608             }
13609         });
13610         
13611         return cfg;
13612         
13613     },
13614     
13615     _initEventsCalled : false,
13616     
13617     // private
13618     initEvents: function()
13619     {   
13620         if (this._initEventsCalled) { // as we call render... prevent looping...
13621             return;
13622         }
13623         this._initEventsCalled = true;
13624         
13625         if (!this.store) {
13626             throw "can not find store for combo";
13627         }
13628         
13629         this.indicator = this.indicatorEl();
13630         
13631         this.store = Roo.factory(this.store, Roo.data);
13632         this.store.parent = this;
13633         
13634         // if we are building from html. then this element is so complex, that we can not really
13635         // use the rendered HTML.
13636         // so we have to trash and replace the previous code.
13637         if (Roo.XComponent.build_from_html) {
13638             // remove this element....
13639             var e = this.el.dom, k=0;
13640             while (e ) { e = e.previousSibling;  ++k;}
13641
13642             this.el.remove();
13643             
13644             this.el=false;
13645             this.rendered = false;
13646             
13647             this.render(this.parent().getChildContainer(true), k);
13648         }
13649         
13650         if(Roo.isIOS && this.useNativeIOS){
13651             this.initIOSView();
13652             return;
13653         }
13654         
13655         /*
13656          * Touch Devices
13657          */
13658         
13659         if(Roo.isTouch && this.mobileTouchView){
13660             this.initTouchView();
13661             return;
13662         }
13663         
13664         if(this.tickable){
13665             this.initTickableEvents();
13666             return;
13667         }
13668         
13669         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13670         
13671         if(this.hiddenName){
13672             
13673             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13674             
13675             this.hiddenField.dom.value =
13676                 this.hiddenValue !== undefined ? this.hiddenValue :
13677                 this.value !== undefined ? this.value : '';
13678
13679             // prevent input submission
13680             this.el.dom.removeAttribute('name');
13681             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13682              
13683              
13684         }
13685         //if(Roo.isGecko){
13686         //    this.el.dom.setAttribute('autocomplete', 'off');
13687         //}
13688         
13689         var cls = 'x-combo-list';
13690         
13691         //this.list = new Roo.Layer({
13692         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13693         //});
13694         
13695         var _this = this;
13696         
13697         (function(){
13698             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13699             _this.list.setWidth(lw);
13700         }).defer(100);
13701         
13702         this.list.on('mouseover', this.onViewOver, this);
13703         this.list.on('mousemove', this.onViewMove, this);
13704         this.list.on('scroll', this.onViewScroll, this);
13705         
13706         /*
13707         this.list.swallowEvent('mousewheel');
13708         this.assetHeight = 0;
13709
13710         if(this.title){
13711             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13712             this.assetHeight += this.header.getHeight();
13713         }
13714
13715         this.innerList = this.list.createChild({cls:cls+'-inner'});
13716         this.innerList.on('mouseover', this.onViewOver, this);
13717         this.innerList.on('mousemove', this.onViewMove, this);
13718         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13719         
13720         if(this.allowBlank && !this.pageSize && !this.disableClear){
13721             this.footer = this.list.createChild({cls:cls+'-ft'});
13722             this.pageTb = new Roo.Toolbar(this.footer);
13723            
13724         }
13725         if(this.pageSize){
13726             this.footer = this.list.createChild({cls:cls+'-ft'});
13727             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13728                     {pageSize: this.pageSize});
13729             
13730         }
13731         
13732         if (this.pageTb && this.allowBlank && !this.disableClear) {
13733             var _this = this;
13734             this.pageTb.add(new Roo.Toolbar.Fill(), {
13735                 cls: 'x-btn-icon x-btn-clear',
13736                 text: '&#160;',
13737                 handler: function()
13738                 {
13739                     _this.collapse();
13740                     _this.clearValue();
13741                     _this.onSelect(false, -1);
13742                 }
13743             });
13744         }
13745         if (this.footer) {
13746             this.assetHeight += this.footer.getHeight();
13747         }
13748         */
13749             
13750         if(!this.tpl){
13751             this.tpl = Roo.bootstrap.version == 4 ?
13752                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13753                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13754         }
13755
13756         this.view = new Roo.View(this.list, this.tpl, {
13757             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13758         });
13759         //this.view.wrapEl.setDisplayed(false);
13760         this.view.on('click', this.onViewClick, this);
13761         
13762         
13763         this.store.on('beforeload', this.onBeforeLoad, this);
13764         this.store.on('load', this.onLoad, this);
13765         this.store.on('loadexception', this.onLoadException, this);
13766         /*
13767         if(this.resizable){
13768             this.resizer = new Roo.Resizable(this.list,  {
13769                pinned:true, handles:'se'
13770             });
13771             this.resizer.on('resize', function(r, w, h){
13772                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13773                 this.listWidth = w;
13774                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13775                 this.restrictHeight();
13776             }, this);
13777             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13778         }
13779         */
13780         if(!this.editable){
13781             this.editable = true;
13782             this.setEditable(false);
13783         }
13784         
13785         /*
13786         
13787         if (typeof(this.events.add.listeners) != 'undefined') {
13788             
13789             this.addicon = this.wrap.createChild(
13790                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13791        
13792             this.addicon.on('click', function(e) {
13793                 this.fireEvent('add', this);
13794             }, this);
13795         }
13796         if (typeof(this.events.edit.listeners) != 'undefined') {
13797             
13798             this.editicon = this.wrap.createChild(
13799                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13800             if (this.addicon) {
13801                 this.editicon.setStyle('margin-left', '40px');
13802             }
13803             this.editicon.on('click', function(e) {
13804                 
13805                 // we fire even  if inothing is selected..
13806                 this.fireEvent('edit', this, this.lastData );
13807                 
13808             }, this);
13809         }
13810         */
13811         
13812         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13813             "up" : function(e){
13814                 this.inKeyMode = true;
13815                 this.selectPrev();
13816             },
13817
13818             "down" : function(e){
13819                 if(!this.isExpanded()){
13820                     this.onTriggerClick();
13821                 }else{
13822                     this.inKeyMode = true;
13823                     this.selectNext();
13824                 }
13825             },
13826
13827             "enter" : function(e){
13828 //                this.onViewClick();
13829                 //return true;
13830                 this.collapse();
13831                 
13832                 if(this.fireEvent("specialkey", this, e)){
13833                     this.onViewClick(false);
13834                 }
13835                 
13836                 return true;
13837             },
13838
13839             "esc" : function(e){
13840                 this.collapse();
13841             },
13842
13843             "tab" : function(e){
13844                 this.collapse();
13845                 
13846                 if(this.fireEvent("specialkey", this, e)){
13847                     this.onViewClick(false);
13848                 }
13849                 
13850                 return true;
13851             },
13852
13853             scope : this,
13854
13855             doRelay : function(foo, bar, hname){
13856                 if(hname == 'down' || this.scope.isExpanded()){
13857                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13858                 }
13859                 return true;
13860             },
13861
13862             forceKeyDown: true
13863         });
13864         
13865         
13866         this.queryDelay = Math.max(this.queryDelay || 10,
13867                 this.mode == 'local' ? 10 : 250);
13868         
13869         
13870         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13871         
13872         if(this.typeAhead){
13873             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13874         }
13875         if(this.editable !== false){
13876             this.inputEl().on("keyup", this.onKeyUp, this);
13877         }
13878         if(this.forceSelection){
13879             this.inputEl().on('blur', this.doForce, this);
13880         }
13881         
13882         if(this.multiple){
13883             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13884             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13885         }
13886     },
13887     
13888     initTickableEvents: function()
13889     {   
13890         this.createList();
13891         
13892         if(this.hiddenName){
13893             
13894             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13895             
13896             this.hiddenField.dom.value =
13897                 this.hiddenValue !== undefined ? this.hiddenValue :
13898                 this.value !== undefined ? this.value : '';
13899
13900             // prevent input submission
13901             this.el.dom.removeAttribute('name');
13902             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13903              
13904              
13905         }
13906         
13907 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13908         
13909         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13910         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13911         if(this.triggerList){
13912             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13913         }
13914          
13915         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13916         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13917         
13918         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13919         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13920         
13921         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13922         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13923         
13924         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13925         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13926         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13927         
13928         this.okBtn.hide();
13929         this.cancelBtn.hide();
13930         
13931         var _this = this;
13932         
13933         (function(){
13934             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13935             _this.list.setWidth(lw);
13936         }).defer(100);
13937         
13938         this.list.on('mouseover', this.onViewOver, this);
13939         this.list.on('mousemove', this.onViewMove, this);
13940         
13941         this.list.on('scroll', this.onViewScroll, this);
13942         
13943         if(!this.tpl){
13944             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13945                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13946         }
13947
13948         this.view = new Roo.View(this.list, this.tpl, {
13949             singleSelect:true,
13950             tickable:true,
13951             parent:this,
13952             store: this.store,
13953             selectedClass: this.selectedClass
13954         });
13955         
13956         //this.view.wrapEl.setDisplayed(false);
13957         this.view.on('click', this.onViewClick, this);
13958         
13959         
13960         
13961         this.store.on('beforeload', this.onBeforeLoad, this);
13962         this.store.on('load', this.onLoad, this);
13963         this.store.on('loadexception', this.onLoadException, this);
13964         
13965         if(this.editable){
13966             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13967                 "up" : function(e){
13968                     this.inKeyMode = true;
13969                     this.selectPrev();
13970                 },
13971
13972                 "down" : function(e){
13973                     this.inKeyMode = true;
13974                     this.selectNext();
13975                 },
13976
13977                 "enter" : function(e){
13978                     if(this.fireEvent("specialkey", this, e)){
13979                         this.onViewClick(false);
13980                     }
13981                     
13982                     return true;
13983                 },
13984
13985                 "esc" : function(e){
13986                     this.onTickableFooterButtonClick(e, false, false);
13987                 },
13988
13989                 "tab" : function(e){
13990                     this.fireEvent("specialkey", this, e);
13991                     
13992                     this.onTickableFooterButtonClick(e, false, false);
13993                     
13994                     return true;
13995                 },
13996
13997                 scope : this,
13998
13999                 doRelay : function(e, fn, key){
14000                     if(this.scope.isExpanded()){
14001                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14002                     }
14003                     return true;
14004                 },
14005
14006                 forceKeyDown: true
14007             });
14008         }
14009         
14010         this.queryDelay = Math.max(this.queryDelay || 10,
14011                 this.mode == 'local' ? 10 : 250);
14012         
14013         
14014         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14015         
14016         if(this.typeAhead){
14017             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14018         }
14019         
14020         if(this.editable !== false){
14021             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14022         }
14023         
14024         this.indicator = this.indicatorEl();
14025         
14026         if(this.indicator){
14027             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14028             this.indicator.hide();
14029         }
14030         
14031     },
14032
14033     onDestroy : function(){
14034         if(this.view){
14035             this.view.setStore(null);
14036             this.view.el.removeAllListeners();
14037             this.view.el.remove();
14038             this.view.purgeListeners();
14039         }
14040         if(this.list){
14041             this.list.dom.innerHTML  = '';
14042         }
14043         
14044         if(this.store){
14045             this.store.un('beforeload', this.onBeforeLoad, this);
14046             this.store.un('load', this.onLoad, this);
14047             this.store.un('loadexception', this.onLoadException, this);
14048         }
14049         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14050     },
14051
14052     // private
14053     fireKey : function(e){
14054         if(e.isNavKeyPress() && !this.list.isVisible()){
14055             this.fireEvent("specialkey", this, e);
14056         }
14057     },
14058
14059     // private
14060     onResize: function(w, h){
14061 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14062 //        
14063 //        if(typeof w != 'number'){
14064 //            // we do not handle it!?!?
14065 //            return;
14066 //        }
14067 //        var tw = this.trigger.getWidth();
14068 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14069 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14070 //        var x = w - tw;
14071 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14072 //            
14073 //        //this.trigger.setStyle('left', x+'px');
14074 //        
14075 //        if(this.list && this.listWidth === undefined){
14076 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14077 //            this.list.setWidth(lw);
14078 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14079 //        }
14080         
14081     
14082         
14083     },
14084
14085     /**
14086      * Allow or prevent the user from directly editing the field text.  If false is passed,
14087      * the user will only be able to select from the items defined in the dropdown list.  This method
14088      * is the runtime equivalent of setting the 'editable' config option at config time.
14089      * @param {Boolean} value True to allow the user to directly edit the field text
14090      */
14091     setEditable : function(value){
14092         if(value == this.editable){
14093             return;
14094         }
14095         this.editable = value;
14096         if(!value){
14097             this.inputEl().dom.setAttribute('readOnly', true);
14098             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14099             this.inputEl().addClass('x-combo-noedit');
14100         }else{
14101             this.inputEl().dom.setAttribute('readOnly', false);
14102             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14103             this.inputEl().removeClass('x-combo-noedit');
14104         }
14105     },
14106
14107     // private
14108     
14109     onBeforeLoad : function(combo,opts){
14110         if(!this.hasFocus){
14111             return;
14112         }
14113          if (!opts.add) {
14114             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14115          }
14116         this.restrictHeight();
14117         this.selectedIndex = -1;
14118     },
14119
14120     // private
14121     onLoad : function(){
14122         
14123         this.hasQuery = false;
14124         
14125         if(!this.hasFocus){
14126             return;
14127         }
14128         
14129         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14130             this.loading.hide();
14131         }
14132         
14133         if(this.store.getCount() > 0){
14134             
14135             this.expand();
14136             this.restrictHeight();
14137             if(this.lastQuery == this.allQuery){
14138                 if(this.editable && !this.tickable){
14139                     this.inputEl().dom.select();
14140                 }
14141                 
14142                 if(
14143                     !this.selectByValue(this.value, true) &&
14144                     this.autoFocus && 
14145                     (
14146                         !this.store.lastOptions ||
14147                         typeof(this.store.lastOptions.add) == 'undefined' || 
14148                         this.store.lastOptions.add != true
14149                     )
14150                 ){
14151                     this.select(0, true);
14152                 }
14153             }else{
14154                 if(this.autoFocus){
14155                     this.selectNext();
14156                 }
14157                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14158                     this.taTask.delay(this.typeAheadDelay);
14159                 }
14160             }
14161         }else{
14162             this.onEmptyResults();
14163         }
14164         
14165         //this.el.focus();
14166     },
14167     // private
14168     onLoadException : function()
14169     {
14170         this.hasQuery = false;
14171         
14172         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14173             this.loading.hide();
14174         }
14175         
14176         if(this.tickable && this.editable){
14177             return;
14178         }
14179         
14180         this.collapse();
14181         // only causes errors at present
14182         //Roo.log(this.store.reader.jsonData);
14183         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14184             // fixme
14185             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14186         //}
14187         
14188         
14189     },
14190     // private
14191     onTypeAhead : function(){
14192         if(this.store.getCount() > 0){
14193             var r = this.store.getAt(0);
14194             var newValue = r.data[this.displayField];
14195             var len = newValue.length;
14196             var selStart = this.getRawValue().length;
14197             
14198             if(selStart != len){
14199                 this.setRawValue(newValue);
14200                 this.selectText(selStart, newValue.length);
14201             }
14202         }
14203     },
14204
14205     // private
14206     onSelect : function(record, index){
14207         
14208         if(this.fireEvent('beforeselect', this, record, index) !== false){
14209         
14210             this.setFromData(index > -1 ? record.data : false);
14211             
14212             this.collapse();
14213             this.fireEvent('select', this, record, index);
14214         }
14215     },
14216
14217     /**
14218      * Returns the currently selected field value or empty string if no value is set.
14219      * @return {String} value The selected value
14220      */
14221     getValue : function()
14222     {
14223         if(Roo.isIOS && this.useNativeIOS){
14224             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14225         }
14226         
14227         if(this.multiple){
14228             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14229         }
14230         
14231         if(this.valueField){
14232             return typeof this.value != 'undefined' ? this.value : '';
14233         }else{
14234             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14235         }
14236     },
14237     
14238     getRawValue : function()
14239     {
14240         if(Roo.isIOS && this.useNativeIOS){
14241             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14242         }
14243         
14244         var v = this.inputEl().getValue();
14245         
14246         return v;
14247     },
14248
14249     /**
14250      * Clears any text/value currently set in the field
14251      */
14252     clearValue : function(){
14253         
14254         if(this.hiddenField){
14255             this.hiddenField.dom.value = '';
14256         }
14257         this.value = '';
14258         this.setRawValue('');
14259         this.lastSelectionText = '';
14260         this.lastData = false;
14261         
14262         var close = this.closeTriggerEl();
14263         
14264         if(close){
14265             close.hide();
14266         }
14267         
14268         this.validate();
14269         
14270     },
14271
14272     /**
14273      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14274      * will be displayed in the field.  If the value does not match the data value of an existing item,
14275      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14276      * Otherwise the field will be blank (although the value will still be set).
14277      * @param {String} value The value to match
14278      */
14279     setValue : function(v)
14280     {
14281         if(Roo.isIOS && this.useNativeIOS){
14282             this.setIOSValue(v);
14283             return;
14284         }
14285         
14286         if(this.multiple){
14287             this.syncValue();
14288             return;
14289         }
14290         
14291         var text = v;
14292         if(this.valueField){
14293             var r = this.findRecord(this.valueField, v);
14294             if(r){
14295                 text = r.data[this.displayField];
14296             }else if(this.valueNotFoundText !== undefined){
14297                 text = this.valueNotFoundText;
14298             }
14299         }
14300         this.lastSelectionText = text;
14301         if(this.hiddenField){
14302             this.hiddenField.dom.value = v;
14303         }
14304         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14305         this.value = v;
14306         
14307         var close = this.closeTriggerEl();
14308         
14309         if(close){
14310             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14311         }
14312         
14313         this.validate();
14314     },
14315     /**
14316      * @property {Object} the last set data for the element
14317      */
14318     
14319     lastData : false,
14320     /**
14321      * Sets the value of the field based on a object which is related to the record format for the store.
14322      * @param {Object} value the value to set as. or false on reset?
14323      */
14324     setFromData : function(o){
14325         
14326         if(this.multiple){
14327             this.addItem(o);
14328             return;
14329         }
14330             
14331         var dv = ''; // display value
14332         var vv = ''; // value value..
14333         this.lastData = o;
14334         if (this.displayField) {
14335             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14336         } else {
14337             // this is an error condition!!!
14338             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14339         }
14340         
14341         if(this.valueField){
14342             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14343         }
14344         
14345         var close = this.closeTriggerEl();
14346         
14347         if(close){
14348             if(dv.length || vv * 1 > 0){
14349                 close.show() ;
14350                 this.blockFocus=true;
14351             } else {
14352                 close.hide();
14353             }             
14354         }
14355         
14356         if(this.hiddenField){
14357             this.hiddenField.dom.value = vv;
14358             
14359             this.lastSelectionText = dv;
14360             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14361             this.value = vv;
14362             return;
14363         }
14364         // no hidden field.. - we store the value in 'value', but still display
14365         // display field!!!!
14366         this.lastSelectionText = dv;
14367         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14368         this.value = vv;
14369         
14370         
14371         
14372     },
14373     // private
14374     reset : function(){
14375         // overridden so that last data is reset..
14376         
14377         if(this.multiple){
14378             this.clearItem();
14379             return;
14380         }
14381         
14382         this.setValue(this.originalValue);
14383         //this.clearInvalid();
14384         this.lastData = false;
14385         if (this.view) {
14386             this.view.clearSelections();
14387         }
14388         
14389         this.validate();
14390     },
14391     // private
14392     findRecord : function(prop, value){
14393         var record;
14394         if(this.store.getCount() > 0){
14395             this.store.each(function(r){
14396                 if(r.data[prop] == value){
14397                     record = r;
14398                     return false;
14399                 }
14400                 return true;
14401             });
14402         }
14403         return record;
14404     },
14405     
14406     getName: function()
14407     {
14408         // returns hidden if it's set..
14409         if (!this.rendered) {return ''};
14410         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14411         
14412     },
14413     // private
14414     onViewMove : function(e, t){
14415         this.inKeyMode = false;
14416     },
14417
14418     // private
14419     onViewOver : function(e, t){
14420         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14421             return;
14422         }
14423         var item = this.view.findItemFromChild(t);
14424         
14425         if(item){
14426             var index = this.view.indexOf(item);
14427             this.select(index, false);
14428         }
14429     },
14430
14431     // private
14432     onViewClick : function(view, doFocus, el, e)
14433     {
14434         var index = this.view.getSelectedIndexes()[0];
14435         
14436         var r = this.store.getAt(index);
14437         
14438         if(this.tickable){
14439             
14440             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14441                 return;
14442             }
14443             
14444             var rm = false;
14445             var _this = this;
14446             
14447             Roo.each(this.tickItems, function(v,k){
14448                 
14449                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14450                     Roo.log(v);
14451                     _this.tickItems.splice(k, 1);
14452                     
14453                     if(typeof(e) == 'undefined' && view == false){
14454                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14455                     }
14456                     
14457                     rm = true;
14458                     return;
14459                 }
14460             });
14461             
14462             if(rm){
14463                 return;
14464             }
14465             
14466             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14467                 this.tickItems.push(r.data);
14468             }
14469             
14470             if(typeof(e) == 'undefined' && view == false){
14471                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14472             }
14473                     
14474             return;
14475         }
14476         
14477         if(r){
14478             this.onSelect(r, index);
14479         }
14480         if(doFocus !== false && !this.blockFocus){
14481             this.inputEl().focus();
14482         }
14483     },
14484
14485     // private
14486     restrictHeight : function(){
14487         //this.innerList.dom.style.height = '';
14488         //var inner = this.innerList.dom;
14489         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14490         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14491         //this.list.beginUpdate();
14492         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14493         this.list.alignTo(this.inputEl(), this.listAlign);
14494         this.list.alignTo(this.inputEl(), this.listAlign);
14495         //this.list.endUpdate();
14496     },
14497
14498     // private
14499     onEmptyResults : function(){
14500         
14501         if(this.tickable && this.editable){
14502             this.hasFocus = false;
14503             this.restrictHeight();
14504             return;
14505         }
14506         
14507         this.collapse();
14508     },
14509
14510     /**
14511      * Returns true if the dropdown list is expanded, else false.
14512      */
14513     isExpanded : function(){
14514         return this.list.isVisible();
14515     },
14516
14517     /**
14518      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14519      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14520      * @param {String} value The data value of the item to select
14521      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14522      * selected item if it is not currently in view (defaults to true)
14523      * @return {Boolean} True if the value matched an item in the list, else false
14524      */
14525     selectByValue : function(v, scrollIntoView){
14526         if(v !== undefined && v !== null){
14527             var r = this.findRecord(this.valueField || this.displayField, v);
14528             if(r){
14529                 this.select(this.store.indexOf(r), scrollIntoView);
14530                 return true;
14531             }
14532         }
14533         return false;
14534     },
14535
14536     /**
14537      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14538      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14539      * @param {Number} index The zero-based index of the list item to select
14540      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14541      * selected item if it is not currently in view (defaults to true)
14542      */
14543     select : function(index, scrollIntoView){
14544         this.selectedIndex = index;
14545         this.view.select(index);
14546         if(scrollIntoView !== false){
14547             var el = this.view.getNode(index);
14548             /*
14549              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14550              */
14551             if(el){
14552                 this.list.scrollChildIntoView(el, false);
14553             }
14554         }
14555     },
14556
14557     // private
14558     selectNext : function(){
14559         var ct = this.store.getCount();
14560         if(ct > 0){
14561             if(this.selectedIndex == -1){
14562                 this.select(0);
14563             }else if(this.selectedIndex < ct-1){
14564                 this.select(this.selectedIndex+1);
14565             }
14566         }
14567     },
14568
14569     // private
14570     selectPrev : function(){
14571         var ct = this.store.getCount();
14572         if(ct > 0){
14573             if(this.selectedIndex == -1){
14574                 this.select(0);
14575             }else if(this.selectedIndex != 0){
14576                 this.select(this.selectedIndex-1);
14577             }
14578         }
14579     },
14580
14581     // private
14582     onKeyUp : function(e){
14583         if(this.editable !== false && !e.isSpecialKey()){
14584             this.lastKey = e.getKey();
14585             this.dqTask.delay(this.queryDelay);
14586         }
14587     },
14588
14589     // private
14590     validateBlur : function(){
14591         return !this.list || !this.list.isVisible();   
14592     },
14593
14594     // private
14595     initQuery : function(){
14596         
14597         var v = this.getRawValue();
14598         
14599         if(this.tickable && this.editable){
14600             v = this.tickableInputEl().getValue();
14601         }
14602         
14603         this.doQuery(v);
14604     },
14605
14606     // private
14607     doForce : function(){
14608         if(this.inputEl().dom.value.length > 0){
14609             this.inputEl().dom.value =
14610                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14611              
14612         }
14613     },
14614
14615     /**
14616      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14617      * query allowing the query action to be canceled if needed.
14618      * @param {String} query The SQL query to execute
14619      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14620      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14621      * saved in the current store (defaults to false)
14622      */
14623     doQuery : function(q, forceAll){
14624         
14625         if(q === undefined || q === null){
14626             q = '';
14627         }
14628         var qe = {
14629             query: q,
14630             forceAll: forceAll,
14631             combo: this,
14632             cancel:false
14633         };
14634         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14635             return false;
14636         }
14637         q = qe.query;
14638         
14639         forceAll = qe.forceAll;
14640         if(forceAll === true || (q.length >= this.minChars)){
14641             
14642             this.hasQuery = true;
14643             
14644             if(this.lastQuery != q || this.alwaysQuery){
14645                 this.lastQuery = q;
14646                 if(this.mode == 'local'){
14647                     this.selectedIndex = -1;
14648                     if(forceAll){
14649                         this.store.clearFilter();
14650                     }else{
14651                         
14652                         if(this.specialFilter){
14653                             this.fireEvent('specialfilter', this);
14654                             this.onLoad();
14655                             return;
14656                         }
14657                         
14658                         this.store.filter(this.displayField, q);
14659                     }
14660                     
14661                     this.store.fireEvent("datachanged", this.store);
14662                     
14663                     this.onLoad();
14664                     
14665                     
14666                 }else{
14667                     
14668                     this.store.baseParams[this.queryParam] = q;
14669                     
14670                     var options = {params : this.getParams(q)};
14671                     
14672                     if(this.loadNext){
14673                         options.add = true;
14674                         options.params.start = this.page * this.pageSize;
14675                     }
14676                     
14677                     this.store.load(options);
14678                     
14679                     /*
14680                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14681                      *  we should expand the list on onLoad
14682                      *  so command out it
14683                      */
14684 //                    this.expand();
14685                 }
14686             }else{
14687                 this.selectedIndex = -1;
14688                 this.onLoad();   
14689             }
14690         }
14691         
14692         this.loadNext = false;
14693     },
14694     
14695     // private
14696     getParams : function(q){
14697         var p = {};
14698         //p[this.queryParam] = q;
14699         
14700         if(this.pageSize){
14701             p.start = 0;
14702             p.limit = this.pageSize;
14703         }
14704         return p;
14705     },
14706
14707     /**
14708      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14709      */
14710     collapse : function(){
14711         if(!this.isExpanded()){
14712             return;
14713         }
14714         
14715         this.list.hide();
14716         
14717         this.hasFocus = false;
14718         
14719         if(this.tickable){
14720             this.okBtn.hide();
14721             this.cancelBtn.hide();
14722             this.trigger.show();
14723             
14724             if(this.editable){
14725                 this.tickableInputEl().dom.value = '';
14726                 this.tickableInputEl().blur();
14727             }
14728             
14729         }
14730         
14731         Roo.get(document).un('mousedown', this.collapseIf, this);
14732         Roo.get(document).un('mousewheel', this.collapseIf, this);
14733         if (!this.editable) {
14734             Roo.get(document).un('keydown', this.listKeyPress, this);
14735         }
14736         this.fireEvent('collapse', this);
14737         
14738         this.validate();
14739     },
14740
14741     // private
14742     collapseIf : function(e){
14743         var in_combo  = e.within(this.el);
14744         var in_list =  e.within(this.list);
14745         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14746         
14747         if (in_combo || in_list || is_list) {
14748             //e.stopPropagation();
14749             return;
14750         }
14751         
14752         if(this.tickable){
14753             this.onTickableFooterButtonClick(e, false, false);
14754         }
14755
14756         this.collapse();
14757         
14758     },
14759
14760     /**
14761      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14762      */
14763     expand : function(){
14764        
14765         if(this.isExpanded() || !this.hasFocus){
14766             return;
14767         }
14768         
14769         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14770         this.list.setWidth(lw);
14771         
14772         Roo.log('expand');
14773         
14774         this.list.show();
14775         
14776         this.restrictHeight();
14777         
14778         if(this.tickable){
14779             
14780             this.tickItems = Roo.apply([], this.item);
14781             
14782             this.okBtn.show();
14783             this.cancelBtn.show();
14784             this.trigger.hide();
14785             
14786             if(this.editable){
14787                 this.tickableInputEl().focus();
14788             }
14789             
14790         }
14791         
14792         Roo.get(document).on('mousedown', this.collapseIf, this);
14793         Roo.get(document).on('mousewheel', this.collapseIf, this);
14794         if (!this.editable) {
14795             Roo.get(document).on('keydown', this.listKeyPress, this);
14796         }
14797         
14798         this.fireEvent('expand', this);
14799     },
14800
14801     // private
14802     // Implements the default empty TriggerField.onTriggerClick function
14803     onTriggerClick : function(e)
14804     {
14805         Roo.log('trigger click');
14806         
14807         if(this.disabled || !this.triggerList){
14808             return;
14809         }
14810         
14811         this.page = 0;
14812         this.loadNext = false;
14813         
14814         if(this.isExpanded()){
14815             this.collapse();
14816             if (!this.blockFocus) {
14817                 this.inputEl().focus();
14818             }
14819             
14820         }else {
14821             this.hasFocus = true;
14822             if(this.triggerAction == 'all') {
14823                 this.doQuery(this.allQuery, true);
14824             } else {
14825                 this.doQuery(this.getRawValue());
14826             }
14827             if (!this.blockFocus) {
14828                 this.inputEl().focus();
14829             }
14830         }
14831     },
14832     
14833     onTickableTriggerClick : function(e)
14834     {
14835         if(this.disabled){
14836             return;
14837         }
14838         
14839         this.page = 0;
14840         this.loadNext = false;
14841         this.hasFocus = true;
14842         
14843         if(this.triggerAction == 'all') {
14844             this.doQuery(this.allQuery, true);
14845         } else {
14846             this.doQuery(this.getRawValue());
14847         }
14848     },
14849     
14850     onSearchFieldClick : function(e)
14851     {
14852         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14853             this.onTickableFooterButtonClick(e, false, false);
14854             return;
14855         }
14856         
14857         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14858             return;
14859         }
14860         
14861         this.page = 0;
14862         this.loadNext = false;
14863         this.hasFocus = true;
14864         
14865         if(this.triggerAction == 'all') {
14866             this.doQuery(this.allQuery, true);
14867         } else {
14868             this.doQuery(this.getRawValue());
14869         }
14870     },
14871     
14872     listKeyPress : function(e)
14873     {
14874         //Roo.log('listkeypress');
14875         // scroll to first matching element based on key pres..
14876         if (e.isSpecialKey()) {
14877             return false;
14878         }
14879         var k = String.fromCharCode(e.getKey()).toUpperCase();
14880         //Roo.log(k);
14881         var match  = false;
14882         var csel = this.view.getSelectedNodes();
14883         var cselitem = false;
14884         if (csel.length) {
14885             var ix = this.view.indexOf(csel[0]);
14886             cselitem  = this.store.getAt(ix);
14887             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14888                 cselitem = false;
14889             }
14890             
14891         }
14892         
14893         this.store.each(function(v) { 
14894             if (cselitem) {
14895                 // start at existing selection.
14896                 if (cselitem.id == v.id) {
14897                     cselitem = false;
14898                 }
14899                 return true;
14900             }
14901                 
14902             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14903                 match = this.store.indexOf(v);
14904                 return false;
14905             }
14906             return true;
14907         }, this);
14908         
14909         if (match === false) {
14910             return true; // no more action?
14911         }
14912         // scroll to?
14913         this.view.select(match);
14914         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14915         sn.scrollIntoView(sn.dom.parentNode, false);
14916     },
14917     
14918     onViewScroll : function(e, t){
14919         
14920         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){
14921             return;
14922         }
14923         
14924         this.hasQuery = true;
14925         
14926         this.loading = this.list.select('.loading', true).first();
14927         
14928         if(this.loading === null){
14929             this.list.createChild({
14930                 tag: 'div',
14931                 cls: 'loading roo-select2-more-results roo-select2-active',
14932                 html: 'Loading more results...'
14933             });
14934             
14935             this.loading = this.list.select('.loading', true).first();
14936             
14937             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14938             
14939             this.loading.hide();
14940         }
14941         
14942         this.loading.show();
14943         
14944         var _combo = this;
14945         
14946         this.page++;
14947         this.loadNext = true;
14948         
14949         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14950         
14951         return;
14952     },
14953     
14954     addItem : function(o)
14955     {   
14956         var dv = ''; // display value
14957         
14958         if (this.displayField) {
14959             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14960         } else {
14961             // this is an error condition!!!
14962             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14963         }
14964         
14965         if(!dv.length){
14966             return;
14967         }
14968         
14969         var choice = this.choices.createChild({
14970             tag: 'li',
14971             cls: 'roo-select2-search-choice',
14972             cn: [
14973                 {
14974                     tag: 'div',
14975                     html: dv
14976                 },
14977                 {
14978                     tag: 'a',
14979                     href: '#',
14980                     cls: 'roo-select2-search-choice-close fa fa-times',
14981                     tabindex: '-1'
14982                 }
14983             ]
14984             
14985         }, this.searchField);
14986         
14987         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14988         
14989         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14990         
14991         this.item.push(o);
14992         
14993         this.lastData = o;
14994         
14995         this.syncValue();
14996         
14997         this.inputEl().dom.value = '';
14998         
14999         this.validate();
15000     },
15001     
15002     onRemoveItem : function(e, _self, o)
15003     {
15004         e.preventDefault();
15005         
15006         this.lastItem = Roo.apply([], this.item);
15007         
15008         var index = this.item.indexOf(o.data) * 1;
15009         
15010         if( index < 0){
15011             Roo.log('not this item?!');
15012             return;
15013         }
15014         
15015         this.item.splice(index, 1);
15016         o.item.remove();
15017         
15018         this.syncValue();
15019         
15020         this.fireEvent('remove', this, e);
15021         
15022         this.validate();
15023         
15024     },
15025     
15026     syncValue : function()
15027     {
15028         if(!this.item.length){
15029             this.clearValue();
15030             return;
15031         }
15032             
15033         var value = [];
15034         var _this = this;
15035         Roo.each(this.item, function(i){
15036             if(_this.valueField){
15037                 value.push(i[_this.valueField]);
15038                 return;
15039             }
15040
15041             value.push(i);
15042         });
15043
15044         this.value = value.join(',');
15045
15046         if(this.hiddenField){
15047             this.hiddenField.dom.value = this.value;
15048         }
15049         
15050         this.store.fireEvent("datachanged", this.store);
15051         
15052         this.validate();
15053     },
15054     
15055     clearItem : function()
15056     {
15057         if(!this.multiple){
15058             return;
15059         }
15060         
15061         this.item = [];
15062         
15063         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15064            c.remove();
15065         });
15066         
15067         this.syncValue();
15068         
15069         this.validate();
15070         
15071         if(this.tickable && !Roo.isTouch){
15072             this.view.refresh();
15073         }
15074     },
15075     
15076     inputEl: function ()
15077     {
15078         if(Roo.isIOS && this.useNativeIOS){
15079             return this.el.select('select.roo-ios-select', true).first();
15080         }
15081         
15082         if(Roo.isTouch && this.mobileTouchView){
15083             return this.el.select('input.form-control',true).first();
15084         }
15085         
15086         if(this.tickable){
15087             return this.searchField;
15088         }
15089         
15090         return this.el.select('input.form-control',true).first();
15091     },
15092     
15093     onTickableFooterButtonClick : function(e, btn, el)
15094     {
15095         e.preventDefault();
15096         
15097         this.lastItem = Roo.apply([], this.item);
15098         
15099         if(btn && btn.name == 'cancel'){
15100             this.tickItems = Roo.apply([], this.item);
15101             this.collapse();
15102             return;
15103         }
15104         
15105         this.clearItem();
15106         
15107         var _this = this;
15108         
15109         Roo.each(this.tickItems, function(o){
15110             _this.addItem(o);
15111         });
15112         
15113         this.collapse();
15114         
15115     },
15116     
15117     validate : function()
15118     {
15119         if(this.getVisibilityEl().hasClass('hidden')){
15120             return true;
15121         }
15122         
15123         var v = this.getRawValue();
15124         
15125         if(this.multiple){
15126             v = this.getValue();
15127         }
15128         
15129         if(this.disabled || this.allowBlank || v.length){
15130             this.markValid();
15131             return true;
15132         }
15133         
15134         this.markInvalid();
15135         return false;
15136     },
15137     
15138     tickableInputEl : function()
15139     {
15140         if(!this.tickable || !this.editable){
15141             return this.inputEl();
15142         }
15143         
15144         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15145     },
15146     
15147     
15148     getAutoCreateTouchView : function()
15149     {
15150         var id = Roo.id();
15151         
15152         var cfg = {
15153             cls: 'form-group' //input-group
15154         };
15155         
15156         var input =  {
15157             tag: 'input',
15158             id : id,
15159             type : this.inputType,
15160             cls : 'form-control x-combo-noedit',
15161             autocomplete: 'new-password',
15162             placeholder : this.placeholder || '',
15163             readonly : true
15164         };
15165         
15166         if (this.name) {
15167             input.name = this.name;
15168         }
15169         
15170         if (this.size) {
15171             input.cls += ' input-' + this.size;
15172         }
15173         
15174         if (this.disabled) {
15175             input.disabled = true;
15176         }
15177         
15178         var inputblock = {
15179             cls : '',
15180             cn : [
15181                 input
15182             ]
15183         };
15184         
15185         if(this.before){
15186             inputblock.cls += ' input-group';
15187             
15188             inputblock.cn.unshift({
15189                 tag :'span',
15190                 cls : 'input-group-addon input-group-prepend input-group-text',
15191                 html : this.before
15192             });
15193         }
15194         
15195         if(this.removable && !this.multiple){
15196             inputblock.cls += ' roo-removable';
15197             
15198             inputblock.cn.push({
15199                 tag: 'button',
15200                 html : 'x',
15201                 cls : 'roo-combo-removable-btn close'
15202             });
15203         }
15204
15205         if(this.hasFeedback && !this.allowBlank){
15206             
15207             inputblock.cls += ' has-feedback';
15208             
15209             inputblock.cn.push({
15210                 tag: 'span',
15211                 cls: 'glyphicon form-control-feedback'
15212             });
15213             
15214         }
15215         
15216         if (this.after) {
15217             
15218             inputblock.cls += (this.before) ? '' : ' input-group';
15219             
15220             inputblock.cn.push({
15221                 tag :'span',
15222                 cls : 'input-group-addon input-group-append input-group-text',
15223                 html : this.after
15224             });
15225         }
15226
15227         
15228         var ibwrap = inputblock;
15229         
15230         if(this.multiple){
15231             ibwrap = {
15232                 tag: 'ul',
15233                 cls: 'roo-select2-choices',
15234                 cn:[
15235                     {
15236                         tag: 'li',
15237                         cls: 'roo-select2-search-field',
15238                         cn: [
15239
15240                             inputblock
15241                         ]
15242                     }
15243                 ]
15244             };
15245         
15246             
15247         }
15248         
15249         var combobox = {
15250             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15251             cn: [
15252                 {
15253                     tag: 'input',
15254                     type : 'hidden',
15255                     cls: 'form-hidden-field'
15256                 },
15257                 ibwrap
15258             ]
15259         };
15260         
15261         if(!this.multiple && this.showToggleBtn){
15262             
15263             var caret = {
15264                         tag: 'span',
15265                         cls: 'caret'
15266             };
15267             
15268             if (this.caret != false) {
15269                 caret = {
15270                      tag: 'i',
15271                      cls: 'fa fa-' + this.caret
15272                 };
15273                 
15274             }
15275             
15276             combobox.cn.push({
15277                 tag :'span',
15278                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15279                 cn : [
15280                     caret,
15281                     {
15282                         tag: 'span',
15283                         cls: 'combobox-clear',
15284                         cn  : [
15285                             {
15286                                 tag : 'i',
15287                                 cls: 'icon-remove'
15288                             }
15289                         ]
15290                     }
15291                 ]
15292
15293             })
15294         }
15295         
15296         if(this.multiple){
15297             combobox.cls += ' roo-select2-container-multi';
15298         }
15299         
15300         var align = this.labelAlign || this.parentLabelAlign();
15301         
15302         if (align ==='left' && this.fieldLabel.length) {
15303
15304             cfg.cn = [
15305                 {
15306                    tag : 'i',
15307                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15308                    tooltip : 'This field is required'
15309                 },
15310                 {
15311                     tag: 'label',
15312                     cls : 'control-label col-form-label',
15313                     html : this.fieldLabel
15314
15315                 },
15316                 {
15317                     cls : '', 
15318                     cn: [
15319                         combobox
15320                     ]
15321                 }
15322             ];
15323             
15324             var labelCfg = cfg.cn[1];
15325             var contentCfg = cfg.cn[2];
15326             
15327
15328             if(this.indicatorpos == 'right'){
15329                 cfg.cn = [
15330                     {
15331                         tag: 'label',
15332                         'for' :  id,
15333                         cls : 'control-label col-form-label',
15334                         cn : [
15335                             {
15336                                 tag : 'span',
15337                                 html : this.fieldLabel
15338                             },
15339                             {
15340                                 tag : 'i',
15341                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15342                                 tooltip : 'This field is required'
15343                             }
15344                         ]
15345                     },
15346                     {
15347                         cls : "",
15348                         cn: [
15349                             combobox
15350                         ]
15351                     }
15352
15353                 ];
15354                 
15355                 labelCfg = cfg.cn[0];
15356                 contentCfg = cfg.cn[1];
15357             }
15358             
15359            
15360             
15361             if(this.labelWidth > 12){
15362                 labelCfg.style = "width: " + this.labelWidth + 'px';
15363             }
15364             
15365             if(this.labelWidth < 13 && this.labelmd == 0){
15366                 this.labelmd = this.labelWidth;
15367             }
15368             
15369             if(this.labellg > 0){
15370                 labelCfg.cls += ' col-lg-' + this.labellg;
15371                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15372             }
15373             
15374             if(this.labelmd > 0){
15375                 labelCfg.cls += ' col-md-' + this.labelmd;
15376                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15377             }
15378             
15379             if(this.labelsm > 0){
15380                 labelCfg.cls += ' col-sm-' + this.labelsm;
15381                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15382             }
15383             
15384             if(this.labelxs > 0){
15385                 labelCfg.cls += ' col-xs-' + this.labelxs;
15386                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15387             }
15388                 
15389                 
15390         } else if ( this.fieldLabel.length) {
15391             cfg.cn = [
15392                 {
15393                    tag : 'i',
15394                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15395                    tooltip : 'This field is required'
15396                 },
15397                 {
15398                     tag: 'label',
15399                     cls : 'control-label',
15400                     html : this.fieldLabel
15401
15402                 },
15403                 {
15404                     cls : '', 
15405                     cn: [
15406                         combobox
15407                     ]
15408                 }
15409             ];
15410             
15411             if(this.indicatorpos == 'right'){
15412                 cfg.cn = [
15413                     {
15414                         tag: 'label',
15415                         cls : 'control-label',
15416                         html : this.fieldLabel,
15417                         cn : [
15418                             {
15419                                tag : 'i',
15420                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15421                                tooltip : 'This field is required'
15422                             }
15423                         ]
15424                     },
15425                     {
15426                         cls : '', 
15427                         cn: [
15428                             combobox
15429                         ]
15430                     }
15431                 ];
15432             }
15433         } else {
15434             cfg.cn = combobox;    
15435         }
15436         
15437         
15438         var settings = this;
15439         
15440         ['xs','sm','md','lg'].map(function(size){
15441             if (settings[size]) {
15442                 cfg.cls += ' col-' + size + '-' + settings[size];
15443             }
15444         });
15445         
15446         return cfg;
15447     },
15448     
15449     initTouchView : function()
15450     {
15451         this.renderTouchView();
15452         
15453         this.touchViewEl.on('scroll', function(){
15454             this.el.dom.scrollTop = 0;
15455         }, this);
15456         
15457         this.originalValue = this.getValue();
15458         
15459         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15460         
15461         this.inputEl().on("click", this.showTouchView, this);
15462         if (this.triggerEl) {
15463             this.triggerEl.on("click", this.showTouchView, this);
15464         }
15465         
15466         
15467         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15468         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15469         
15470         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15471         
15472         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15473         this.store.on('load', this.onTouchViewLoad, this);
15474         this.store.on('loadexception', this.onTouchViewLoadException, this);
15475         
15476         if(this.hiddenName){
15477             
15478             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15479             
15480             this.hiddenField.dom.value =
15481                 this.hiddenValue !== undefined ? this.hiddenValue :
15482                 this.value !== undefined ? this.value : '';
15483         
15484             this.el.dom.removeAttribute('name');
15485             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15486         }
15487         
15488         if(this.multiple){
15489             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15490             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15491         }
15492         
15493         if(this.removable && !this.multiple){
15494             var close = this.closeTriggerEl();
15495             if(close){
15496                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15497                 close.on('click', this.removeBtnClick, this, close);
15498             }
15499         }
15500         /*
15501          * fix the bug in Safari iOS8
15502          */
15503         this.inputEl().on("focus", function(e){
15504             document.activeElement.blur();
15505         }, this);
15506         
15507         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15508         
15509         return;
15510         
15511         
15512     },
15513     
15514     renderTouchView : function()
15515     {
15516         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15517         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15518         
15519         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15520         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15521         
15522         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15523         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15524         this.touchViewBodyEl.setStyle('overflow', 'auto');
15525         
15526         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15527         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15528         
15529         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15530         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15531         
15532     },
15533     
15534     showTouchView : function()
15535     {
15536         if(this.disabled){
15537             return;
15538         }
15539         
15540         this.touchViewHeaderEl.hide();
15541
15542         if(this.modalTitle.length){
15543             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15544             this.touchViewHeaderEl.show();
15545         }
15546
15547         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15548         this.touchViewEl.show();
15549
15550         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15551         
15552         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15553         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15554
15555         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15556
15557         if(this.modalTitle.length){
15558             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15559         }
15560         
15561         this.touchViewBodyEl.setHeight(bodyHeight);
15562
15563         if(this.animate){
15564             var _this = this;
15565             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15566         }else{
15567             this.touchViewEl.addClass('in');
15568         }
15569         
15570         if(this._touchViewMask){
15571             Roo.get(document.body).addClass("x-body-masked");
15572             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15573             this._touchViewMask.setStyle('z-index', 10000);
15574             this._touchViewMask.addClass('show');
15575         }
15576         
15577         this.doTouchViewQuery();
15578         
15579     },
15580     
15581     hideTouchView : function()
15582     {
15583         this.touchViewEl.removeClass('in');
15584
15585         if(this.animate){
15586             var _this = this;
15587             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15588         }else{
15589             this.touchViewEl.setStyle('display', 'none');
15590         }
15591         
15592         if(this._touchViewMask){
15593             this._touchViewMask.removeClass('show');
15594             Roo.get(document.body).removeClass("x-body-masked");
15595         }
15596     },
15597     
15598     setTouchViewValue : function()
15599     {
15600         if(this.multiple){
15601             this.clearItem();
15602         
15603             var _this = this;
15604
15605             Roo.each(this.tickItems, function(o){
15606                 this.addItem(o);
15607             }, this);
15608         }
15609         
15610         this.hideTouchView();
15611     },
15612     
15613     doTouchViewQuery : function()
15614     {
15615         var qe = {
15616             query: '',
15617             forceAll: true,
15618             combo: this,
15619             cancel:false
15620         };
15621         
15622         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15623             return false;
15624         }
15625         
15626         if(!this.alwaysQuery || this.mode == 'local'){
15627             this.onTouchViewLoad();
15628             return;
15629         }
15630         
15631         this.store.load();
15632     },
15633     
15634     onTouchViewBeforeLoad : function(combo,opts)
15635     {
15636         return;
15637     },
15638
15639     // private
15640     onTouchViewLoad : function()
15641     {
15642         if(this.store.getCount() < 1){
15643             this.onTouchViewEmptyResults();
15644             return;
15645         }
15646         
15647         this.clearTouchView();
15648         
15649         var rawValue = this.getRawValue();
15650         
15651         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15652         
15653         this.tickItems = [];
15654         
15655         this.store.data.each(function(d, rowIndex){
15656             var row = this.touchViewListGroup.createChild(template);
15657             
15658             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15659                 row.addClass(d.data.cls);
15660             }
15661             
15662             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15663                 var cfg = {
15664                     data : d.data,
15665                     html : d.data[this.displayField]
15666                 };
15667                 
15668                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15669                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15670                 }
15671             }
15672             row.removeClass('selected');
15673             if(!this.multiple && this.valueField &&
15674                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15675             {
15676                 // radio buttons..
15677                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15678                 row.addClass('selected');
15679             }
15680             
15681             if(this.multiple && this.valueField &&
15682                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15683             {
15684                 
15685                 // checkboxes...
15686                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15687                 this.tickItems.push(d.data);
15688             }
15689             
15690             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15691             
15692         }, this);
15693         
15694         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15695         
15696         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15697
15698         if(this.modalTitle.length){
15699             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15700         }
15701
15702         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15703         
15704         if(this.mobile_restrict_height && listHeight < bodyHeight){
15705             this.touchViewBodyEl.setHeight(listHeight);
15706         }
15707         
15708         var _this = this;
15709         
15710         if(firstChecked && listHeight > bodyHeight){
15711             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15712         }
15713         
15714     },
15715     
15716     onTouchViewLoadException : function()
15717     {
15718         this.hideTouchView();
15719     },
15720     
15721     onTouchViewEmptyResults : function()
15722     {
15723         this.clearTouchView();
15724         
15725         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15726         
15727         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15728         
15729     },
15730     
15731     clearTouchView : function()
15732     {
15733         this.touchViewListGroup.dom.innerHTML = '';
15734     },
15735     
15736     onTouchViewClick : function(e, el, o)
15737     {
15738         e.preventDefault();
15739         
15740         var row = o.row;
15741         var rowIndex = o.rowIndex;
15742         
15743         var r = this.store.getAt(rowIndex);
15744         
15745         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15746             
15747             if(!this.multiple){
15748                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15749                     c.dom.removeAttribute('checked');
15750                 }, this);
15751
15752                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15753
15754                 this.setFromData(r.data);
15755
15756                 var close = this.closeTriggerEl();
15757
15758                 if(close){
15759                     close.show();
15760                 }
15761
15762                 this.hideTouchView();
15763
15764                 this.fireEvent('select', this, r, rowIndex);
15765
15766                 return;
15767             }
15768
15769             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15770                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15771                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15772                 return;
15773             }
15774
15775             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15776             this.addItem(r.data);
15777             this.tickItems.push(r.data);
15778         }
15779     },
15780     
15781     getAutoCreateNativeIOS : function()
15782     {
15783         var cfg = {
15784             cls: 'form-group' //input-group,
15785         };
15786         
15787         var combobox =  {
15788             tag: 'select',
15789             cls : 'roo-ios-select'
15790         };
15791         
15792         if (this.name) {
15793             combobox.name = this.name;
15794         }
15795         
15796         if (this.disabled) {
15797             combobox.disabled = true;
15798         }
15799         
15800         var settings = this;
15801         
15802         ['xs','sm','md','lg'].map(function(size){
15803             if (settings[size]) {
15804                 cfg.cls += ' col-' + size + '-' + settings[size];
15805             }
15806         });
15807         
15808         cfg.cn = combobox;
15809         
15810         return cfg;
15811         
15812     },
15813     
15814     initIOSView : function()
15815     {
15816         this.store.on('load', this.onIOSViewLoad, this);
15817         
15818         return;
15819     },
15820     
15821     onIOSViewLoad : function()
15822     {
15823         if(this.store.getCount() < 1){
15824             return;
15825         }
15826         
15827         this.clearIOSView();
15828         
15829         if(this.allowBlank) {
15830             
15831             var default_text = '-- SELECT --';
15832             
15833             if(this.placeholder.length){
15834                 default_text = this.placeholder;
15835             }
15836             
15837             if(this.emptyTitle.length){
15838                 default_text += ' - ' + this.emptyTitle + ' -';
15839             }
15840             
15841             var opt = this.inputEl().createChild({
15842                 tag: 'option',
15843                 value : 0,
15844                 html : default_text
15845             });
15846             
15847             var o = {};
15848             o[this.valueField] = 0;
15849             o[this.displayField] = default_text;
15850             
15851             this.ios_options.push({
15852                 data : o,
15853                 el : opt
15854             });
15855             
15856         }
15857         
15858         this.store.data.each(function(d, rowIndex){
15859             
15860             var html = '';
15861             
15862             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15863                 html = d.data[this.displayField];
15864             }
15865             
15866             var value = '';
15867             
15868             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15869                 value = d.data[this.valueField];
15870             }
15871             
15872             var option = {
15873                 tag: 'option',
15874                 value : value,
15875                 html : html
15876             };
15877             
15878             if(this.value == d.data[this.valueField]){
15879                 option['selected'] = true;
15880             }
15881             
15882             var opt = this.inputEl().createChild(option);
15883             
15884             this.ios_options.push({
15885                 data : d.data,
15886                 el : opt
15887             });
15888             
15889         }, this);
15890         
15891         this.inputEl().on('change', function(){
15892            this.fireEvent('select', this);
15893         }, this);
15894         
15895     },
15896     
15897     clearIOSView: function()
15898     {
15899         this.inputEl().dom.innerHTML = '';
15900         
15901         this.ios_options = [];
15902     },
15903     
15904     setIOSValue: function(v)
15905     {
15906         this.value = v;
15907         
15908         if(!this.ios_options){
15909             return;
15910         }
15911         
15912         Roo.each(this.ios_options, function(opts){
15913            
15914            opts.el.dom.removeAttribute('selected');
15915            
15916            if(opts.data[this.valueField] != v){
15917                return;
15918            }
15919            
15920            opts.el.dom.setAttribute('selected', true);
15921            
15922         }, this);
15923     }
15924
15925     /** 
15926     * @cfg {Boolean} grow 
15927     * @hide 
15928     */
15929     /** 
15930     * @cfg {Number} growMin 
15931     * @hide 
15932     */
15933     /** 
15934     * @cfg {Number} growMax 
15935     * @hide 
15936     */
15937     /**
15938      * @hide
15939      * @method autoSize
15940      */
15941 });
15942
15943 Roo.apply(Roo.bootstrap.ComboBox,  {
15944     
15945     header : {
15946         tag: 'div',
15947         cls: 'modal-header',
15948         cn: [
15949             {
15950                 tag: 'h4',
15951                 cls: 'modal-title'
15952             }
15953         ]
15954     },
15955     
15956     body : {
15957         tag: 'div',
15958         cls: 'modal-body',
15959         cn: [
15960             {
15961                 tag: 'ul',
15962                 cls: 'list-group'
15963             }
15964         ]
15965     },
15966     
15967     listItemRadio : {
15968         tag: 'li',
15969         cls: 'list-group-item',
15970         cn: [
15971             {
15972                 tag: 'span',
15973                 cls: 'roo-combobox-list-group-item-value'
15974             },
15975             {
15976                 tag: 'div',
15977                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15978                 cn: [
15979                     {
15980                         tag: 'input',
15981                         type: 'radio'
15982                     },
15983                     {
15984                         tag: 'label'
15985                     }
15986                 ]
15987             }
15988         ]
15989     },
15990     
15991     listItemCheckbox : {
15992         tag: 'li',
15993         cls: 'list-group-item',
15994         cn: [
15995             {
15996                 tag: 'span',
15997                 cls: 'roo-combobox-list-group-item-value'
15998             },
15999             {
16000                 tag: 'div',
16001                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16002                 cn: [
16003                     {
16004                         tag: 'input',
16005                         type: 'checkbox'
16006                     },
16007                     {
16008                         tag: 'label'
16009                     }
16010                 ]
16011             }
16012         ]
16013     },
16014     
16015     emptyResult : {
16016         tag: 'div',
16017         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16018     },
16019     
16020     footer : {
16021         tag: 'div',
16022         cls: 'modal-footer',
16023         cn: [
16024             {
16025                 tag: 'div',
16026                 cls: 'row',
16027                 cn: [
16028                     {
16029                         tag: 'div',
16030                         cls: 'col-xs-6 text-left',
16031                         cn: {
16032                             tag: 'button',
16033                             cls: 'btn btn-danger roo-touch-view-cancel',
16034                             html: 'Cancel'
16035                         }
16036                     },
16037                     {
16038                         tag: 'div',
16039                         cls: 'col-xs-6 text-right',
16040                         cn: {
16041                             tag: 'button',
16042                             cls: 'btn btn-success roo-touch-view-ok',
16043                             html: 'OK'
16044                         }
16045                     }
16046                 ]
16047             }
16048         ]
16049         
16050     }
16051 });
16052
16053 Roo.apply(Roo.bootstrap.ComboBox,  {
16054     
16055     touchViewTemplate : {
16056         tag: 'div',
16057         cls: 'modal fade roo-combobox-touch-view',
16058         cn: [
16059             {
16060                 tag: 'div',
16061                 cls: 'modal-dialog',
16062                 style : 'position:fixed', // we have to fix position....
16063                 cn: [
16064                     {
16065                         tag: 'div',
16066                         cls: 'modal-content',
16067                         cn: [
16068                             Roo.bootstrap.ComboBox.header,
16069                             Roo.bootstrap.ComboBox.body,
16070                             Roo.bootstrap.ComboBox.footer
16071                         ]
16072                     }
16073                 ]
16074             }
16075         ]
16076     }
16077 });/*
16078  * Based on:
16079  * Ext JS Library 1.1.1
16080  * Copyright(c) 2006-2007, Ext JS, LLC.
16081  *
16082  * Originally Released Under LGPL - original licence link has changed is not relivant.
16083  *
16084  * Fork - LGPL
16085  * <script type="text/javascript">
16086  */
16087
16088 /**
16089  * @class Roo.View
16090  * @extends Roo.util.Observable
16091  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16092  * This class also supports single and multi selection modes. <br>
16093  * Create a data model bound view:
16094  <pre><code>
16095  var store = new Roo.data.Store(...);
16096
16097  var view = new Roo.View({
16098     el : "my-element",
16099     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16100  
16101     singleSelect: true,
16102     selectedClass: "ydataview-selected",
16103     store: store
16104  });
16105
16106  // listen for node click?
16107  view.on("click", function(vw, index, node, e){
16108  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16109  });
16110
16111  // load XML data
16112  dataModel.load("foobar.xml");
16113  </code></pre>
16114  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16115  * <br><br>
16116  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16117  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16118  * 
16119  * Note: old style constructor is still suported (container, template, config)
16120  * 
16121  * @constructor
16122  * Create a new View
16123  * @param {Object} config The config object
16124  * 
16125  */
16126 Roo.View = function(config, depreciated_tpl, depreciated_config){
16127     
16128     this.parent = false;
16129     
16130     if (typeof(depreciated_tpl) == 'undefined') {
16131         // new way.. - universal constructor.
16132         Roo.apply(this, config);
16133         this.el  = Roo.get(this.el);
16134     } else {
16135         // old format..
16136         this.el  = Roo.get(config);
16137         this.tpl = depreciated_tpl;
16138         Roo.apply(this, depreciated_config);
16139     }
16140     this.wrapEl  = this.el.wrap().wrap();
16141     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16142     
16143     
16144     if(typeof(this.tpl) == "string"){
16145         this.tpl = new Roo.Template(this.tpl);
16146     } else {
16147         // support xtype ctors..
16148         this.tpl = new Roo.factory(this.tpl, Roo);
16149     }
16150     
16151     
16152     this.tpl.compile();
16153     
16154     /** @private */
16155     this.addEvents({
16156         /**
16157          * @event beforeclick
16158          * Fires before a click is processed. Returns false to cancel the default action.
16159          * @param {Roo.View} this
16160          * @param {Number} index The index of the target node
16161          * @param {HTMLElement} node The target node
16162          * @param {Roo.EventObject} e The raw event object
16163          */
16164             "beforeclick" : true,
16165         /**
16166          * @event click
16167          * Fires when a template node is clicked.
16168          * @param {Roo.View} this
16169          * @param {Number} index The index of the target node
16170          * @param {HTMLElement} node The target node
16171          * @param {Roo.EventObject} e The raw event object
16172          */
16173             "click" : true,
16174         /**
16175          * @event dblclick
16176          * Fires when a template node is double clicked.
16177          * @param {Roo.View} this
16178          * @param {Number} index The index of the target node
16179          * @param {HTMLElement} node The target node
16180          * @param {Roo.EventObject} e The raw event object
16181          */
16182             "dblclick" : true,
16183         /**
16184          * @event contextmenu
16185          * Fires when a template node is right clicked.
16186          * @param {Roo.View} this
16187          * @param {Number} index The index of the target node
16188          * @param {HTMLElement} node The target node
16189          * @param {Roo.EventObject} e The raw event object
16190          */
16191             "contextmenu" : true,
16192         /**
16193          * @event selectionchange
16194          * Fires when the selected nodes change.
16195          * @param {Roo.View} this
16196          * @param {Array} selections Array of the selected nodes
16197          */
16198             "selectionchange" : true,
16199     
16200         /**
16201          * @event beforeselect
16202          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16203          * @param {Roo.View} this
16204          * @param {HTMLElement} node The node to be selected
16205          * @param {Array} selections Array of currently selected nodes
16206          */
16207             "beforeselect" : true,
16208         /**
16209          * @event preparedata
16210          * Fires on every row to render, to allow you to change the data.
16211          * @param {Roo.View} this
16212          * @param {Object} data to be rendered (change this)
16213          */
16214           "preparedata" : true
16215           
16216           
16217         });
16218
16219
16220
16221     this.el.on({
16222         "click": this.onClick,
16223         "dblclick": this.onDblClick,
16224         "contextmenu": this.onContextMenu,
16225         scope:this
16226     });
16227
16228     this.selections = [];
16229     this.nodes = [];
16230     this.cmp = new Roo.CompositeElementLite([]);
16231     if(this.store){
16232         this.store = Roo.factory(this.store, Roo.data);
16233         this.setStore(this.store, true);
16234     }
16235     
16236     if ( this.footer && this.footer.xtype) {
16237            
16238          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16239         
16240         this.footer.dataSource = this.store;
16241         this.footer.container = fctr;
16242         this.footer = Roo.factory(this.footer, Roo);
16243         fctr.insertFirst(this.el);
16244         
16245         // this is a bit insane - as the paging toolbar seems to detach the el..
16246 //        dom.parentNode.parentNode.parentNode
16247          // they get detached?
16248     }
16249     
16250     
16251     Roo.View.superclass.constructor.call(this);
16252     
16253     
16254 };
16255
16256 Roo.extend(Roo.View, Roo.util.Observable, {
16257     
16258      /**
16259      * @cfg {Roo.data.Store} store Data store to load data from.
16260      */
16261     store : false,
16262     
16263     /**
16264      * @cfg {String|Roo.Element} el The container element.
16265      */
16266     el : '',
16267     
16268     /**
16269      * @cfg {String|Roo.Template} tpl The template used by this View 
16270      */
16271     tpl : false,
16272     /**
16273      * @cfg {String} dataName the named area of the template to use as the data area
16274      *                          Works with domtemplates roo-name="name"
16275      */
16276     dataName: false,
16277     /**
16278      * @cfg {String} selectedClass The css class to add to selected nodes
16279      */
16280     selectedClass : "x-view-selected",
16281      /**
16282      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16283      */
16284     emptyText : "",
16285     
16286     /**
16287      * @cfg {String} text to display on mask (default Loading)
16288      */
16289     mask : false,
16290     /**
16291      * @cfg {Boolean} multiSelect Allow multiple selection
16292      */
16293     multiSelect : false,
16294     /**
16295      * @cfg {Boolean} singleSelect Allow single selection
16296      */
16297     singleSelect:  false,
16298     
16299     /**
16300      * @cfg {Boolean} toggleSelect - selecting 
16301      */
16302     toggleSelect : false,
16303     
16304     /**
16305      * @cfg {Boolean} tickable - selecting 
16306      */
16307     tickable : false,
16308     
16309     /**
16310      * Returns the element this view is bound to.
16311      * @return {Roo.Element}
16312      */
16313     getEl : function(){
16314         return this.wrapEl;
16315     },
16316     
16317     
16318
16319     /**
16320      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16321      */
16322     refresh : function(){
16323         //Roo.log('refresh');
16324         var t = this.tpl;
16325         
16326         // if we are using something like 'domtemplate', then
16327         // the what gets used is:
16328         // t.applySubtemplate(NAME, data, wrapping data..)
16329         // the outer template then get' applied with
16330         //     the store 'extra data'
16331         // and the body get's added to the
16332         //      roo-name="data" node?
16333         //      <span class='roo-tpl-{name}'></span> ?????
16334         
16335         
16336         
16337         this.clearSelections();
16338         this.el.update("");
16339         var html = [];
16340         var records = this.store.getRange();
16341         if(records.length < 1) {
16342             
16343             // is this valid??  = should it render a template??
16344             
16345             this.el.update(this.emptyText);
16346             return;
16347         }
16348         var el = this.el;
16349         if (this.dataName) {
16350             this.el.update(t.apply(this.store.meta)); //????
16351             el = this.el.child('.roo-tpl-' + this.dataName);
16352         }
16353         
16354         for(var i = 0, len = records.length; i < len; i++){
16355             var data = this.prepareData(records[i].data, i, records[i]);
16356             this.fireEvent("preparedata", this, data, i, records[i]);
16357             
16358             var d = Roo.apply({}, data);
16359             
16360             if(this.tickable){
16361                 Roo.apply(d, {'roo-id' : Roo.id()});
16362                 
16363                 var _this = this;
16364             
16365                 Roo.each(this.parent.item, function(item){
16366                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16367                         return;
16368                     }
16369                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16370                 });
16371             }
16372             
16373             html[html.length] = Roo.util.Format.trim(
16374                 this.dataName ?
16375                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16376                     t.apply(d)
16377             );
16378         }
16379         
16380         
16381         
16382         el.update(html.join(""));
16383         this.nodes = el.dom.childNodes;
16384         this.updateIndexes(0);
16385     },
16386     
16387
16388     /**
16389      * Function to override to reformat the data that is sent to
16390      * the template for each node.
16391      * DEPRICATED - use the preparedata event handler.
16392      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16393      * a JSON object for an UpdateManager bound view).
16394      */
16395     prepareData : function(data, index, record)
16396     {
16397         this.fireEvent("preparedata", this, data, index, record);
16398         return data;
16399     },
16400
16401     onUpdate : function(ds, record){
16402         // Roo.log('on update');   
16403         this.clearSelections();
16404         var index = this.store.indexOf(record);
16405         var n = this.nodes[index];
16406         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16407         n.parentNode.removeChild(n);
16408         this.updateIndexes(index, index);
16409     },
16410
16411     
16412     
16413 // --------- FIXME     
16414     onAdd : function(ds, records, index)
16415     {
16416         //Roo.log(['on Add', ds, records, index] );        
16417         this.clearSelections();
16418         if(this.nodes.length == 0){
16419             this.refresh();
16420             return;
16421         }
16422         var n = this.nodes[index];
16423         for(var i = 0, len = records.length; i < len; i++){
16424             var d = this.prepareData(records[i].data, i, records[i]);
16425             if(n){
16426                 this.tpl.insertBefore(n, d);
16427             }else{
16428                 
16429                 this.tpl.append(this.el, d);
16430             }
16431         }
16432         this.updateIndexes(index);
16433     },
16434
16435     onRemove : function(ds, record, index){
16436        // Roo.log('onRemove');
16437         this.clearSelections();
16438         var el = this.dataName  ?
16439             this.el.child('.roo-tpl-' + this.dataName) :
16440             this.el; 
16441         
16442         el.dom.removeChild(this.nodes[index]);
16443         this.updateIndexes(index);
16444     },
16445
16446     /**
16447      * Refresh an individual node.
16448      * @param {Number} index
16449      */
16450     refreshNode : function(index){
16451         this.onUpdate(this.store, this.store.getAt(index));
16452     },
16453
16454     updateIndexes : function(startIndex, endIndex){
16455         var ns = this.nodes;
16456         startIndex = startIndex || 0;
16457         endIndex = endIndex || ns.length - 1;
16458         for(var i = startIndex; i <= endIndex; i++){
16459             ns[i].nodeIndex = i;
16460         }
16461     },
16462
16463     /**
16464      * Changes the data store this view uses and refresh the view.
16465      * @param {Store} store
16466      */
16467     setStore : function(store, initial){
16468         if(!initial && this.store){
16469             this.store.un("datachanged", this.refresh);
16470             this.store.un("add", this.onAdd);
16471             this.store.un("remove", this.onRemove);
16472             this.store.un("update", this.onUpdate);
16473             this.store.un("clear", this.refresh);
16474             this.store.un("beforeload", this.onBeforeLoad);
16475             this.store.un("load", this.onLoad);
16476             this.store.un("loadexception", this.onLoad);
16477         }
16478         if(store){
16479           
16480             store.on("datachanged", this.refresh, this);
16481             store.on("add", this.onAdd, this);
16482             store.on("remove", this.onRemove, this);
16483             store.on("update", this.onUpdate, this);
16484             store.on("clear", this.refresh, this);
16485             store.on("beforeload", this.onBeforeLoad, this);
16486             store.on("load", this.onLoad, this);
16487             store.on("loadexception", this.onLoad, this);
16488         }
16489         
16490         if(store){
16491             this.refresh();
16492         }
16493     },
16494     /**
16495      * onbeforeLoad - masks the loading area.
16496      *
16497      */
16498     onBeforeLoad : function(store,opts)
16499     {
16500          //Roo.log('onBeforeLoad');   
16501         if (!opts.add) {
16502             this.el.update("");
16503         }
16504         this.el.mask(this.mask ? this.mask : "Loading" ); 
16505     },
16506     onLoad : function ()
16507     {
16508         this.el.unmask();
16509     },
16510     
16511
16512     /**
16513      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16514      * @param {HTMLElement} node
16515      * @return {HTMLElement} The template node
16516      */
16517     findItemFromChild : function(node){
16518         var el = this.dataName  ?
16519             this.el.child('.roo-tpl-' + this.dataName,true) :
16520             this.el.dom; 
16521         
16522         if(!node || node.parentNode == el){
16523                     return node;
16524             }
16525             var p = node.parentNode;
16526             while(p && p != el){
16527             if(p.parentNode == el){
16528                 return p;
16529             }
16530             p = p.parentNode;
16531         }
16532             return null;
16533     },
16534
16535     /** @ignore */
16536     onClick : function(e){
16537         var item = this.findItemFromChild(e.getTarget());
16538         if(item){
16539             var index = this.indexOf(item);
16540             if(this.onItemClick(item, index, e) !== false){
16541                 this.fireEvent("click", this, index, item, e);
16542             }
16543         }else{
16544             this.clearSelections();
16545         }
16546     },
16547
16548     /** @ignore */
16549     onContextMenu : function(e){
16550         var item = this.findItemFromChild(e.getTarget());
16551         if(item){
16552             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16553         }
16554     },
16555
16556     /** @ignore */
16557     onDblClick : function(e){
16558         var item = this.findItemFromChild(e.getTarget());
16559         if(item){
16560             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16561         }
16562     },
16563
16564     onItemClick : function(item, index, e)
16565     {
16566         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16567             return false;
16568         }
16569         if (this.toggleSelect) {
16570             var m = this.isSelected(item) ? 'unselect' : 'select';
16571             //Roo.log(m);
16572             var _t = this;
16573             _t[m](item, true, false);
16574             return true;
16575         }
16576         if(this.multiSelect || this.singleSelect){
16577             if(this.multiSelect && e.shiftKey && this.lastSelection){
16578                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16579             }else{
16580                 this.select(item, this.multiSelect && e.ctrlKey);
16581                 this.lastSelection = item;
16582             }
16583             
16584             if(!this.tickable){
16585                 e.preventDefault();
16586             }
16587             
16588         }
16589         return true;
16590     },
16591
16592     /**
16593      * Get the number of selected nodes.
16594      * @return {Number}
16595      */
16596     getSelectionCount : function(){
16597         return this.selections.length;
16598     },
16599
16600     /**
16601      * Get the currently selected nodes.
16602      * @return {Array} An array of HTMLElements
16603      */
16604     getSelectedNodes : function(){
16605         return this.selections;
16606     },
16607
16608     /**
16609      * Get the indexes of the selected nodes.
16610      * @return {Array}
16611      */
16612     getSelectedIndexes : function(){
16613         var indexes = [], s = this.selections;
16614         for(var i = 0, len = s.length; i < len; i++){
16615             indexes.push(s[i].nodeIndex);
16616         }
16617         return indexes;
16618     },
16619
16620     /**
16621      * Clear all selections
16622      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16623      */
16624     clearSelections : function(suppressEvent){
16625         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16626             this.cmp.elements = this.selections;
16627             this.cmp.removeClass(this.selectedClass);
16628             this.selections = [];
16629             if(!suppressEvent){
16630                 this.fireEvent("selectionchange", this, this.selections);
16631             }
16632         }
16633     },
16634
16635     /**
16636      * Returns true if the passed node is selected
16637      * @param {HTMLElement/Number} node The node or node index
16638      * @return {Boolean}
16639      */
16640     isSelected : function(node){
16641         var s = this.selections;
16642         if(s.length < 1){
16643             return false;
16644         }
16645         node = this.getNode(node);
16646         return s.indexOf(node) !== -1;
16647     },
16648
16649     /**
16650      * Selects nodes.
16651      * @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
16652      * @param {Boolean} keepExisting (optional) true to keep existing selections
16653      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16654      */
16655     select : function(nodeInfo, keepExisting, suppressEvent){
16656         if(nodeInfo instanceof Array){
16657             if(!keepExisting){
16658                 this.clearSelections(true);
16659             }
16660             for(var i = 0, len = nodeInfo.length; i < len; i++){
16661                 this.select(nodeInfo[i], true, true);
16662             }
16663             return;
16664         } 
16665         var node = this.getNode(nodeInfo);
16666         if(!node || this.isSelected(node)){
16667             return; // already selected.
16668         }
16669         if(!keepExisting){
16670             this.clearSelections(true);
16671         }
16672         
16673         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16674             Roo.fly(node).addClass(this.selectedClass);
16675             this.selections.push(node);
16676             if(!suppressEvent){
16677                 this.fireEvent("selectionchange", this, this.selections);
16678             }
16679         }
16680         
16681         
16682     },
16683       /**
16684      * Unselects nodes.
16685      * @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
16686      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16687      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16688      */
16689     unselect : function(nodeInfo, keepExisting, suppressEvent)
16690     {
16691         if(nodeInfo instanceof Array){
16692             Roo.each(this.selections, function(s) {
16693                 this.unselect(s, nodeInfo);
16694             }, this);
16695             return;
16696         }
16697         var node = this.getNode(nodeInfo);
16698         if(!node || !this.isSelected(node)){
16699             //Roo.log("not selected");
16700             return; // not selected.
16701         }
16702         // fireevent???
16703         var ns = [];
16704         Roo.each(this.selections, function(s) {
16705             if (s == node ) {
16706                 Roo.fly(node).removeClass(this.selectedClass);
16707
16708                 return;
16709             }
16710             ns.push(s);
16711         },this);
16712         
16713         this.selections= ns;
16714         this.fireEvent("selectionchange", this, this.selections);
16715     },
16716
16717     /**
16718      * Gets a template node.
16719      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16720      * @return {HTMLElement} The node or null if it wasn't found
16721      */
16722     getNode : function(nodeInfo){
16723         if(typeof nodeInfo == "string"){
16724             return document.getElementById(nodeInfo);
16725         }else if(typeof nodeInfo == "number"){
16726             return this.nodes[nodeInfo];
16727         }
16728         return nodeInfo;
16729     },
16730
16731     /**
16732      * Gets a range template nodes.
16733      * @param {Number} startIndex
16734      * @param {Number} endIndex
16735      * @return {Array} An array of nodes
16736      */
16737     getNodes : function(start, end){
16738         var ns = this.nodes;
16739         start = start || 0;
16740         end = typeof end == "undefined" ? ns.length - 1 : end;
16741         var nodes = [];
16742         if(start <= end){
16743             for(var i = start; i <= end; i++){
16744                 nodes.push(ns[i]);
16745             }
16746         } else{
16747             for(var i = start; i >= end; i--){
16748                 nodes.push(ns[i]);
16749             }
16750         }
16751         return nodes;
16752     },
16753
16754     /**
16755      * Finds the index of the passed node
16756      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16757      * @return {Number} The index of the node or -1
16758      */
16759     indexOf : function(node){
16760         node = this.getNode(node);
16761         if(typeof node.nodeIndex == "number"){
16762             return node.nodeIndex;
16763         }
16764         var ns = this.nodes;
16765         for(var i = 0, len = ns.length; i < len; i++){
16766             if(ns[i] == node){
16767                 return i;
16768             }
16769         }
16770         return -1;
16771     }
16772 });
16773 /*
16774  * - LGPL
16775  *
16776  * based on jquery fullcalendar
16777  * 
16778  */
16779
16780 Roo.bootstrap = Roo.bootstrap || {};
16781 /**
16782  * @class Roo.bootstrap.Calendar
16783  * @extends Roo.bootstrap.Component
16784  * Bootstrap Calendar class
16785  * @cfg {Boolean} loadMask (true|false) default false
16786  * @cfg {Object} header generate the user specific header of the calendar, default false
16787
16788  * @constructor
16789  * Create a new Container
16790  * @param {Object} config The config object
16791  */
16792
16793
16794
16795 Roo.bootstrap.Calendar = function(config){
16796     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16797      this.addEvents({
16798         /**
16799              * @event select
16800              * Fires when a date is selected
16801              * @param {DatePicker} this
16802              * @param {Date} date The selected date
16803              */
16804         'select': true,
16805         /**
16806              * @event monthchange
16807              * Fires when the displayed month changes 
16808              * @param {DatePicker} this
16809              * @param {Date} date The selected month
16810              */
16811         'monthchange': true,
16812         /**
16813              * @event evententer
16814              * Fires when mouse over an event
16815              * @param {Calendar} this
16816              * @param {event} Event
16817              */
16818         'evententer': true,
16819         /**
16820              * @event eventleave
16821              * Fires when the mouse leaves an
16822              * @param {Calendar} this
16823              * @param {event}
16824              */
16825         'eventleave': true,
16826         /**
16827              * @event eventclick
16828              * Fires when the mouse click an
16829              * @param {Calendar} this
16830              * @param {event}
16831              */
16832         'eventclick': true
16833         
16834     });
16835
16836 };
16837
16838 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16839     
16840      /**
16841      * @cfg {Number} startDay
16842      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16843      */
16844     startDay : 0,
16845     
16846     loadMask : false,
16847     
16848     header : false,
16849       
16850     getAutoCreate : function(){
16851         
16852         
16853         var fc_button = function(name, corner, style, content ) {
16854             return Roo.apply({},{
16855                 tag : 'span',
16856                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16857                          (corner.length ?
16858                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16859                             ''
16860                         ),
16861                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16862                 unselectable: 'on'
16863             });
16864         };
16865         
16866         var header = {};
16867         
16868         if(!this.header){
16869             header = {
16870                 tag : 'table',
16871                 cls : 'fc-header',
16872                 style : 'width:100%',
16873                 cn : [
16874                     {
16875                         tag: 'tr',
16876                         cn : [
16877                             {
16878                                 tag : 'td',
16879                                 cls : 'fc-header-left',
16880                                 cn : [
16881                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16882                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16883                                     { tag: 'span', cls: 'fc-header-space' },
16884                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16885
16886
16887                                 ]
16888                             },
16889
16890                             {
16891                                 tag : 'td',
16892                                 cls : 'fc-header-center',
16893                                 cn : [
16894                                     {
16895                                         tag: 'span',
16896                                         cls: 'fc-header-title',
16897                                         cn : {
16898                                             tag: 'H2',
16899                                             html : 'month / year'
16900                                         }
16901                                     }
16902
16903                                 ]
16904                             },
16905                             {
16906                                 tag : 'td',
16907                                 cls : 'fc-header-right',
16908                                 cn : [
16909                               /*      fc_button('month', 'left', '', 'month' ),
16910                                     fc_button('week', '', '', 'week' ),
16911                                     fc_button('day', 'right', '', 'day' )
16912                                 */    
16913
16914                                 ]
16915                             }
16916
16917                         ]
16918                     }
16919                 ]
16920             };
16921         }
16922         
16923         header = this.header;
16924         
16925        
16926         var cal_heads = function() {
16927             var ret = [];
16928             // fixme - handle this.
16929             
16930             for (var i =0; i < Date.dayNames.length; i++) {
16931                 var d = Date.dayNames[i];
16932                 ret.push({
16933                     tag: 'th',
16934                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16935                     html : d.substring(0,3)
16936                 });
16937                 
16938             }
16939             ret[0].cls += ' fc-first';
16940             ret[6].cls += ' fc-last';
16941             return ret;
16942         };
16943         var cal_cell = function(n) {
16944             return  {
16945                 tag: 'td',
16946                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16947                 cn : [
16948                     {
16949                         cn : [
16950                             {
16951                                 cls: 'fc-day-number',
16952                                 html: 'D'
16953                             },
16954                             {
16955                                 cls: 'fc-day-content',
16956                              
16957                                 cn : [
16958                                      {
16959                                         style: 'position: relative;' // height: 17px;
16960                                     }
16961                                 ]
16962                             }
16963                             
16964                             
16965                         ]
16966                     }
16967                 ]
16968                 
16969             }
16970         };
16971         var cal_rows = function() {
16972             
16973             var ret = [];
16974             for (var r = 0; r < 6; r++) {
16975                 var row= {
16976                     tag : 'tr',
16977                     cls : 'fc-week',
16978                     cn : []
16979                 };
16980                 
16981                 for (var i =0; i < Date.dayNames.length; i++) {
16982                     var d = Date.dayNames[i];
16983                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16984
16985                 }
16986                 row.cn[0].cls+=' fc-first';
16987                 row.cn[0].cn[0].style = 'min-height:90px';
16988                 row.cn[6].cls+=' fc-last';
16989                 ret.push(row);
16990                 
16991             }
16992             ret[0].cls += ' fc-first';
16993             ret[4].cls += ' fc-prev-last';
16994             ret[5].cls += ' fc-last';
16995             return ret;
16996             
16997         };
16998         
16999         var cal_table = {
17000             tag: 'table',
17001             cls: 'fc-border-separate',
17002             style : 'width:100%',
17003             cellspacing  : 0,
17004             cn : [
17005                 { 
17006                     tag: 'thead',
17007                     cn : [
17008                         { 
17009                             tag: 'tr',
17010                             cls : 'fc-first fc-last',
17011                             cn : cal_heads()
17012                         }
17013                     ]
17014                 },
17015                 { 
17016                     tag: 'tbody',
17017                     cn : cal_rows()
17018                 }
17019                   
17020             ]
17021         };
17022          
17023          var cfg = {
17024             cls : 'fc fc-ltr',
17025             cn : [
17026                 header,
17027                 {
17028                     cls : 'fc-content',
17029                     style : "position: relative;",
17030                     cn : [
17031                         {
17032                             cls : 'fc-view fc-view-month fc-grid',
17033                             style : 'position: relative',
17034                             unselectable : 'on',
17035                             cn : [
17036                                 {
17037                                     cls : 'fc-event-container',
17038                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17039                                 },
17040                                 cal_table
17041                             ]
17042                         }
17043                     ]
17044     
17045                 }
17046            ] 
17047             
17048         };
17049         
17050          
17051         
17052         return cfg;
17053     },
17054     
17055     
17056     initEvents : function()
17057     {
17058         if(!this.store){
17059             throw "can not find store for calendar";
17060         }
17061         
17062         var mark = {
17063             tag: "div",
17064             cls:"x-dlg-mask",
17065             style: "text-align:center",
17066             cn: [
17067                 {
17068                     tag: "div",
17069                     style: "background-color:white;width:50%;margin:250 auto",
17070                     cn: [
17071                         {
17072                             tag: "img",
17073                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17074                         },
17075                         {
17076                             tag: "span",
17077                             html: "Loading"
17078                         }
17079                         
17080                     ]
17081                 }
17082             ]
17083         };
17084         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17085         
17086         var size = this.el.select('.fc-content', true).first().getSize();
17087         this.maskEl.setSize(size.width, size.height);
17088         this.maskEl.enableDisplayMode("block");
17089         if(!this.loadMask){
17090             this.maskEl.hide();
17091         }
17092         
17093         this.store = Roo.factory(this.store, Roo.data);
17094         this.store.on('load', this.onLoad, this);
17095         this.store.on('beforeload', this.onBeforeLoad, this);
17096         
17097         this.resize();
17098         
17099         this.cells = this.el.select('.fc-day',true);
17100         //Roo.log(this.cells);
17101         this.textNodes = this.el.query('.fc-day-number');
17102         this.cells.addClassOnOver('fc-state-hover');
17103         
17104         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17105         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17106         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17107         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17108         
17109         this.on('monthchange', this.onMonthChange, this);
17110         
17111         this.update(new Date().clearTime());
17112     },
17113     
17114     resize : function() {
17115         var sz  = this.el.getSize();
17116         
17117         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17118         this.el.select('.fc-day-content div',true).setHeight(34);
17119     },
17120     
17121     
17122     // private
17123     showPrevMonth : function(e){
17124         this.update(this.activeDate.add("mo", -1));
17125     },
17126     showToday : function(e){
17127         this.update(new Date().clearTime());
17128     },
17129     // private
17130     showNextMonth : function(e){
17131         this.update(this.activeDate.add("mo", 1));
17132     },
17133
17134     // private
17135     showPrevYear : function(){
17136         this.update(this.activeDate.add("y", -1));
17137     },
17138
17139     // private
17140     showNextYear : function(){
17141         this.update(this.activeDate.add("y", 1));
17142     },
17143
17144     
17145    // private
17146     update : function(date)
17147     {
17148         var vd = this.activeDate;
17149         this.activeDate = date;
17150 //        if(vd && this.el){
17151 //            var t = date.getTime();
17152 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17153 //                Roo.log('using add remove');
17154 //                
17155 //                this.fireEvent('monthchange', this, date);
17156 //                
17157 //                this.cells.removeClass("fc-state-highlight");
17158 //                this.cells.each(function(c){
17159 //                   if(c.dateValue == t){
17160 //                       c.addClass("fc-state-highlight");
17161 //                       setTimeout(function(){
17162 //                            try{c.dom.firstChild.focus();}catch(e){}
17163 //                       }, 50);
17164 //                       return false;
17165 //                   }
17166 //                   return true;
17167 //                });
17168 //                return;
17169 //            }
17170 //        }
17171         
17172         var days = date.getDaysInMonth();
17173         
17174         var firstOfMonth = date.getFirstDateOfMonth();
17175         var startingPos = firstOfMonth.getDay()-this.startDay;
17176         
17177         if(startingPos < this.startDay){
17178             startingPos += 7;
17179         }
17180         
17181         var pm = date.add(Date.MONTH, -1);
17182         var prevStart = pm.getDaysInMonth()-startingPos;
17183 //        
17184         this.cells = this.el.select('.fc-day',true);
17185         this.textNodes = this.el.query('.fc-day-number');
17186         this.cells.addClassOnOver('fc-state-hover');
17187         
17188         var cells = this.cells.elements;
17189         var textEls = this.textNodes;
17190         
17191         Roo.each(cells, function(cell){
17192             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17193         });
17194         
17195         days += startingPos;
17196
17197         // convert everything to numbers so it's fast
17198         var day = 86400000;
17199         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17200         //Roo.log(d);
17201         //Roo.log(pm);
17202         //Roo.log(prevStart);
17203         
17204         var today = new Date().clearTime().getTime();
17205         var sel = date.clearTime().getTime();
17206         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17207         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17208         var ddMatch = this.disabledDatesRE;
17209         var ddText = this.disabledDatesText;
17210         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17211         var ddaysText = this.disabledDaysText;
17212         var format = this.format;
17213         
17214         var setCellClass = function(cal, cell){
17215             cell.row = 0;
17216             cell.events = [];
17217             cell.more = [];
17218             //Roo.log('set Cell Class');
17219             cell.title = "";
17220             var t = d.getTime();
17221             
17222             //Roo.log(d);
17223             
17224             cell.dateValue = t;
17225             if(t == today){
17226                 cell.className += " fc-today";
17227                 cell.className += " fc-state-highlight";
17228                 cell.title = cal.todayText;
17229             }
17230             if(t == sel){
17231                 // disable highlight in other month..
17232                 //cell.className += " fc-state-highlight";
17233                 
17234             }
17235             // disabling
17236             if(t < min) {
17237                 cell.className = " fc-state-disabled";
17238                 cell.title = cal.minText;
17239                 return;
17240             }
17241             if(t > max) {
17242                 cell.className = " fc-state-disabled";
17243                 cell.title = cal.maxText;
17244                 return;
17245             }
17246             if(ddays){
17247                 if(ddays.indexOf(d.getDay()) != -1){
17248                     cell.title = ddaysText;
17249                     cell.className = " fc-state-disabled";
17250                 }
17251             }
17252             if(ddMatch && format){
17253                 var fvalue = d.dateFormat(format);
17254                 if(ddMatch.test(fvalue)){
17255                     cell.title = ddText.replace("%0", fvalue);
17256                     cell.className = " fc-state-disabled";
17257                 }
17258             }
17259             
17260             if (!cell.initialClassName) {
17261                 cell.initialClassName = cell.dom.className;
17262             }
17263             
17264             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17265         };
17266
17267         var i = 0;
17268         
17269         for(; i < startingPos; i++) {
17270             textEls[i].innerHTML = (++prevStart);
17271             d.setDate(d.getDate()+1);
17272             
17273             cells[i].className = "fc-past fc-other-month";
17274             setCellClass(this, cells[i]);
17275         }
17276         
17277         var intDay = 0;
17278         
17279         for(; i < days; i++){
17280             intDay = i - startingPos + 1;
17281             textEls[i].innerHTML = (intDay);
17282             d.setDate(d.getDate()+1);
17283             
17284             cells[i].className = ''; // "x-date-active";
17285             setCellClass(this, cells[i]);
17286         }
17287         var extraDays = 0;
17288         
17289         for(; i < 42; i++) {
17290             textEls[i].innerHTML = (++extraDays);
17291             d.setDate(d.getDate()+1);
17292             
17293             cells[i].className = "fc-future fc-other-month";
17294             setCellClass(this, cells[i]);
17295         }
17296         
17297         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17298         
17299         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17300         
17301         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17302         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17303         
17304         if(totalRows != 6){
17305             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17306             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17307         }
17308         
17309         this.fireEvent('monthchange', this, date);
17310         
17311         
17312         /*
17313         if(!this.internalRender){
17314             var main = this.el.dom.firstChild;
17315             var w = main.offsetWidth;
17316             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17317             Roo.fly(main).setWidth(w);
17318             this.internalRender = true;
17319             // opera does not respect the auto grow header center column
17320             // then, after it gets a width opera refuses to recalculate
17321             // without a second pass
17322             if(Roo.isOpera && !this.secondPass){
17323                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17324                 this.secondPass = true;
17325                 this.update.defer(10, this, [date]);
17326             }
17327         }
17328         */
17329         
17330     },
17331     
17332     findCell : function(dt) {
17333         dt = dt.clearTime().getTime();
17334         var ret = false;
17335         this.cells.each(function(c){
17336             //Roo.log("check " +c.dateValue + '?=' + dt);
17337             if(c.dateValue == dt){
17338                 ret = c;
17339                 return false;
17340             }
17341             return true;
17342         });
17343         
17344         return ret;
17345     },
17346     
17347     findCells : function(ev) {
17348         var s = ev.start.clone().clearTime().getTime();
17349        // Roo.log(s);
17350         var e= ev.end.clone().clearTime().getTime();
17351        // Roo.log(e);
17352         var ret = [];
17353         this.cells.each(function(c){
17354              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17355             
17356             if(c.dateValue > e){
17357                 return ;
17358             }
17359             if(c.dateValue < s){
17360                 return ;
17361             }
17362             ret.push(c);
17363         });
17364         
17365         return ret;    
17366     },
17367     
17368 //    findBestRow: function(cells)
17369 //    {
17370 //        var ret = 0;
17371 //        
17372 //        for (var i =0 ; i < cells.length;i++) {
17373 //            ret  = Math.max(cells[i].rows || 0,ret);
17374 //        }
17375 //        return ret;
17376 //        
17377 //    },
17378     
17379     
17380     addItem : function(ev)
17381     {
17382         // look for vertical location slot in
17383         var cells = this.findCells(ev);
17384         
17385 //        ev.row = this.findBestRow(cells);
17386         
17387         // work out the location.
17388         
17389         var crow = false;
17390         var rows = [];
17391         for(var i =0; i < cells.length; i++) {
17392             
17393             cells[i].row = cells[0].row;
17394             
17395             if(i == 0){
17396                 cells[i].row = cells[i].row + 1;
17397             }
17398             
17399             if (!crow) {
17400                 crow = {
17401                     start : cells[i],
17402                     end :  cells[i]
17403                 };
17404                 continue;
17405             }
17406             if (crow.start.getY() == cells[i].getY()) {
17407                 // on same row.
17408                 crow.end = cells[i];
17409                 continue;
17410             }
17411             // different row.
17412             rows.push(crow);
17413             crow = {
17414                 start: cells[i],
17415                 end : cells[i]
17416             };
17417             
17418         }
17419         
17420         rows.push(crow);
17421         ev.els = [];
17422         ev.rows = rows;
17423         ev.cells = cells;
17424         
17425         cells[0].events.push(ev);
17426         
17427         this.calevents.push(ev);
17428     },
17429     
17430     clearEvents: function() {
17431         
17432         if(!this.calevents){
17433             return;
17434         }
17435         
17436         Roo.each(this.cells.elements, function(c){
17437             c.row = 0;
17438             c.events = [];
17439             c.more = [];
17440         });
17441         
17442         Roo.each(this.calevents, function(e) {
17443             Roo.each(e.els, function(el) {
17444                 el.un('mouseenter' ,this.onEventEnter, this);
17445                 el.un('mouseleave' ,this.onEventLeave, this);
17446                 el.remove();
17447             },this);
17448         },this);
17449         
17450         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17451             e.remove();
17452         });
17453         
17454     },
17455     
17456     renderEvents: function()
17457     {   
17458         var _this = this;
17459         
17460         this.cells.each(function(c) {
17461             
17462             if(c.row < 5){
17463                 return;
17464             }
17465             
17466             var ev = c.events;
17467             
17468             var r = 4;
17469             if(c.row != c.events.length){
17470                 r = 4 - (4 - (c.row - c.events.length));
17471             }
17472             
17473             c.events = ev.slice(0, r);
17474             c.more = ev.slice(r);
17475             
17476             if(c.more.length && c.more.length == 1){
17477                 c.events.push(c.more.pop());
17478             }
17479             
17480             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17481             
17482         });
17483             
17484         this.cells.each(function(c) {
17485             
17486             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17487             
17488             
17489             for (var e = 0; e < c.events.length; e++){
17490                 var ev = c.events[e];
17491                 var rows = ev.rows;
17492                 
17493                 for(var i = 0; i < rows.length; i++) {
17494                 
17495                     // how many rows should it span..
17496
17497                     var  cfg = {
17498                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17499                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17500
17501                         unselectable : "on",
17502                         cn : [
17503                             {
17504                                 cls: 'fc-event-inner',
17505                                 cn : [
17506     //                                {
17507     //                                  tag:'span',
17508     //                                  cls: 'fc-event-time',
17509     //                                  html : cells.length > 1 ? '' : ev.time
17510     //                                },
17511                                     {
17512                                       tag:'span',
17513                                       cls: 'fc-event-title',
17514                                       html : String.format('{0}', ev.title)
17515                                     }
17516
17517
17518                                 ]
17519                             },
17520                             {
17521                                 cls: 'ui-resizable-handle ui-resizable-e',
17522                                 html : '&nbsp;&nbsp;&nbsp'
17523                             }
17524
17525                         ]
17526                     };
17527
17528                     if (i == 0) {
17529                         cfg.cls += ' fc-event-start';
17530                     }
17531                     if ((i+1) == rows.length) {
17532                         cfg.cls += ' fc-event-end';
17533                     }
17534
17535                     var ctr = _this.el.select('.fc-event-container',true).first();
17536                     var cg = ctr.createChild(cfg);
17537
17538                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17539                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17540
17541                     var r = (c.more.length) ? 1 : 0;
17542                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17543                     cg.setWidth(ebox.right - sbox.x -2);
17544
17545                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17546                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17547                     cg.on('click', _this.onEventClick, _this, ev);
17548
17549                     ev.els.push(cg);
17550                     
17551                 }
17552                 
17553             }
17554             
17555             
17556             if(c.more.length){
17557                 var  cfg = {
17558                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17559                     style : 'position: absolute',
17560                     unselectable : "on",
17561                     cn : [
17562                         {
17563                             cls: 'fc-event-inner',
17564                             cn : [
17565                                 {
17566                                   tag:'span',
17567                                   cls: 'fc-event-title',
17568                                   html : 'More'
17569                                 }
17570
17571
17572                             ]
17573                         },
17574                         {
17575                             cls: 'ui-resizable-handle ui-resizable-e',
17576                             html : '&nbsp;&nbsp;&nbsp'
17577                         }
17578
17579                     ]
17580                 };
17581
17582                 var ctr = _this.el.select('.fc-event-container',true).first();
17583                 var cg = ctr.createChild(cfg);
17584
17585                 var sbox = c.select('.fc-day-content',true).first().getBox();
17586                 var ebox = c.select('.fc-day-content',true).first().getBox();
17587                 //Roo.log(cg);
17588                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17589                 cg.setWidth(ebox.right - sbox.x -2);
17590
17591                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17592                 
17593             }
17594             
17595         });
17596         
17597         
17598         
17599     },
17600     
17601     onEventEnter: function (e, el,event,d) {
17602         this.fireEvent('evententer', this, el, event);
17603     },
17604     
17605     onEventLeave: function (e, el,event,d) {
17606         this.fireEvent('eventleave', this, el, event);
17607     },
17608     
17609     onEventClick: function (e, el,event,d) {
17610         this.fireEvent('eventclick', this, el, event);
17611     },
17612     
17613     onMonthChange: function () {
17614         this.store.load();
17615     },
17616     
17617     onMoreEventClick: function(e, el, more)
17618     {
17619         var _this = this;
17620         
17621         this.calpopover.placement = 'right';
17622         this.calpopover.setTitle('More');
17623         
17624         this.calpopover.setContent('');
17625         
17626         var ctr = this.calpopover.el.select('.popover-content', true).first();
17627         
17628         Roo.each(more, function(m){
17629             var cfg = {
17630                 cls : 'fc-event-hori fc-event-draggable',
17631                 html : m.title
17632             };
17633             var cg = ctr.createChild(cfg);
17634             
17635             cg.on('click', _this.onEventClick, _this, m);
17636         });
17637         
17638         this.calpopover.show(el);
17639         
17640         
17641     },
17642     
17643     onLoad: function () 
17644     {   
17645         this.calevents = [];
17646         var cal = this;
17647         
17648         if(this.store.getCount() > 0){
17649             this.store.data.each(function(d){
17650                cal.addItem({
17651                     id : d.data.id,
17652                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17653                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17654                     time : d.data.start_time,
17655                     title : d.data.title,
17656                     description : d.data.description,
17657                     venue : d.data.venue
17658                 });
17659             });
17660         }
17661         
17662         this.renderEvents();
17663         
17664         if(this.calevents.length && this.loadMask){
17665             this.maskEl.hide();
17666         }
17667     },
17668     
17669     onBeforeLoad: function()
17670     {
17671         this.clearEvents();
17672         if(this.loadMask){
17673             this.maskEl.show();
17674         }
17675     }
17676 });
17677
17678  
17679  /*
17680  * - LGPL
17681  *
17682  * element
17683  * 
17684  */
17685
17686 /**
17687  * @class Roo.bootstrap.Popover
17688  * @extends Roo.bootstrap.Component
17689  * Bootstrap Popover class
17690  * @cfg {String} html contents of the popover   (or false to use children..)
17691  * @cfg {String} title of popover (or false to hide)
17692  * @cfg {String} placement how it is placed
17693  * @cfg {String} trigger click || hover (or false to trigger manually)
17694  * @cfg {String} over what (parent or false to trigger manually.)
17695  * @cfg {Number} delay - delay before showing
17696  
17697  * @constructor
17698  * Create a new Popover
17699  * @param {Object} config The config object
17700  */
17701
17702 Roo.bootstrap.Popover = function(config){
17703     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17704     
17705     this.addEvents({
17706         // raw events
17707          /**
17708          * @event show
17709          * After the popover show
17710          * 
17711          * @param {Roo.bootstrap.Popover} this
17712          */
17713         "show" : true,
17714         /**
17715          * @event hide
17716          * After the popover hide
17717          * 
17718          * @param {Roo.bootstrap.Popover} this
17719          */
17720         "hide" : true
17721     });
17722 };
17723
17724 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17725     
17726     title: 'Fill in a title',
17727     html: false,
17728     
17729     placement : 'right',
17730     trigger : 'hover', // hover
17731     
17732     delay : 0,
17733     
17734     over: 'parent',
17735     
17736     can_build_overlaid : false,
17737     
17738     getChildContainer : function()
17739     {
17740         return this.el.select('.popover-content',true).first();
17741     },
17742     
17743     getAutoCreate : function(){
17744          
17745         var cfg = {
17746            cls : 'popover roo-dynamic',
17747            style: 'display:block',
17748            cn : [
17749                 {
17750                     cls : 'arrow'
17751                 },
17752                 {
17753                     cls : 'popover-inner',
17754                     cn : [
17755                         {
17756                             tag: 'h3',
17757                             cls: 'popover-title popover-header',
17758                             html : this.title
17759                         },
17760                         {
17761                             cls : 'popover-content popover-body',
17762                             html : this.html
17763                         }
17764                     ]
17765                     
17766                 }
17767            ]
17768         };
17769         
17770         return cfg;
17771     },
17772     setTitle: function(str)
17773     {
17774         this.title = str;
17775         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17776     },
17777     setContent: function(str)
17778     {
17779         this.html = str;
17780         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17781     },
17782     // as it get's added to the bottom of the page.
17783     onRender : function(ct, position)
17784     {
17785         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17786         if(!this.el){
17787             var cfg = Roo.apply({},  this.getAutoCreate());
17788             cfg.id = Roo.id();
17789             
17790             if (this.cls) {
17791                 cfg.cls += ' ' + this.cls;
17792             }
17793             if (this.style) {
17794                 cfg.style = this.style;
17795             }
17796             //Roo.log("adding to ");
17797             this.el = Roo.get(document.body).createChild(cfg, position);
17798 //            Roo.log(this.el);
17799         }
17800         this.initEvents();
17801     },
17802     
17803     initEvents : function()
17804     {
17805         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17806         this.el.enableDisplayMode('block');
17807         this.el.hide();
17808         if (this.over === false) {
17809             return; 
17810         }
17811         if (this.triggers === false) {
17812             return;
17813         }
17814         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17815         var triggers = this.trigger ? this.trigger.split(' ') : [];
17816         Roo.each(triggers, function(trigger) {
17817         
17818             if (trigger == 'click') {
17819                 on_el.on('click', this.toggle, this);
17820             } else if (trigger != 'manual') {
17821                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17822                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17823       
17824                 on_el.on(eventIn  ,this.enter, this);
17825                 on_el.on(eventOut, this.leave, this);
17826             }
17827         }, this);
17828         
17829     },
17830     
17831     
17832     // private
17833     timeout : null,
17834     hoverState : null,
17835     
17836     toggle : function () {
17837         this.hoverState == 'in' ? this.leave() : this.enter();
17838     },
17839     
17840     enter : function () {
17841         
17842         clearTimeout(this.timeout);
17843     
17844         this.hoverState = 'in';
17845     
17846         if (!this.delay || !this.delay.show) {
17847             this.show();
17848             return;
17849         }
17850         var _t = this;
17851         this.timeout = setTimeout(function () {
17852             if (_t.hoverState == 'in') {
17853                 _t.show();
17854             }
17855         }, this.delay.show)
17856     },
17857     
17858     leave : function() {
17859         clearTimeout(this.timeout);
17860     
17861         this.hoverState = 'out';
17862     
17863         if (!this.delay || !this.delay.hide) {
17864             this.hide();
17865             return;
17866         }
17867         var _t = this;
17868         this.timeout = setTimeout(function () {
17869             if (_t.hoverState == 'out') {
17870                 _t.hide();
17871             }
17872         }, this.delay.hide)
17873     },
17874     
17875     show : function (on_el)
17876     {
17877         if (!on_el) {
17878             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17879         }
17880         
17881         // set content.
17882         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17883         if (this.html !== false) {
17884             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17885         }
17886         this.el.removeClass([
17887             'fade','top','bottom', 'left', 'right','in',
17888             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17889         ]);
17890         if (!this.title.length) {
17891             this.el.select('.popover-title',true).hide();
17892         }
17893         
17894         var placement = typeof this.placement == 'function' ?
17895             this.placement.call(this, this.el, on_el) :
17896             this.placement;
17897             
17898         var autoToken = /\s?auto?\s?/i;
17899         var autoPlace = autoToken.test(placement);
17900         if (autoPlace) {
17901             placement = placement.replace(autoToken, '') || 'top';
17902         }
17903         
17904         //this.el.detach()
17905         //this.el.setXY([0,0]);
17906         this.el.show();
17907         this.el.dom.style.display='block';
17908         this.el.addClass(placement);
17909         
17910         //this.el.appendTo(on_el);
17911         
17912         var p = this.getPosition();
17913         var box = this.el.getBox();
17914         
17915         if (autoPlace) {
17916             // fixme..
17917         }
17918         var align = Roo.bootstrap.Popover.alignment[placement];
17919         
17920 //        Roo.log(align);
17921         this.el.alignTo(on_el, align[0],align[1]);
17922         //var arrow = this.el.select('.arrow',true).first();
17923         //arrow.set(align[2], 
17924         
17925         this.el.addClass('in');
17926         
17927         
17928         if (this.el.hasClass('fade')) {
17929             // fade it?
17930         }
17931         
17932         this.hoverState = 'in';
17933         
17934         this.fireEvent('show', this);
17935         
17936     },
17937     hide : function()
17938     {
17939         this.el.setXY([0,0]);
17940         this.el.removeClass('in');
17941         this.el.hide();
17942         this.hoverState = null;
17943         
17944         this.fireEvent('hide', this);
17945     }
17946     
17947 });
17948
17949 Roo.bootstrap.Popover.alignment = {
17950     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17951     'right' : ['l-r', [10,0], 'left bs-popover-left'],
17952     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17953     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17954 };
17955
17956  /*
17957  * - LGPL
17958  *
17959  * Progress
17960  * 
17961  */
17962
17963 /**
17964  * @class Roo.bootstrap.Progress
17965  * @extends Roo.bootstrap.Component
17966  * Bootstrap Progress class
17967  * @cfg {Boolean} striped striped of the progress bar
17968  * @cfg {Boolean} active animated of the progress bar
17969  * 
17970  * 
17971  * @constructor
17972  * Create a new Progress
17973  * @param {Object} config The config object
17974  */
17975
17976 Roo.bootstrap.Progress = function(config){
17977     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17978 };
17979
17980 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17981     
17982     striped : false,
17983     active: false,
17984     
17985     getAutoCreate : function(){
17986         var cfg = {
17987             tag: 'div',
17988             cls: 'progress'
17989         };
17990         
17991         
17992         if(this.striped){
17993             cfg.cls += ' progress-striped';
17994         }
17995       
17996         if(this.active){
17997             cfg.cls += ' active';
17998         }
17999         
18000         
18001         return cfg;
18002     }
18003    
18004 });
18005
18006  
18007
18008  /*
18009  * - LGPL
18010  *
18011  * ProgressBar
18012  * 
18013  */
18014
18015 /**
18016  * @class Roo.bootstrap.ProgressBar
18017  * @extends Roo.bootstrap.Component
18018  * Bootstrap ProgressBar class
18019  * @cfg {Number} aria_valuenow aria-value now
18020  * @cfg {Number} aria_valuemin aria-value min
18021  * @cfg {Number} aria_valuemax aria-value max
18022  * @cfg {String} label label for the progress bar
18023  * @cfg {String} panel (success | info | warning | danger )
18024  * @cfg {String} role role of the progress bar
18025  * @cfg {String} sr_only text
18026  * 
18027  * 
18028  * @constructor
18029  * Create a new ProgressBar
18030  * @param {Object} config The config object
18031  */
18032
18033 Roo.bootstrap.ProgressBar = function(config){
18034     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18035 };
18036
18037 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18038     
18039     aria_valuenow : 0,
18040     aria_valuemin : 0,
18041     aria_valuemax : 100,
18042     label : false,
18043     panel : false,
18044     role : false,
18045     sr_only: false,
18046     
18047     getAutoCreate : function()
18048     {
18049         
18050         var cfg = {
18051             tag: 'div',
18052             cls: 'progress-bar',
18053             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18054         };
18055         
18056         if(this.sr_only){
18057             cfg.cn = {
18058                 tag: 'span',
18059                 cls: 'sr-only',
18060                 html: this.sr_only
18061             }
18062         }
18063         
18064         if(this.role){
18065             cfg.role = this.role;
18066         }
18067         
18068         if(this.aria_valuenow){
18069             cfg['aria-valuenow'] = this.aria_valuenow;
18070         }
18071         
18072         if(this.aria_valuemin){
18073             cfg['aria-valuemin'] = this.aria_valuemin;
18074         }
18075         
18076         if(this.aria_valuemax){
18077             cfg['aria-valuemax'] = this.aria_valuemax;
18078         }
18079         
18080         if(this.label && !this.sr_only){
18081             cfg.html = this.label;
18082         }
18083         
18084         if(this.panel){
18085             cfg.cls += ' progress-bar-' + this.panel;
18086         }
18087         
18088         return cfg;
18089     },
18090     
18091     update : function(aria_valuenow)
18092     {
18093         this.aria_valuenow = aria_valuenow;
18094         
18095         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18096     }
18097    
18098 });
18099
18100  
18101
18102  /*
18103  * - LGPL
18104  *
18105  * column
18106  * 
18107  */
18108
18109 /**
18110  * @class Roo.bootstrap.TabGroup
18111  * @extends Roo.bootstrap.Column
18112  * Bootstrap Column class
18113  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18114  * @cfg {Boolean} carousel true to make the group behave like a carousel
18115  * @cfg {Boolean} bullets show bullets for the panels
18116  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18117  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18118  * @cfg {Boolean} showarrow (true|false) show arrow default true
18119  * 
18120  * @constructor
18121  * Create a new TabGroup
18122  * @param {Object} config The config object
18123  */
18124
18125 Roo.bootstrap.TabGroup = function(config){
18126     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18127     if (!this.navId) {
18128         this.navId = Roo.id();
18129     }
18130     this.tabs = [];
18131     Roo.bootstrap.TabGroup.register(this);
18132     
18133 };
18134
18135 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18136     
18137     carousel : false,
18138     transition : false,
18139     bullets : 0,
18140     timer : 0,
18141     autoslide : false,
18142     slideFn : false,
18143     slideOnTouch : false,
18144     showarrow : true,
18145     
18146     getAutoCreate : function()
18147     {
18148         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18149         
18150         cfg.cls += ' tab-content';
18151         
18152         if (this.carousel) {
18153             cfg.cls += ' carousel slide';
18154             
18155             cfg.cn = [{
18156                cls : 'carousel-inner',
18157                cn : []
18158             }];
18159         
18160             if(this.bullets  && !Roo.isTouch){
18161                 
18162                 var bullets = {
18163                     cls : 'carousel-bullets',
18164                     cn : []
18165                 };
18166                
18167                 if(this.bullets_cls){
18168                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18169                 }
18170                 
18171                 bullets.cn.push({
18172                     cls : 'clear'
18173                 });
18174                 
18175                 cfg.cn[0].cn.push(bullets);
18176             }
18177             
18178             if(this.showarrow){
18179                 cfg.cn[0].cn.push({
18180                     tag : 'div',
18181                     class : 'carousel-arrow',
18182                     cn : [
18183                         {
18184                             tag : 'div',
18185                             class : 'carousel-prev',
18186                             cn : [
18187                                 {
18188                                     tag : 'i',
18189                                     class : 'fa fa-chevron-left'
18190                                 }
18191                             ]
18192                         },
18193                         {
18194                             tag : 'div',
18195                             class : 'carousel-next',
18196                             cn : [
18197                                 {
18198                                     tag : 'i',
18199                                     class : 'fa fa-chevron-right'
18200                                 }
18201                             ]
18202                         }
18203                     ]
18204                 });
18205             }
18206             
18207         }
18208         
18209         return cfg;
18210     },
18211     
18212     initEvents:  function()
18213     {
18214 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18215 //            this.el.on("touchstart", this.onTouchStart, this);
18216 //        }
18217         
18218         if(this.autoslide){
18219             var _this = this;
18220             
18221             this.slideFn = window.setInterval(function() {
18222                 _this.showPanelNext();
18223             }, this.timer);
18224         }
18225         
18226         if(this.showarrow){
18227             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18228             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18229         }
18230         
18231         
18232     },
18233     
18234 //    onTouchStart : function(e, el, o)
18235 //    {
18236 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18237 //            return;
18238 //        }
18239 //        
18240 //        this.showPanelNext();
18241 //    },
18242     
18243     
18244     getChildContainer : function()
18245     {
18246         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18247     },
18248     
18249     /**
18250     * register a Navigation item
18251     * @param {Roo.bootstrap.NavItem} the navitem to add
18252     */
18253     register : function(item)
18254     {
18255         this.tabs.push( item);
18256         item.navId = this.navId; // not really needed..
18257         this.addBullet();
18258     
18259     },
18260     
18261     getActivePanel : function()
18262     {
18263         var r = false;
18264         Roo.each(this.tabs, function(t) {
18265             if (t.active) {
18266                 r = t;
18267                 return false;
18268             }
18269             return null;
18270         });
18271         return r;
18272         
18273     },
18274     getPanelByName : function(n)
18275     {
18276         var r = false;
18277         Roo.each(this.tabs, function(t) {
18278             if (t.tabId == n) {
18279                 r = t;
18280                 return false;
18281             }
18282             return null;
18283         });
18284         return r;
18285     },
18286     indexOfPanel : function(p)
18287     {
18288         var r = false;
18289         Roo.each(this.tabs, function(t,i) {
18290             if (t.tabId == p.tabId) {
18291                 r = i;
18292                 return false;
18293             }
18294             return null;
18295         });
18296         return r;
18297     },
18298     /**
18299      * show a specific panel
18300      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18301      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18302      */
18303     showPanel : function (pan)
18304     {
18305         if(this.transition || typeof(pan) == 'undefined'){
18306             Roo.log("waiting for the transitionend");
18307             return;
18308         }
18309         
18310         if (typeof(pan) == 'number') {
18311             pan = this.tabs[pan];
18312         }
18313         
18314         if (typeof(pan) == 'string') {
18315             pan = this.getPanelByName(pan);
18316         }
18317         
18318         var cur = this.getActivePanel();
18319         
18320         if(!pan || !cur){
18321             Roo.log('pan or acitve pan is undefined');
18322             return false;
18323         }
18324         
18325         if (pan.tabId == this.getActivePanel().tabId) {
18326             return true;
18327         }
18328         
18329         if (false === cur.fireEvent('beforedeactivate')) {
18330             return false;
18331         }
18332         
18333         if(this.bullets > 0 && !Roo.isTouch){
18334             this.setActiveBullet(this.indexOfPanel(pan));
18335         }
18336         
18337         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18338             
18339             this.transition = true;
18340             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18341             var lr = dir == 'next' ? 'left' : 'right';
18342             pan.el.addClass(dir); // or prev
18343             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18344             cur.el.addClass(lr); // or right
18345             pan.el.addClass(lr);
18346             
18347             var _this = this;
18348             cur.el.on('transitionend', function() {
18349                 Roo.log("trans end?");
18350                 
18351                 pan.el.removeClass([lr,dir]);
18352                 pan.setActive(true);
18353                 
18354                 cur.el.removeClass([lr]);
18355                 cur.setActive(false);
18356                 
18357                 _this.transition = false;
18358                 
18359             }, this, { single:  true } );
18360             
18361             return true;
18362         }
18363         
18364         cur.setActive(false);
18365         pan.setActive(true);
18366         
18367         return true;
18368         
18369     },
18370     showPanelNext : function()
18371     {
18372         var i = this.indexOfPanel(this.getActivePanel());
18373         
18374         if (i >= this.tabs.length - 1 && !this.autoslide) {
18375             return;
18376         }
18377         
18378         if (i >= this.tabs.length - 1 && this.autoslide) {
18379             i = -1;
18380         }
18381         
18382         this.showPanel(this.tabs[i+1]);
18383     },
18384     
18385     showPanelPrev : function()
18386     {
18387         var i = this.indexOfPanel(this.getActivePanel());
18388         
18389         if (i  < 1 && !this.autoslide) {
18390             return;
18391         }
18392         
18393         if (i < 1 && this.autoslide) {
18394             i = this.tabs.length;
18395         }
18396         
18397         this.showPanel(this.tabs[i-1]);
18398     },
18399     
18400     
18401     addBullet: function()
18402     {
18403         if(!this.bullets || Roo.isTouch){
18404             return;
18405         }
18406         var ctr = this.el.select('.carousel-bullets',true).first();
18407         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18408         var bullet = ctr.createChild({
18409             cls : 'bullet bullet-' + i
18410         },ctr.dom.lastChild);
18411         
18412         
18413         var _this = this;
18414         
18415         bullet.on('click', (function(e, el, o, ii, t){
18416
18417             e.preventDefault();
18418
18419             this.showPanel(ii);
18420
18421             if(this.autoslide && this.slideFn){
18422                 clearInterval(this.slideFn);
18423                 this.slideFn = window.setInterval(function() {
18424                     _this.showPanelNext();
18425                 }, this.timer);
18426             }
18427
18428         }).createDelegate(this, [i, bullet], true));
18429                 
18430         
18431     },
18432      
18433     setActiveBullet : function(i)
18434     {
18435         if(Roo.isTouch){
18436             return;
18437         }
18438         
18439         Roo.each(this.el.select('.bullet', true).elements, function(el){
18440             el.removeClass('selected');
18441         });
18442
18443         var bullet = this.el.select('.bullet-' + i, true).first();
18444         
18445         if(!bullet){
18446             return;
18447         }
18448         
18449         bullet.addClass('selected');
18450     }
18451     
18452     
18453   
18454 });
18455
18456  
18457
18458  
18459  
18460 Roo.apply(Roo.bootstrap.TabGroup, {
18461     
18462     groups: {},
18463      /**
18464     * register a Navigation Group
18465     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18466     */
18467     register : function(navgrp)
18468     {
18469         this.groups[navgrp.navId] = navgrp;
18470         
18471     },
18472     /**
18473     * fetch a Navigation Group based on the navigation ID
18474     * if one does not exist , it will get created.
18475     * @param {string} the navgroup to add
18476     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18477     */
18478     get: function(navId) {
18479         if (typeof(this.groups[navId]) == 'undefined') {
18480             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18481         }
18482         return this.groups[navId] ;
18483     }
18484     
18485     
18486     
18487 });
18488
18489  /*
18490  * - LGPL
18491  *
18492  * TabPanel
18493  * 
18494  */
18495
18496 /**
18497  * @class Roo.bootstrap.TabPanel
18498  * @extends Roo.bootstrap.Component
18499  * Bootstrap TabPanel class
18500  * @cfg {Boolean} active panel active
18501  * @cfg {String} html panel content
18502  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18503  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18504  * @cfg {String} href click to link..
18505  * 
18506  * 
18507  * @constructor
18508  * Create a new TabPanel
18509  * @param {Object} config The config object
18510  */
18511
18512 Roo.bootstrap.TabPanel = function(config){
18513     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18514     this.addEvents({
18515         /**
18516              * @event changed
18517              * Fires when the active status changes
18518              * @param {Roo.bootstrap.TabPanel} this
18519              * @param {Boolean} state the new state
18520             
18521          */
18522         'changed': true,
18523         /**
18524              * @event beforedeactivate
18525              * Fires before a tab is de-activated - can be used to do validation on a form.
18526              * @param {Roo.bootstrap.TabPanel} this
18527              * @return {Boolean} false if there is an error
18528             
18529          */
18530         'beforedeactivate': true
18531      });
18532     
18533     this.tabId = this.tabId || Roo.id();
18534   
18535 };
18536
18537 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18538     
18539     active: false,
18540     html: false,
18541     tabId: false,
18542     navId : false,
18543     href : '',
18544     
18545     getAutoCreate : function(){
18546         var cfg = {
18547             tag: 'div',
18548             // item is needed for carousel - not sure if it has any effect otherwise
18549             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18550             html: this.html || ''
18551         };
18552         
18553         if(this.active){
18554             cfg.cls += ' active';
18555         }
18556         
18557         if(this.tabId){
18558             cfg.tabId = this.tabId;
18559         }
18560         
18561         
18562         return cfg;
18563     },
18564     
18565     initEvents:  function()
18566     {
18567         var p = this.parent();
18568         
18569         this.navId = this.navId || p.navId;
18570         
18571         if (typeof(this.navId) != 'undefined') {
18572             // not really needed.. but just in case.. parent should be a NavGroup.
18573             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18574             
18575             tg.register(this);
18576             
18577             var i = tg.tabs.length - 1;
18578             
18579             if(this.active && tg.bullets > 0 && i < tg.bullets){
18580                 tg.setActiveBullet(i);
18581             }
18582         }
18583         
18584         this.el.on('click', this.onClick, this);
18585         
18586         if(Roo.isTouch){
18587             this.el.on("touchstart", this.onTouchStart, this);
18588             this.el.on("touchmove", this.onTouchMove, this);
18589             this.el.on("touchend", this.onTouchEnd, this);
18590         }
18591         
18592     },
18593     
18594     onRender : function(ct, position)
18595     {
18596         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18597     },
18598     
18599     setActive : function(state)
18600     {
18601         Roo.log("panel - set active " + this.tabId + "=" + state);
18602         
18603         this.active = state;
18604         if (!state) {
18605             this.el.removeClass('active');
18606             
18607         } else  if (!this.el.hasClass('active')) {
18608             this.el.addClass('active');
18609         }
18610         
18611         this.fireEvent('changed', this, state);
18612     },
18613     
18614     onClick : function(e)
18615     {
18616         e.preventDefault();
18617         
18618         if(!this.href.length){
18619             return;
18620         }
18621         
18622         window.location.href = this.href;
18623     },
18624     
18625     startX : 0,
18626     startY : 0,
18627     endX : 0,
18628     endY : 0,
18629     swiping : false,
18630     
18631     onTouchStart : function(e)
18632     {
18633         this.swiping = false;
18634         
18635         this.startX = e.browserEvent.touches[0].clientX;
18636         this.startY = e.browserEvent.touches[0].clientY;
18637     },
18638     
18639     onTouchMove : function(e)
18640     {
18641         this.swiping = true;
18642         
18643         this.endX = e.browserEvent.touches[0].clientX;
18644         this.endY = e.browserEvent.touches[0].clientY;
18645     },
18646     
18647     onTouchEnd : function(e)
18648     {
18649         if(!this.swiping){
18650             this.onClick(e);
18651             return;
18652         }
18653         
18654         var tabGroup = this.parent();
18655         
18656         if(this.endX > this.startX){ // swiping right
18657             tabGroup.showPanelPrev();
18658             return;
18659         }
18660         
18661         if(this.startX > this.endX){ // swiping left
18662             tabGroup.showPanelNext();
18663             return;
18664         }
18665     }
18666     
18667     
18668 });
18669  
18670
18671  
18672
18673  /*
18674  * - LGPL
18675  *
18676  * DateField
18677  * 
18678  */
18679
18680 /**
18681  * @class Roo.bootstrap.DateField
18682  * @extends Roo.bootstrap.Input
18683  * Bootstrap DateField class
18684  * @cfg {Number} weekStart default 0
18685  * @cfg {String} viewMode default empty, (months|years)
18686  * @cfg {String} minViewMode default empty, (months|years)
18687  * @cfg {Number} startDate default -Infinity
18688  * @cfg {Number} endDate default Infinity
18689  * @cfg {Boolean} todayHighlight default false
18690  * @cfg {Boolean} todayBtn default false
18691  * @cfg {Boolean} calendarWeeks default false
18692  * @cfg {Object} daysOfWeekDisabled default empty
18693  * @cfg {Boolean} singleMode default false (true | false)
18694  * 
18695  * @cfg {Boolean} keyboardNavigation default true
18696  * @cfg {String} language default en
18697  * 
18698  * @constructor
18699  * Create a new DateField
18700  * @param {Object} config The config object
18701  */
18702
18703 Roo.bootstrap.DateField = function(config){
18704     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18705      this.addEvents({
18706             /**
18707              * @event show
18708              * Fires when this field show.
18709              * @param {Roo.bootstrap.DateField} this
18710              * @param {Mixed} date The date value
18711              */
18712             show : true,
18713             /**
18714              * @event show
18715              * Fires when this field hide.
18716              * @param {Roo.bootstrap.DateField} this
18717              * @param {Mixed} date The date value
18718              */
18719             hide : true,
18720             /**
18721              * @event select
18722              * Fires when select a date.
18723              * @param {Roo.bootstrap.DateField} this
18724              * @param {Mixed} date The date value
18725              */
18726             select : true,
18727             /**
18728              * @event beforeselect
18729              * Fires when before select a date.
18730              * @param {Roo.bootstrap.DateField} this
18731              * @param {Mixed} date The date value
18732              */
18733             beforeselect : true
18734         });
18735 };
18736
18737 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18738     
18739     /**
18740      * @cfg {String} format
18741      * The default date format string which can be overriden for localization support.  The format must be
18742      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18743      */
18744     format : "m/d/y",
18745     /**
18746      * @cfg {String} altFormats
18747      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18748      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18749      */
18750     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18751     
18752     weekStart : 0,
18753     
18754     viewMode : '',
18755     
18756     minViewMode : '',
18757     
18758     todayHighlight : false,
18759     
18760     todayBtn: false,
18761     
18762     language: 'en',
18763     
18764     keyboardNavigation: true,
18765     
18766     calendarWeeks: false,
18767     
18768     startDate: -Infinity,
18769     
18770     endDate: Infinity,
18771     
18772     daysOfWeekDisabled: [],
18773     
18774     _events: [],
18775     
18776     singleMode : false,
18777     
18778     UTCDate: function()
18779     {
18780         return new Date(Date.UTC.apply(Date, arguments));
18781     },
18782     
18783     UTCToday: function()
18784     {
18785         var today = new Date();
18786         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18787     },
18788     
18789     getDate: function() {
18790             var d = this.getUTCDate();
18791             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18792     },
18793     
18794     getUTCDate: function() {
18795             return this.date;
18796     },
18797     
18798     setDate: function(d) {
18799             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18800     },
18801     
18802     setUTCDate: function(d) {
18803             this.date = d;
18804             this.setValue(this.formatDate(this.date));
18805     },
18806         
18807     onRender: function(ct, position)
18808     {
18809         
18810         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18811         
18812         this.language = this.language || 'en';
18813         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18814         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18815         
18816         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18817         this.format = this.format || 'm/d/y';
18818         this.isInline = false;
18819         this.isInput = true;
18820         this.component = this.el.select('.add-on', true).first() || false;
18821         this.component = (this.component && this.component.length === 0) ? false : this.component;
18822         this.hasInput = this.component && this.inputEl().length;
18823         
18824         if (typeof(this.minViewMode === 'string')) {
18825             switch (this.minViewMode) {
18826                 case 'months':
18827                     this.minViewMode = 1;
18828                     break;
18829                 case 'years':
18830                     this.minViewMode = 2;
18831                     break;
18832                 default:
18833                     this.minViewMode = 0;
18834                     break;
18835             }
18836         }
18837         
18838         if (typeof(this.viewMode === 'string')) {
18839             switch (this.viewMode) {
18840                 case 'months':
18841                     this.viewMode = 1;
18842                     break;
18843                 case 'years':
18844                     this.viewMode = 2;
18845                     break;
18846                 default:
18847                     this.viewMode = 0;
18848                     break;
18849             }
18850         }
18851                 
18852         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18853         
18854 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18855         
18856         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18857         
18858         this.picker().on('mousedown', this.onMousedown, this);
18859         this.picker().on('click', this.onClick, this);
18860         
18861         this.picker().addClass('datepicker-dropdown');
18862         
18863         this.startViewMode = this.viewMode;
18864         
18865         if(this.singleMode){
18866             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18867                 v.setVisibilityMode(Roo.Element.DISPLAY);
18868                 v.hide();
18869             });
18870             
18871             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18872                 v.setStyle('width', '189px');
18873             });
18874         }
18875         
18876         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18877             if(!this.calendarWeeks){
18878                 v.remove();
18879                 return;
18880             }
18881             
18882             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18883             v.attr('colspan', function(i, val){
18884                 return parseInt(val) + 1;
18885             });
18886         });
18887                         
18888         
18889         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18890         
18891         this.setStartDate(this.startDate);
18892         this.setEndDate(this.endDate);
18893         
18894         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18895         
18896         this.fillDow();
18897         this.fillMonths();
18898         this.update();
18899         this.showMode();
18900         
18901         if(this.isInline) {
18902             this.showPopup();
18903         }
18904     },
18905     
18906     picker : function()
18907     {
18908         return this.pickerEl;
18909 //        return this.el.select('.datepicker', true).first();
18910     },
18911     
18912     fillDow: function()
18913     {
18914         var dowCnt = this.weekStart;
18915         
18916         var dow = {
18917             tag: 'tr',
18918             cn: [
18919                 
18920             ]
18921         };
18922         
18923         if(this.calendarWeeks){
18924             dow.cn.push({
18925                 tag: 'th',
18926                 cls: 'cw',
18927                 html: '&nbsp;'
18928             })
18929         }
18930         
18931         while (dowCnt < this.weekStart + 7) {
18932             dow.cn.push({
18933                 tag: 'th',
18934                 cls: 'dow',
18935                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18936             });
18937         }
18938         
18939         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18940     },
18941     
18942     fillMonths: function()
18943     {    
18944         var i = 0;
18945         var months = this.picker().select('>.datepicker-months td', true).first();
18946         
18947         months.dom.innerHTML = '';
18948         
18949         while (i < 12) {
18950             var month = {
18951                 tag: 'span',
18952                 cls: 'month',
18953                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18954             };
18955             
18956             months.createChild(month);
18957         }
18958         
18959     },
18960     
18961     update: function()
18962     {
18963         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;
18964         
18965         if (this.date < this.startDate) {
18966             this.viewDate = new Date(this.startDate);
18967         } else if (this.date > this.endDate) {
18968             this.viewDate = new Date(this.endDate);
18969         } else {
18970             this.viewDate = new Date(this.date);
18971         }
18972         
18973         this.fill();
18974     },
18975     
18976     fill: function() 
18977     {
18978         var d = new Date(this.viewDate),
18979                 year = d.getUTCFullYear(),
18980                 month = d.getUTCMonth(),
18981                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18982                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18983                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18984                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18985                 currentDate = this.date && this.date.valueOf(),
18986                 today = this.UTCToday();
18987         
18988         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18989         
18990 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18991         
18992 //        this.picker.select('>tfoot th.today').
18993 //                                              .text(dates[this.language].today)
18994 //                                              .toggle(this.todayBtn !== false);
18995     
18996         this.updateNavArrows();
18997         this.fillMonths();
18998                                                 
18999         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19000         
19001         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19002          
19003         prevMonth.setUTCDate(day);
19004         
19005         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19006         
19007         var nextMonth = new Date(prevMonth);
19008         
19009         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19010         
19011         nextMonth = nextMonth.valueOf();
19012         
19013         var fillMonths = false;
19014         
19015         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19016         
19017         while(prevMonth.valueOf() <= nextMonth) {
19018             var clsName = '';
19019             
19020             if (prevMonth.getUTCDay() === this.weekStart) {
19021                 if(fillMonths){
19022                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19023                 }
19024                     
19025                 fillMonths = {
19026                     tag: 'tr',
19027                     cn: []
19028                 };
19029                 
19030                 if(this.calendarWeeks){
19031                     // ISO 8601: First week contains first thursday.
19032                     // ISO also states week starts on Monday, but we can be more abstract here.
19033                     var
19034                     // Start of current week: based on weekstart/current date
19035                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19036                     // Thursday of this week
19037                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19038                     // First Thursday of year, year from thursday
19039                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19040                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19041                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19042                     
19043                     fillMonths.cn.push({
19044                         tag: 'td',
19045                         cls: 'cw',
19046                         html: calWeek
19047                     });
19048                 }
19049             }
19050             
19051             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19052                 clsName += ' old';
19053             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19054                 clsName += ' new';
19055             }
19056             if (this.todayHighlight &&
19057                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19058                 prevMonth.getUTCMonth() == today.getMonth() &&
19059                 prevMonth.getUTCDate() == today.getDate()) {
19060                 clsName += ' today';
19061             }
19062             
19063             if (currentDate && prevMonth.valueOf() === currentDate) {
19064                 clsName += ' active';
19065             }
19066             
19067             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19068                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19069                     clsName += ' disabled';
19070             }
19071             
19072             fillMonths.cn.push({
19073                 tag: 'td',
19074                 cls: 'day ' + clsName,
19075                 html: prevMonth.getDate()
19076             });
19077             
19078             prevMonth.setDate(prevMonth.getDate()+1);
19079         }
19080           
19081         var currentYear = this.date && this.date.getUTCFullYear();
19082         var currentMonth = this.date && this.date.getUTCMonth();
19083         
19084         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19085         
19086         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19087             v.removeClass('active');
19088             
19089             if(currentYear === year && k === currentMonth){
19090                 v.addClass('active');
19091             }
19092             
19093             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19094                 v.addClass('disabled');
19095             }
19096             
19097         });
19098         
19099         
19100         year = parseInt(year/10, 10) * 10;
19101         
19102         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19103         
19104         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19105         
19106         year -= 1;
19107         for (var i = -1; i < 11; i++) {
19108             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19109                 tag: 'span',
19110                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19111                 html: year
19112             });
19113             
19114             year += 1;
19115         }
19116     },
19117     
19118     showMode: function(dir) 
19119     {
19120         if (dir) {
19121             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19122         }
19123         
19124         Roo.each(this.picker().select('>div',true).elements, function(v){
19125             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19126             v.hide();
19127         });
19128         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19129     },
19130     
19131     place: function()
19132     {
19133         if(this.isInline) {
19134             return;
19135         }
19136         
19137         this.picker().removeClass(['bottom', 'top']);
19138         
19139         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19140             /*
19141              * place to the top of element!
19142              *
19143              */
19144             
19145             this.picker().addClass('top');
19146             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19147             
19148             return;
19149         }
19150         
19151         this.picker().addClass('bottom');
19152         
19153         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19154     },
19155     
19156     parseDate : function(value)
19157     {
19158         if(!value || value instanceof Date){
19159             return value;
19160         }
19161         var v = Date.parseDate(value, this.format);
19162         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19163             v = Date.parseDate(value, 'Y-m-d');
19164         }
19165         if(!v && this.altFormats){
19166             if(!this.altFormatsArray){
19167                 this.altFormatsArray = this.altFormats.split("|");
19168             }
19169             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19170                 v = Date.parseDate(value, this.altFormatsArray[i]);
19171             }
19172         }
19173         return v;
19174     },
19175     
19176     formatDate : function(date, fmt)
19177     {   
19178         return (!date || !(date instanceof Date)) ?
19179         date : date.dateFormat(fmt || this.format);
19180     },
19181     
19182     onFocus : function()
19183     {
19184         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19185         this.showPopup();
19186     },
19187     
19188     onBlur : function()
19189     {
19190         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19191         
19192         var d = this.inputEl().getValue();
19193         
19194         this.setValue(d);
19195                 
19196         this.hidePopup();
19197     },
19198     
19199     showPopup : function()
19200     {
19201         this.picker().show();
19202         this.update();
19203         this.place();
19204         
19205         this.fireEvent('showpopup', this, this.date);
19206     },
19207     
19208     hidePopup : function()
19209     {
19210         if(this.isInline) {
19211             return;
19212         }
19213         this.picker().hide();
19214         this.viewMode = this.startViewMode;
19215         this.showMode();
19216         
19217         this.fireEvent('hidepopup', this, this.date);
19218         
19219     },
19220     
19221     onMousedown: function(e)
19222     {
19223         e.stopPropagation();
19224         e.preventDefault();
19225     },
19226     
19227     keyup: function(e)
19228     {
19229         Roo.bootstrap.DateField.superclass.keyup.call(this);
19230         this.update();
19231     },
19232
19233     setValue: function(v)
19234     {
19235         if(this.fireEvent('beforeselect', this, v) !== false){
19236             var d = new Date(this.parseDate(v) ).clearTime();
19237         
19238             if(isNaN(d.getTime())){
19239                 this.date = this.viewDate = '';
19240                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19241                 return;
19242             }
19243
19244             v = this.formatDate(d);
19245
19246             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19247
19248             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19249
19250             this.update();
19251
19252             this.fireEvent('select', this, this.date);
19253         }
19254     },
19255     
19256     getValue: function()
19257     {
19258         return this.formatDate(this.date);
19259     },
19260     
19261     fireKey: function(e)
19262     {
19263         if (!this.picker().isVisible()){
19264             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19265                 this.showPopup();
19266             }
19267             return;
19268         }
19269         
19270         var dateChanged = false,
19271         dir, day, month,
19272         newDate, newViewDate;
19273         
19274         switch(e.keyCode){
19275             case 27: // escape
19276                 this.hidePopup();
19277                 e.preventDefault();
19278                 break;
19279             case 37: // left
19280             case 39: // right
19281                 if (!this.keyboardNavigation) {
19282                     break;
19283                 }
19284                 dir = e.keyCode == 37 ? -1 : 1;
19285                 
19286                 if (e.ctrlKey){
19287                     newDate = this.moveYear(this.date, dir);
19288                     newViewDate = this.moveYear(this.viewDate, dir);
19289                 } else if (e.shiftKey){
19290                     newDate = this.moveMonth(this.date, dir);
19291                     newViewDate = this.moveMonth(this.viewDate, dir);
19292                 } else {
19293                     newDate = new Date(this.date);
19294                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19295                     newViewDate = new Date(this.viewDate);
19296                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19297                 }
19298                 if (this.dateWithinRange(newDate)){
19299                     this.date = newDate;
19300                     this.viewDate = newViewDate;
19301                     this.setValue(this.formatDate(this.date));
19302 //                    this.update();
19303                     e.preventDefault();
19304                     dateChanged = true;
19305                 }
19306                 break;
19307             case 38: // up
19308             case 40: // down
19309                 if (!this.keyboardNavigation) {
19310                     break;
19311                 }
19312                 dir = e.keyCode == 38 ? -1 : 1;
19313                 if (e.ctrlKey){
19314                     newDate = this.moveYear(this.date, dir);
19315                     newViewDate = this.moveYear(this.viewDate, dir);
19316                 } else if (e.shiftKey){
19317                     newDate = this.moveMonth(this.date, dir);
19318                     newViewDate = this.moveMonth(this.viewDate, dir);
19319                 } else {
19320                     newDate = new Date(this.date);
19321                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19322                     newViewDate = new Date(this.viewDate);
19323                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19324                 }
19325                 if (this.dateWithinRange(newDate)){
19326                     this.date = newDate;
19327                     this.viewDate = newViewDate;
19328                     this.setValue(this.formatDate(this.date));
19329 //                    this.update();
19330                     e.preventDefault();
19331                     dateChanged = true;
19332                 }
19333                 break;
19334             case 13: // enter
19335                 this.setValue(this.formatDate(this.date));
19336                 this.hidePopup();
19337                 e.preventDefault();
19338                 break;
19339             case 9: // tab
19340                 this.setValue(this.formatDate(this.date));
19341                 this.hidePopup();
19342                 break;
19343             case 16: // shift
19344             case 17: // ctrl
19345             case 18: // alt
19346                 break;
19347             default :
19348                 this.hidePopup();
19349                 
19350         }
19351     },
19352     
19353     
19354     onClick: function(e) 
19355     {
19356         e.stopPropagation();
19357         e.preventDefault();
19358         
19359         var target = e.getTarget();
19360         
19361         if(target.nodeName.toLowerCase() === 'i'){
19362             target = Roo.get(target).dom.parentNode;
19363         }
19364         
19365         var nodeName = target.nodeName;
19366         var className = target.className;
19367         var html = target.innerHTML;
19368         //Roo.log(nodeName);
19369         
19370         switch(nodeName.toLowerCase()) {
19371             case 'th':
19372                 switch(className) {
19373                     case 'switch':
19374                         this.showMode(1);
19375                         break;
19376                     case 'prev':
19377                     case 'next':
19378                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19379                         switch(this.viewMode){
19380                                 case 0:
19381                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19382                                         break;
19383                                 case 1:
19384                                 case 2:
19385                                         this.viewDate = this.moveYear(this.viewDate, dir);
19386                                         break;
19387                         }
19388                         this.fill();
19389                         break;
19390                     case 'today':
19391                         var date = new Date();
19392                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19393 //                        this.fill()
19394                         this.setValue(this.formatDate(this.date));
19395                         
19396                         this.hidePopup();
19397                         break;
19398                 }
19399                 break;
19400             case 'span':
19401                 if (className.indexOf('disabled') < 0) {
19402                     this.viewDate.setUTCDate(1);
19403                     if (className.indexOf('month') > -1) {
19404                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19405                     } else {
19406                         var year = parseInt(html, 10) || 0;
19407                         this.viewDate.setUTCFullYear(year);
19408                         
19409                     }
19410                     
19411                     if(this.singleMode){
19412                         this.setValue(this.formatDate(this.viewDate));
19413                         this.hidePopup();
19414                         return;
19415                     }
19416                     
19417                     this.showMode(-1);
19418                     this.fill();
19419                 }
19420                 break;
19421                 
19422             case 'td':
19423                 //Roo.log(className);
19424                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19425                     var day = parseInt(html, 10) || 1;
19426                     var year = this.viewDate.getUTCFullYear(),
19427                         month = this.viewDate.getUTCMonth();
19428
19429                     if (className.indexOf('old') > -1) {
19430                         if(month === 0 ){
19431                             month = 11;
19432                             year -= 1;
19433                         }else{
19434                             month -= 1;
19435                         }
19436                     } else if (className.indexOf('new') > -1) {
19437                         if (month == 11) {
19438                             month = 0;
19439                             year += 1;
19440                         } else {
19441                             month += 1;
19442                         }
19443                     }
19444                     //Roo.log([year,month,day]);
19445                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19446                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19447 //                    this.fill();
19448                     //Roo.log(this.formatDate(this.date));
19449                     this.setValue(this.formatDate(this.date));
19450                     this.hidePopup();
19451                 }
19452                 break;
19453         }
19454     },
19455     
19456     setStartDate: function(startDate)
19457     {
19458         this.startDate = startDate || -Infinity;
19459         if (this.startDate !== -Infinity) {
19460             this.startDate = this.parseDate(this.startDate);
19461         }
19462         this.update();
19463         this.updateNavArrows();
19464     },
19465
19466     setEndDate: function(endDate)
19467     {
19468         this.endDate = endDate || Infinity;
19469         if (this.endDate !== Infinity) {
19470             this.endDate = this.parseDate(this.endDate);
19471         }
19472         this.update();
19473         this.updateNavArrows();
19474     },
19475     
19476     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19477     {
19478         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19479         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19480             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19481         }
19482         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19483             return parseInt(d, 10);
19484         });
19485         this.update();
19486         this.updateNavArrows();
19487     },
19488     
19489     updateNavArrows: function() 
19490     {
19491         if(this.singleMode){
19492             return;
19493         }
19494         
19495         var d = new Date(this.viewDate),
19496         year = d.getUTCFullYear(),
19497         month = d.getUTCMonth();
19498         
19499         Roo.each(this.picker().select('.prev', true).elements, function(v){
19500             v.show();
19501             switch (this.viewMode) {
19502                 case 0:
19503
19504                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19505                         v.hide();
19506                     }
19507                     break;
19508                 case 1:
19509                 case 2:
19510                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19511                         v.hide();
19512                     }
19513                     break;
19514             }
19515         });
19516         
19517         Roo.each(this.picker().select('.next', true).elements, function(v){
19518             v.show();
19519             switch (this.viewMode) {
19520                 case 0:
19521
19522                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19523                         v.hide();
19524                     }
19525                     break;
19526                 case 1:
19527                 case 2:
19528                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19529                         v.hide();
19530                     }
19531                     break;
19532             }
19533         })
19534     },
19535     
19536     moveMonth: function(date, dir)
19537     {
19538         if (!dir) {
19539             return date;
19540         }
19541         var new_date = new Date(date.valueOf()),
19542         day = new_date.getUTCDate(),
19543         month = new_date.getUTCMonth(),
19544         mag = Math.abs(dir),
19545         new_month, test;
19546         dir = dir > 0 ? 1 : -1;
19547         if (mag == 1){
19548             test = dir == -1
19549             // If going back one month, make sure month is not current month
19550             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19551             ? function(){
19552                 return new_date.getUTCMonth() == month;
19553             }
19554             // If going forward one month, make sure month is as expected
19555             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19556             : function(){
19557                 return new_date.getUTCMonth() != new_month;
19558             };
19559             new_month = month + dir;
19560             new_date.setUTCMonth(new_month);
19561             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19562             if (new_month < 0 || new_month > 11) {
19563                 new_month = (new_month + 12) % 12;
19564             }
19565         } else {
19566             // For magnitudes >1, move one month at a time...
19567             for (var i=0; i<mag; i++) {
19568                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19569                 new_date = this.moveMonth(new_date, dir);
19570             }
19571             // ...then reset the day, keeping it in the new month
19572             new_month = new_date.getUTCMonth();
19573             new_date.setUTCDate(day);
19574             test = function(){
19575                 return new_month != new_date.getUTCMonth();
19576             };
19577         }
19578         // Common date-resetting loop -- if date is beyond end of month, make it
19579         // end of month
19580         while (test()){
19581             new_date.setUTCDate(--day);
19582             new_date.setUTCMonth(new_month);
19583         }
19584         return new_date;
19585     },
19586
19587     moveYear: function(date, dir)
19588     {
19589         return this.moveMonth(date, dir*12);
19590     },
19591
19592     dateWithinRange: function(date)
19593     {
19594         return date >= this.startDate && date <= this.endDate;
19595     },
19596
19597     
19598     remove: function() 
19599     {
19600         this.picker().remove();
19601     },
19602     
19603     validateValue : function(value)
19604     {
19605         if(this.getVisibilityEl().hasClass('hidden')){
19606             return true;
19607         }
19608         
19609         if(value.length < 1)  {
19610             if(this.allowBlank){
19611                 return true;
19612             }
19613             return false;
19614         }
19615         
19616         if(value.length < this.minLength){
19617             return false;
19618         }
19619         if(value.length > this.maxLength){
19620             return false;
19621         }
19622         if(this.vtype){
19623             var vt = Roo.form.VTypes;
19624             if(!vt[this.vtype](value, this)){
19625                 return false;
19626             }
19627         }
19628         if(typeof this.validator == "function"){
19629             var msg = this.validator(value);
19630             if(msg !== true){
19631                 return false;
19632             }
19633         }
19634         
19635         if(this.regex && !this.regex.test(value)){
19636             return false;
19637         }
19638         
19639         if(typeof(this.parseDate(value)) == 'undefined'){
19640             return false;
19641         }
19642         
19643         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19644             return false;
19645         }      
19646         
19647         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19648             return false;
19649         } 
19650         
19651         
19652         return true;
19653     },
19654     
19655     reset : function()
19656     {
19657         this.date = this.viewDate = '';
19658         
19659         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19660     }
19661    
19662 });
19663
19664 Roo.apply(Roo.bootstrap.DateField,  {
19665     
19666     head : {
19667         tag: 'thead',
19668         cn: [
19669         {
19670             tag: 'tr',
19671             cn: [
19672             {
19673                 tag: 'th',
19674                 cls: 'prev',
19675                 html: '<i class="fa fa-arrow-left"/>'
19676             },
19677             {
19678                 tag: 'th',
19679                 cls: 'switch',
19680                 colspan: '5'
19681             },
19682             {
19683                 tag: 'th',
19684                 cls: 'next',
19685                 html: '<i class="fa fa-arrow-right"/>'
19686             }
19687
19688             ]
19689         }
19690         ]
19691     },
19692     
19693     content : {
19694         tag: 'tbody',
19695         cn: [
19696         {
19697             tag: 'tr',
19698             cn: [
19699             {
19700                 tag: 'td',
19701                 colspan: '7'
19702             }
19703             ]
19704         }
19705         ]
19706     },
19707     
19708     footer : {
19709         tag: 'tfoot',
19710         cn: [
19711         {
19712             tag: 'tr',
19713             cn: [
19714             {
19715                 tag: 'th',
19716                 colspan: '7',
19717                 cls: 'today'
19718             }
19719                     
19720             ]
19721         }
19722         ]
19723     },
19724     
19725     dates:{
19726         en: {
19727             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19728             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19729             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19730             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19731             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19732             today: "Today"
19733         }
19734     },
19735     
19736     modes: [
19737     {
19738         clsName: 'days',
19739         navFnc: 'Month',
19740         navStep: 1
19741     },
19742     {
19743         clsName: 'months',
19744         navFnc: 'FullYear',
19745         navStep: 1
19746     },
19747     {
19748         clsName: 'years',
19749         navFnc: 'FullYear',
19750         navStep: 10
19751     }]
19752 });
19753
19754 Roo.apply(Roo.bootstrap.DateField,  {
19755   
19756     template : {
19757         tag: 'div',
19758         cls: 'datepicker dropdown-menu roo-dynamic',
19759         cn: [
19760         {
19761             tag: 'div',
19762             cls: 'datepicker-days',
19763             cn: [
19764             {
19765                 tag: 'table',
19766                 cls: 'table-condensed',
19767                 cn:[
19768                 Roo.bootstrap.DateField.head,
19769                 {
19770                     tag: 'tbody'
19771                 },
19772                 Roo.bootstrap.DateField.footer
19773                 ]
19774             }
19775             ]
19776         },
19777         {
19778             tag: 'div',
19779             cls: 'datepicker-months',
19780             cn: [
19781             {
19782                 tag: 'table',
19783                 cls: 'table-condensed',
19784                 cn:[
19785                 Roo.bootstrap.DateField.head,
19786                 Roo.bootstrap.DateField.content,
19787                 Roo.bootstrap.DateField.footer
19788                 ]
19789             }
19790             ]
19791         },
19792         {
19793             tag: 'div',
19794             cls: 'datepicker-years',
19795             cn: [
19796             {
19797                 tag: 'table',
19798                 cls: 'table-condensed',
19799                 cn:[
19800                 Roo.bootstrap.DateField.head,
19801                 Roo.bootstrap.DateField.content,
19802                 Roo.bootstrap.DateField.footer
19803                 ]
19804             }
19805             ]
19806         }
19807         ]
19808     }
19809 });
19810
19811  
19812
19813  /*
19814  * - LGPL
19815  *
19816  * TimeField
19817  * 
19818  */
19819
19820 /**
19821  * @class Roo.bootstrap.TimeField
19822  * @extends Roo.bootstrap.Input
19823  * Bootstrap DateField class
19824  * 
19825  * 
19826  * @constructor
19827  * Create a new TimeField
19828  * @param {Object} config The config object
19829  */
19830
19831 Roo.bootstrap.TimeField = function(config){
19832     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19833     this.addEvents({
19834             /**
19835              * @event show
19836              * Fires when this field show.
19837              * @param {Roo.bootstrap.DateField} thisthis
19838              * @param {Mixed} date The date value
19839              */
19840             show : true,
19841             /**
19842              * @event show
19843              * Fires when this field hide.
19844              * @param {Roo.bootstrap.DateField} this
19845              * @param {Mixed} date The date value
19846              */
19847             hide : true,
19848             /**
19849              * @event select
19850              * Fires when select a date.
19851              * @param {Roo.bootstrap.DateField} this
19852              * @param {Mixed} date The date value
19853              */
19854             select : true
19855         });
19856 };
19857
19858 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19859     
19860     /**
19861      * @cfg {String} format
19862      * The default time format string which can be overriden for localization support.  The format must be
19863      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19864      */
19865     format : "H:i",
19866        
19867     onRender: function(ct, position)
19868     {
19869         
19870         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19871                 
19872         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19873         
19874         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19875         
19876         this.pop = this.picker().select('>.datepicker-time',true).first();
19877         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19878         
19879         this.picker().on('mousedown', this.onMousedown, this);
19880         this.picker().on('click', this.onClick, this);
19881         
19882         this.picker().addClass('datepicker-dropdown');
19883     
19884         this.fillTime();
19885         this.update();
19886             
19887         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19888         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19889         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19890         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19891         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19892         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19893
19894     },
19895     
19896     fireKey: function(e){
19897         if (!this.picker().isVisible()){
19898             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19899                 this.show();
19900             }
19901             return;
19902         }
19903
19904         e.preventDefault();
19905         
19906         switch(e.keyCode){
19907             case 27: // escape
19908                 this.hide();
19909                 break;
19910             case 37: // left
19911             case 39: // right
19912                 this.onTogglePeriod();
19913                 break;
19914             case 38: // up
19915                 this.onIncrementMinutes();
19916                 break;
19917             case 40: // down
19918                 this.onDecrementMinutes();
19919                 break;
19920             case 13: // enter
19921             case 9: // tab
19922                 this.setTime();
19923                 break;
19924         }
19925     },
19926     
19927     onClick: function(e) {
19928         e.stopPropagation();
19929         e.preventDefault();
19930     },
19931     
19932     picker : function()
19933     {
19934         return this.el.select('.datepicker', true).first();
19935     },
19936     
19937     fillTime: function()
19938     {    
19939         var time = this.pop.select('tbody', true).first();
19940         
19941         time.dom.innerHTML = '';
19942         
19943         time.createChild({
19944             tag: 'tr',
19945             cn: [
19946                 {
19947                     tag: 'td',
19948                     cn: [
19949                         {
19950                             tag: 'a',
19951                             href: '#',
19952                             cls: 'btn',
19953                             cn: [
19954                                 {
19955                                     tag: 'span',
19956                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19957                                 }
19958                             ]
19959                         } 
19960                     ]
19961                 },
19962                 {
19963                     tag: 'td',
19964                     cls: 'separator'
19965                 },
19966                 {
19967                     tag: 'td',
19968                     cn: [
19969                         {
19970                             tag: 'a',
19971                             href: '#',
19972                             cls: 'btn',
19973                             cn: [
19974                                 {
19975                                     tag: 'span',
19976                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19977                                 }
19978                             ]
19979                         }
19980                     ]
19981                 },
19982                 {
19983                     tag: 'td',
19984                     cls: 'separator'
19985                 }
19986             ]
19987         });
19988         
19989         time.createChild({
19990             tag: 'tr',
19991             cn: [
19992                 {
19993                     tag: 'td',
19994                     cn: [
19995                         {
19996                             tag: 'span',
19997                             cls: 'timepicker-hour',
19998                             html: '00'
19999                         }  
20000                     ]
20001                 },
20002                 {
20003                     tag: 'td',
20004                     cls: 'separator',
20005                     html: ':'
20006                 },
20007                 {
20008                     tag: 'td',
20009                     cn: [
20010                         {
20011                             tag: 'span',
20012                             cls: 'timepicker-minute',
20013                             html: '00'
20014                         }  
20015                     ]
20016                 },
20017                 {
20018                     tag: 'td',
20019                     cls: 'separator'
20020                 },
20021                 {
20022                     tag: 'td',
20023                     cn: [
20024                         {
20025                             tag: 'button',
20026                             type: 'button',
20027                             cls: 'btn btn-primary period',
20028                             html: 'AM'
20029                             
20030                         }
20031                     ]
20032                 }
20033             ]
20034         });
20035         
20036         time.createChild({
20037             tag: 'tr',
20038             cn: [
20039                 {
20040                     tag: 'td',
20041                     cn: [
20042                         {
20043                             tag: 'a',
20044                             href: '#',
20045                             cls: 'btn',
20046                             cn: [
20047                                 {
20048                                     tag: 'span',
20049                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20050                                 }
20051                             ]
20052                         }
20053                     ]
20054                 },
20055                 {
20056                     tag: 'td',
20057                     cls: 'separator'
20058                 },
20059                 {
20060                     tag: 'td',
20061                     cn: [
20062                         {
20063                             tag: 'a',
20064                             href: '#',
20065                             cls: 'btn',
20066                             cn: [
20067                                 {
20068                                     tag: 'span',
20069                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20070                                 }
20071                             ]
20072                         }
20073                     ]
20074                 },
20075                 {
20076                     tag: 'td',
20077                     cls: 'separator'
20078                 }
20079             ]
20080         });
20081         
20082     },
20083     
20084     update: function()
20085     {
20086         
20087         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20088         
20089         this.fill();
20090     },
20091     
20092     fill: function() 
20093     {
20094         var hours = this.time.getHours();
20095         var minutes = this.time.getMinutes();
20096         var period = 'AM';
20097         
20098         if(hours > 11){
20099             period = 'PM';
20100         }
20101         
20102         if(hours == 0){
20103             hours = 12;
20104         }
20105         
20106         
20107         if(hours > 12){
20108             hours = hours - 12;
20109         }
20110         
20111         if(hours < 10){
20112             hours = '0' + hours;
20113         }
20114         
20115         if(minutes < 10){
20116             minutes = '0' + minutes;
20117         }
20118         
20119         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20120         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20121         this.pop.select('button', true).first().dom.innerHTML = period;
20122         
20123     },
20124     
20125     place: function()
20126     {   
20127         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20128         
20129         var cls = ['bottom'];
20130         
20131         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20132             cls.pop();
20133             cls.push('top');
20134         }
20135         
20136         cls.push('right');
20137         
20138         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20139             cls.pop();
20140             cls.push('left');
20141         }
20142         
20143         this.picker().addClass(cls.join('-'));
20144         
20145         var _this = this;
20146         
20147         Roo.each(cls, function(c){
20148             if(c == 'bottom'){
20149                 _this.picker().setTop(_this.inputEl().getHeight());
20150                 return;
20151             }
20152             if(c == 'top'){
20153                 _this.picker().setTop(0 - _this.picker().getHeight());
20154                 return;
20155             }
20156             
20157             if(c == 'left'){
20158                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20159                 return;
20160             }
20161             if(c == 'right'){
20162                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20163                 return;
20164             }
20165         });
20166         
20167     },
20168   
20169     onFocus : function()
20170     {
20171         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20172         this.show();
20173     },
20174     
20175     onBlur : function()
20176     {
20177         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20178         this.hide();
20179     },
20180     
20181     show : function()
20182     {
20183         this.picker().show();
20184         this.pop.show();
20185         this.update();
20186         this.place();
20187         
20188         this.fireEvent('show', this, this.date);
20189     },
20190     
20191     hide : function()
20192     {
20193         this.picker().hide();
20194         this.pop.hide();
20195         
20196         this.fireEvent('hide', this, this.date);
20197     },
20198     
20199     setTime : function()
20200     {
20201         this.hide();
20202         this.setValue(this.time.format(this.format));
20203         
20204         this.fireEvent('select', this, this.date);
20205         
20206         
20207     },
20208     
20209     onMousedown: function(e){
20210         e.stopPropagation();
20211         e.preventDefault();
20212     },
20213     
20214     onIncrementHours: function()
20215     {
20216         Roo.log('onIncrementHours');
20217         this.time = this.time.add(Date.HOUR, 1);
20218         this.update();
20219         
20220     },
20221     
20222     onDecrementHours: function()
20223     {
20224         Roo.log('onDecrementHours');
20225         this.time = this.time.add(Date.HOUR, -1);
20226         this.update();
20227     },
20228     
20229     onIncrementMinutes: function()
20230     {
20231         Roo.log('onIncrementMinutes');
20232         this.time = this.time.add(Date.MINUTE, 1);
20233         this.update();
20234     },
20235     
20236     onDecrementMinutes: function()
20237     {
20238         Roo.log('onDecrementMinutes');
20239         this.time = this.time.add(Date.MINUTE, -1);
20240         this.update();
20241     },
20242     
20243     onTogglePeriod: function()
20244     {
20245         Roo.log('onTogglePeriod');
20246         this.time = this.time.add(Date.HOUR, 12);
20247         this.update();
20248     }
20249     
20250    
20251 });
20252
20253 Roo.apply(Roo.bootstrap.TimeField,  {
20254     
20255     content : {
20256         tag: 'tbody',
20257         cn: [
20258             {
20259                 tag: 'tr',
20260                 cn: [
20261                 {
20262                     tag: 'td',
20263                     colspan: '7'
20264                 }
20265                 ]
20266             }
20267         ]
20268     },
20269     
20270     footer : {
20271         tag: 'tfoot',
20272         cn: [
20273             {
20274                 tag: 'tr',
20275                 cn: [
20276                 {
20277                     tag: 'th',
20278                     colspan: '7',
20279                     cls: '',
20280                     cn: [
20281                         {
20282                             tag: 'button',
20283                             cls: 'btn btn-info ok',
20284                             html: 'OK'
20285                         }
20286                     ]
20287                 }
20288
20289                 ]
20290             }
20291         ]
20292     }
20293 });
20294
20295 Roo.apply(Roo.bootstrap.TimeField,  {
20296   
20297     template : {
20298         tag: 'div',
20299         cls: 'datepicker dropdown-menu',
20300         cn: [
20301             {
20302                 tag: 'div',
20303                 cls: 'datepicker-time',
20304                 cn: [
20305                 {
20306                     tag: 'table',
20307                     cls: 'table-condensed',
20308                     cn:[
20309                     Roo.bootstrap.TimeField.content,
20310                     Roo.bootstrap.TimeField.footer
20311                     ]
20312                 }
20313                 ]
20314             }
20315         ]
20316     }
20317 });
20318
20319  
20320
20321  /*
20322  * - LGPL
20323  *
20324  * MonthField
20325  * 
20326  */
20327
20328 /**
20329  * @class Roo.bootstrap.MonthField
20330  * @extends Roo.bootstrap.Input
20331  * Bootstrap MonthField class
20332  * 
20333  * @cfg {String} language default en
20334  * 
20335  * @constructor
20336  * Create a new MonthField
20337  * @param {Object} config The config object
20338  */
20339
20340 Roo.bootstrap.MonthField = function(config){
20341     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20342     
20343     this.addEvents({
20344         /**
20345          * @event show
20346          * Fires when this field show.
20347          * @param {Roo.bootstrap.MonthField} this
20348          * @param {Mixed} date The date value
20349          */
20350         show : true,
20351         /**
20352          * @event show
20353          * Fires when this field hide.
20354          * @param {Roo.bootstrap.MonthField} this
20355          * @param {Mixed} date The date value
20356          */
20357         hide : true,
20358         /**
20359          * @event select
20360          * Fires when select a date.
20361          * @param {Roo.bootstrap.MonthField} this
20362          * @param {String} oldvalue The old value
20363          * @param {String} newvalue The new value
20364          */
20365         select : true
20366     });
20367 };
20368
20369 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20370     
20371     onRender: function(ct, position)
20372     {
20373         
20374         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20375         
20376         this.language = this.language || 'en';
20377         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20378         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20379         
20380         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20381         this.isInline = false;
20382         this.isInput = true;
20383         this.component = this.el.select('.add-on', true).first() || false;
20384         this.component = (this.component && this.component.length === 0) ? false : this.component;
20385         this.hasInput = this.component && this.inputEL().length;
20386         
20387         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20388         
20389         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20390         
20391         this.picker().on('mousedown', this.onMousedown, this);
20392         this.picker().on('click', this.onClick, this);
20393         
20394         this.picker().addClass('datepicker-dropdown');
20395         
20396         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20397             v.setStyle('width', '189px');
20398         });
20399         
20400         this.fillMonths();
20401         
20402         this.update();
20403         
20404         if(this.isInline) {
20405             this.show();
20406         }
20407         
20408     },
20409     
20410     setValue: function(v, suppressEvent)
20411     {   
20412         var o = this.getValue();
20413         
20414         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20415         
20416         this.update();
20417
20418         if(suppressEvent !== true){
20419             this.fireEvent('select', this, o, v);
20420         }
20421         
20422     },
20423     
20424     getValue: function()
20425     {
20426         return this.value;
20427     },
20428     
20429     onClick: function(e) 
20430     {
20431         e.stopPropagation();
20432         e.preventDefault();
20433         
20434         var target = e.getTarget();
20435         
20436         if(target.nodeName.toLowerCase() === 'i'){
20437             target = Roo.get(target).dom.parentNode;
20438         }
20439         
20440         var nodeName = target.nodeName;
20441         var className = target.className;
20442         var html = target.innerHTML;
20443         
20444         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20445             return;
20446         }
20447         
20448         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20449         
20450         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20451         
20452         this.hide();
20453                         
20454     },
20455     
20456     picker : function()
20457     {
20458         return this.pickerEl;
20459     },
20460     
20461     fillMonths: function()
20462     {    
20463         var i = 0;
20464         var months = this.picker().select('>.datepicker-months td', true).first();
20465         
20466         months.dom.innerHTML = '';
20467         
20468         while (i < 12) {
20469             var month = {
20470                 tag: 'span',
20471                 cls: 'month',
20472                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20473             };
20474             
20475             months.createChild(month);
20476         }
20477         
20478     },
20479     
20480     update: function()
20481     {
20482         var _this = this;
20483         
20484         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20485             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20486         }
20487         
20488         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20489             e.removeClass('active');
20490             
20491             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20492                 e.addClass('active');
20493             }
20494         })
20495     },
20496     
20497     place: function()
20498     {
20499         if(this.isInline) {
20500             return;
20501         }
20502         
20503         this.picker().removeClass(['bottom', 'top']);
20504         
20505         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20506             /*
20507              * place to the top of element!
20508              *
20509              */
20510             
20511             this.picker().addClass('top');
20512             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20513             
20514             return;
20515         }
20516         
20517         this.picker().addClass('bottom');
20518         
20519         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20520     },
20521     
20522     onFocus : function()
20523     {
20524         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20525         this.show();
20526     },
20527     
20528     onBlur : function()
20529     {
20530         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20531         
20532         var d = this.inputEl().getValue();
20533         
20534         this.setValue(d);
20535                 
20536         this.hide();
20537     },
20538     
20539     show : function()
20540     {
20541         this.picker().show();
20542         this.picker().select('>.datepicker-months', true).first().show();
20543         this.update();
20544         this.place();
20545         
20546         this.fireEvent('show', this, this.date);
20547     },
20548     
20549     hide : function()
20550     {
20551         if(this.isInline) {
20552             return;
20553         }
20554         this.picker().hide();
20555         this.fireEvent('hide', this, this.date);
20556         
20557     },
20558     
20559     onMousedown: function(e)
20560     {
20561         e.stopPropagation();
20562         e.preventDefault();
20563     },
20564     
20565     keyup: function(e)
20566     {
20567         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20568         this.update();
20569     },
20570
20571     fireKey: function(e)
20572     {
20573         if (!this.picker().isVisible()){
20574             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20575                 this.show();
20576             }
20577             return;
20578         }
20579         
20580         var dir;
20581         
20582         switch(e.keyCode){
20583             case 27: // escape
20584                 this.hide();
20585                 e.preventDefault();
20586                 break;
20587             case 37: // left
20588             case 39: // right
20589                 dir = e.keyCode == 37 ? -1 : 1;
20590                 
20591                 this.vIndex = this.vIndex + dir;
20592                 
20593                 if(this.vIndex < 0){
20594                     this.vIndex = 0;
20595                 }
20596                 
20597                 if(this.vIndex > 11){
20598                     this.vIndex = 11;
20599                 }
20600                 
20601                 if(isNaN(this.vIndex)){
20602                     this.vIndex = 0;
20603                 }
20604                 
20605                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20606                 
20607                 break;
20608             case 38: // up
20609             case 40: // down
20610                 
20611                 dir = e.keyCode == 38 ? -1 : 1;
20612                 
20613                 this.vIndex = this.vIndex + dir * 4;
20614                 
20615                 if(this.vIndex < 0){
20616                     this.vIndex = 0;
20617                 }
20618                 
20619                 if(this.vIndex > 11){
20620                     this.vIndex = 11;
20621                 }
20622                 
20623                 if(isNaN(this.vIndex)){
20624                     this.vIndex = 0;
20625                 }
20626                 
20627                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20628                 break;
20629                 
20630             case 13: // enter
20631                 
20632                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20633                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20634                 }
20635                 
20636                 this.hide();
20637                 e.preventDefault();
20638                 break;
20639             case 9: // tab
20640                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20641                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20642                 }
20643                 this.hide();
20644                 break;
20645             case 16: // shift
20646             case 17: // ctrl
20647             case 18: // alt
20648                 break;
20649             default :
20650                 this.hide();
20651                 
20652         }
20653     },
20654     
20655     remove: function() 
20656     {
20657         this.picker().remove();
20658     }
20659    
20660 });
20661
20662 Roo.apply(Roo.bootstrap.MonthField,  {
20663     
20664     content : {
20665         tag: 'tbody',
20666         cn: [
20667         {
20668             tag: 'tr',
20669             cn: [
20670             {
20671                 tag: 'td',
20672                 colspan: '7'
20673             }
20674             ]
20675         }
20676         ]
20677     },
20678     
20679     dates:{
20680         en: {
20681             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20682             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20683         }
20684     }
20685 });
20686
20687 Roo.apply(Roo.bootstrap.MonthField,  {
20688   
20689     template : {
20690         tag: 'div',
20691         cls: 'datepicker dropdown-menu roo-dynamic',
20692         cn: [
20693             {
20694                 tag: 'div',
20695                 cls: 'datepicker-months',
20696                 cn: [
20697                 {
20698                     tag: 'table',
20699                     cls: 'table-condensed',
20700                     cn:[
20701                         Roo.bootstrap.DateField.content
20702                     ]
20703                 }
20704                 ]
20705             }
20706         ]
20707     }
20708 });
20709
20710  
20711
20712  
20713  /*
20714  * - LGPL
20715  *
20716  * CheckBox
20717  * 
20718  */
20719
20720 /**
20721  * @class Roo.bootstrap.CheckBox
20722  * @extends Roo.bootstrap.Input
20723  * Bootstrap CheckBox class
20724  * 
20725  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20726  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20727  * @cfg {String} boxLabel The text that appears beside the checkbox
20728  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20729  * @cfg {Boolean} checked initnal the element
20730  * @cfg {Boolean} inline inline the element (default false)
20731  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20732  * @cfg {String} tooltip label tooltip
20733  * 
20734  * @constructor
20735  * Create a new CheckBox
20736  * @param {Object} config The config object
20737  */
20738
20739 Roo.bootstrap.CheckBox = function(config){
20740     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20741    
20742     this.addEvents({
20743         /**
20744         * @event check
20745         * Fires when the element is checked or unchecked.
20746         * @param {Roo.bootstrap.CheckBox} this This input
20747         * @param {Boolean} checked The new checked value
20748         */
20749        check : true,
20750        /**
20751         * @event click
20752         * Fires when the element is click.
20753         * @param {Roo.bootstrap.CheckBox} this This input
20754         */
20755        click : true
20756     });
20757     
20758 };
20759
20760 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20761   
20762     inputType: 'checkbox',
20763     inputValue: 1,
20764     valueOff: 0,
20765     boxLabel: false,
20766     checked: false,
20767     weight : false,
20768     inline: false,
20769     tooltip : '',
20770     
20771     getAutoCreate : function()
20772     {
20773         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20774         
20775         var id = Roo.id();
20776         
20777         var cfg = {};
20778         
20779         cfg.cls = 'form-group ' + this.inputType; //input-group
20780         
20781         if(this.inline){
20782             cfg.cls += ' ' + this.inputType + '-inline';
20783         }
20784         
20785         var input =  {
20786             tag: 'input',
20787             id : id,
20788             type : this.inputType,
20789             value : this.inputValue,
20790             cls : 'roo-' + this.inputType, //'form-box',
20791             placeholder : this.placeholder || ''
20792             
20793         };
20794         
20795         if(this.inputType != 'radio'){
20796             var hidden =  {
20797                 tag: 'input',
20798                 type : 'hidden',
20799                 cls : 'roo-hidden-value',
20800                 value : this.checked ? this.inputValue : this.valueOff
20801             };
20802         }
20803         
20804             
20805         if (this.weight) { // Validity check?
20806             cfg.cls += " " + this.inputType + "-" + this.weight;
20807         }
20808         
20809         if (this.disabled) {
20810             input.disabled=true;
20811         }
20812         
20813         if(this.checked){
20814             input.checked = this.checked;
20815         }
20816         
20817         if (this.name) {
20818             
20819             input.name = this.name;
20820             
20821             if(this.inputType != 'radio'){
20822                 hidden.name = this.name;
20823                 input.name = '_hidden_' + this.name;
20824             }
20825         }
20826         
20827         if (this.size) {
20828             input.cls += ' input-' + this.size;
20829         }
20830         
20831         var settings=this;
20832         
20833         ['xs','sm','md','lg'].map(function(size){
20834             if (settings[size]) {
20835                 cfg.cls += ' col-' + size + '-' + settings[size];
20836             }
20837         });
20838         
20839         var inputblock = input;
20840          
20841         if (this.before || this.after) {
20842             
20843             inputblock = {
20844                 cls : 'input-group',
20845                 cn :  [] 
20846             };
20847             
20848             if (this.before) {
20849                 inputblock.cn.push({
20850                     tag :'span',
20851                     cls : 'input-group-addon',
20852                     html : this.before
20853                 });
20854             }
20855             
20856             inputblock.cn.push(input);
20857             
20858             if(this.inputType != 'radio'){
20859                 inputblock.cn.push(hidden);
20860             }
20861             
20862             if (this.after) {
20863                 inputblock.cn.push({
20864                     tag :'span',
20865                     cls : 'input-group-addon',
20866                     html : this.after
20867                 });
20868             }
20869             
20870         }
20871         
20872         if (align ==='left' && this.fieldLabel.length) {
20873 //                Roo.log("left and has label");
20874             cfg.cn = [
20875                 {
20876                     tag: 'label',
20877                     'for' :  id,
20878                     cls : 'control-label',
20879                     html : this.fieldLabel
20880                 },
20881                 {
20882                     cls : "", 
20883                     cn: [
20884                         inputblock
20885                     ]
20886                 }
20887             ];
20888             
20889             if(this.labelWidth > 12){
20890                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20891             }
20892             
20893             if(this.labelWidth < 13 && this.labelmd == 0){
20894                 this.labelmd = this.labelWidth;
20895             }
20896             
20897             if(this.labellg > 0){
20898                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20899                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20900             }
20901             
20902             if(this.labelmd > 0){
20903                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20904                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20905             }
20906             
20907             if(this.labelsm > 0){
20908                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20909                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20910             }
20911             
20912             if(this.labelxs > 0){
20913                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20914                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20915             }
20916             
20917         } else if ( this.fieldLabel.length) {
20918 //                Roo.log(" label");
20919                 cfg.cn = [
20920                    
20921                     {
20922                         tag: this.boxLabel ? 'span' : 'label',
20923                         'for': id,
20924                         cls: 'control-label box-input-label',
20925                         //cls : 'input-group-addon',
20926                         html : this.fieldLabel
20927                     },
20928                     
20929                     inputblock
20930                     
20931                 ];
20932
20933         } else {
20934             
20935 //                Roo.log(" no label && no align");
20936                 cfg.cn = [  inputblock ] ;
20937                 
20938                 
20939         }
20940         
20941         if(this.boxLabel){
20942              var boxLabelCfg = {
20943                 tag: 'label',
20944                 //'for': id, // box label is handled by onclick - so no for...
20945                 cls: 'box-label',
20946                 html: this.boxLabel
20947             };
20948             
20949             if(this.tooltip){
20950                 boxLabelCfg.tooltip = this.tooltip;
20951             }
20952              
20953             cfg.cn.push(boxLabelCfg);
20954         }
20955         
20956         if(this.inputType != 'radio'){
20957             cfg.cn.push(hidden);
20958         }
20959         
20960         return cfg;
20961         
20962     },
20963     
20964     /**
20965      * return the real input element.
20966      */
20967     inputEl: function ()
20968     {
20969         return this.el.select('input.roo-' + this.inputType,true).first();
20970     },
20971     hiddenEl: function ()
20972     {
20973         return this.el.select('input.roo-hidden-value',true).first();
20974     },
20975     
20976     labelEl: function()
20977     {
20978         return this.el.select('label.control-label',true).first();
20979     },
20980     /* depricated... */
20981     
20982     label: function()
20983     {
20984         return this.labelEl();
20985     },
20986     
20987     boxLabelEl: function()
20988     {
20989         return this.el.select('label.box-label',true).first();
20990     },
20991     
20992     initEvents : function()
20993     {
20994 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20995         
20996         this.inputEl().on('click', this.onClick,  this);
20997         
20998         if (this.boxLabel) { 
20999             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21000         }
21001         
21002         this.startValue = this.getValue();
21003         
21004         if(this.groupId){
21005             Roo.bootstrap.CheckBox.register(this);
21006         }
21007     },
21008     
21009     onClick : function(e)
21010     {   
21011         if(this.fireEvent('click', this, e) !== false){
21012             this.setChecked(!this.checked);
21013         }
21014         
21015     },
21016     
21017     setChecked : function(state,suppressEvent)
21018     {
21019         this.startValue = this.getValue();
21020
21021         if(this.inputType == 'radio'){
21022             
21023             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21024                 e.dom.checked = false;
21025             });
21026             
21027             this.inputEl().dom.checked = true;
21028             
21029             this.inputEl().dom.value = this.inputValue;
21030             
21031             if(suppressEvent !== true){
21032                 this.fireEvent('check', this, true);
21033             }
21034             
21035             this.validate();
21036             
21037             return;
21038         }
21039         
21040         this.checked = state;
21041         
21042         this.inputEl().dom.checked = state;
21043         
21044         
21045         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21046         
21047         if(suppressEvent !== true){
21048             this.fireEvent('check', this, state);
21049         }
21050         
21051         this.validate();
21052     },
21053     
21054     getValue : function()
21055     {
21056         if(this.inputType == 'radio'){
21057             return this.getGroupValue();
21058         }
21059         
21060         return this.hiddenEl().dom.value;
21061         
21062     },
21063     
21064     getGroupValue : function()
21065     {
21066         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21067             return '';
21068         }
21069         
21070         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21071     },
21072     
21073     setValue : function(v,suppressEvent)
21074     {
21075         if(this.inputType == 'radio'){
21076             this.setGroupValue(v, suppressEvent);
21077             return;
21078         }
21079         
21080         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21081         
21082         this.validate();
21083     },
21084     
21085     setGroupValue : function(v, suppressEvent)
21086     {
21087         this.startValue = this.getValue();
21088         
21089         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21090             e.dom.checked = false;
21091             
21092             if(e.dom.value == v){
21093                 e.dom.checked = true;
21094             }
21095         });
21096         
21097         if(suppressEvent !== true){
21098             this.fireEvent('check', this, true);
21099         }
21100
21101         this.validate();
21102         
21103         return;
21104     },
21105     
21106     validate : function()
21107     {
21108         if(this.getVisibilityEl().hasClass('hidden')){
21109             return true;
21110         }
21111         
21112         if(
21113                 this.disabled || 
21114                 (this.inputType == 'radio' && this.validateRadio()) ||
21115                 (this.inputType == 'checkbox' && this.validateCheckbox())
21116         ){
21117             this.markValid();
21118             return true;
21119         }
21120         
21121         this.markInvalid();
21122         return false;
21123     },
21124     
21125     validateRadio : function()
21126     {
21127         if(this.getVisibilityEl().hasClass('hidden')){
21128             return true;
21129         }
21130         
21131         if(this.allowBlank){
21132             return true;
21133         }
21134         
21135         var valid = false;
21136         
21137         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21138             if(!e.dom.checked){
21139                 return;
21140             }
21141             
21142             valid = true;
21143             
21144             return false;
21145         });
21146         
21147         return valid;
21148     },
21149     
21150     validateCheckbox : function()
21151     {
21152         if(!this.groupId){
21153             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21154             //return (this.getValue() == this.inputValue) ? true : false;
21155         }
21156         
21157         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21158         
21159         if(!group){
21160             return false;
21161         }
21162         
21163         var r = false;
21164         
21165         for(var i in group){
21166             if(group[i].el.isVisible(true)){
21167                 r = false;
21168                 break;
21169             }
21170             
21171             r = true;
21172         }
21173         
21174         for(var i in group){
21175             if(r){
21176                 break;
21177             }
21178             
21179             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21180         }
21181         
21182         return r;
21183     },
21184     
21185     /**
21186      * Mark this field as valid
21187      */
21188     markValid : function()
21189     {
21190         var _this = this;
21191         
21192         this.fireEvent('valid', this);
21193         
21194         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21195         
21196         if(this.groupId){
21197             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21198         }
21199         
21200         if(label){
21201             label.markValid();
21202         }
21203
21204         if(this.inputType == 'radio'){
21205             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21206                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21207                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21208             });
21209             
21210             return;
21211         }
21212
21213         if(!this.groupId){
21214             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21215             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21216             return;
21217         }
21218         
21219         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21220         
21221         if(!group){
21222             return;
21223         }
21224         
21225         for(var i in group){
21226             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21227             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21228         }
21229     },
21230     
21231      /**
21232      * Mark this field as invalid
21233      * @param {String} msg The validation message
21234      */
21235     markInvalid : function(msg)
21236     {
21237         if(this.allowBlank){
21238             return;
21239         }
21240         
21241         var _this = this;
21242         
21243         this.fireEvent('invalid', this, msg);
21244         
21245         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21246         
21247         if(this.groupId){
21248             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21249         }
21250         
21251         if(label){
21252             label.markInvalid();
21253         }
21254             
21255         if(this.inputType == 'radio'){
21256             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21257                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21258                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21259             });
21260             
21261             return;
21262         }
21263         
21264         if(!this.groupId){
21265             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21266             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21267             return;
21268         }
21269         
21270         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21271         
21272         if(!group){
21273             return;
21274         }
21275         
21276         for(var i in group){
21277             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21278             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21279         }
21280         
21281     },
21282     
21283     clearInvalid : function()
21284     {
21285         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21286         
21287         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21288         
21289         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21290         
21291         if (label && label.iconEl) {
21292             label.iconEl.removeClass(label.validClass);
21293             label.iconEl.removeClass(label.invalidClass);
21294         }
21295     },
21296     
21297     disable : function()
21298     {
21299         if(this.inputType != 'radio'){
21300             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21301             return;
21302         }
21303         
21304         var _this = this;
21305         
21306         if(this.rendered){
21307             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21308                 _this.getActionEl().addClass(this.disabledClass);
21309                 e.dom.disabled = true;
21310             });
21311         }
21312         
21313         this.disabled = true;
21314         this.fireEvent("disable", this);
21315         return this;
21316     },
21317
21318     enable : function()
21319     {
21320         if(this.inputType != 'radio'){
21321             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21322             return;
21323         }
21324         
21325         var _this = this;
21326         
21327         if(this.rendered){
21328             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21329                 _this.getActionEl().removeClass(this.disabledClass);
21330                 e.dom.disabled = false;
21331             });
21332         }
21333         
21334         this.disabled = false;
21335         this.fireEvent("enable", this);
21336         return this;
21337     },
21338     
21339     setBoxLabel : function(v)
21340     {
21341         this.boxLabel = v;
21342         
21343         if(this.rendered){
21344             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21345         }
21346     }
21347
21348 });
21349
21350 Roo.apply(Roo.bootstrap.CheckBox, {
21351     
21352     groups: {},
21353     
21354      /**
21355     * register a CheckBox Group
21356     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21357     */
21358     register : function(checkbox)
21359     {
21360         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21361             this.groups[checkbox.groupId] = {};
21362         }
21363         
21364         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21365             return;
21366         }
21367         
21368         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21369         
21370     },
21371     /**
21372     * fetch a CheckBox Group based on the group ID
21373     * @param {string} the group ID
21374     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21375     */
21376     get: function(groupId) {
21377         if (typeof(this.groups[groupId]) == 'undefined') {
21378             return false;
21379         }
21380         
21381         return this.groups[groupId] ;
21382     }
21383     
21384     
21385 });
21386 /*
21387  * - LGPL
21388  *
21389  * RadioItem
21390  * 
21391  */
21392
21393 /**
21394  * @class Roo.bootstrap.Radio
21395  * @extends Roo.bootstrap.Component
21396  * Bootstrap Radio class
21397  * @cfg {String} boxLabel - the label associated
21398  * @cfg {String} value - the value of radio
21399  * 
21400  * @constructor
21401  * Create a new Radio
21402  * @param {Object} config The config object
21403  */
21404 Roo.bootstrap.Radio = function(config){
21405     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21406     
21407 };
21408
21409 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21410     
21411     boxLabel : '',
21412     
21413     value : '',
21414     
21415     getAutoCreate : function()
21416     {
21417         var cfg = {
21418             tag : 'div',
21419             cls : 'form-group radio',
21420             cn : [
21421                 {
21422                     tag : 'label',
21423                     cls : 'box-label',
21424                     html : this.boxLabel
21425                 }
21426             ]
21427         };
21428         
21429         return cfg;
21430     },
21431     
21432     initEvents : function() 
21433     {
21434         this.parent().register(this);
21435         
21436         this.el.on('click', this.onClick, this);
21437         
21438     },
21439     
21440     onClick : function(e)
21441     {
21442         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21443             this.setChecked(true);
21444         }
21445     },
21446     
21447     setChecked : function(state, suppressEvent)
21448     {
21449         this.parent().setValue(this.value, suppressEvent);
21450         
21451     },
21452     
21453     setBoxLabel : function(v)
21454     {
21455         this.boxLabel = v;
21456         
21457         if(this.rendered){
21458             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21459         }
21460     }
21461     
21462 });
21463  
21464
21465  /*
21466  * - LGPL
21467  *
21468  * Input
21469  * 
21470  */
21471
21472 /**
21473  * @class Roo.bootstrap.SecurePass
21474  * @extends Roo.bootstrap.Input
21475  * Bootstrap SecurePass class
21476  *
21477  * 
21478  * @constructor
21479  * Create a new SecurePass
21480  * @param {Object} config The config object
21481  */
21482  
21483 Roo.bootstrap.SecurePass = function (config) {
21484     // these go here, so the translation tool can replace them..
21485     this.errors = {
21486         PwdEmpty: "Please type a password, and then retype it to confirm.",
21487         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21488         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21489         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21490         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21491         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21492         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21493         TooWeak: "Your password is Too Weak."
21494     },
21495     this.meterLabel = "Password strength:";
21496     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21497     this.meterClass = [
21498         "roo-password-meter-tooweak", 
21499         "roo-password-meter-weak", 
21500         "roo-password-meter-medium", 
21501         "roo-password-meter-strong", 
21502         "roo-password-meter-grey"
21503     ];
21504     
21505     this.errors = {};
21506     
21507     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21508 }
21509
21510 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21511     /**
21512      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21513      * {
21514      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21515      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21516      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21517      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21518      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21519      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21520      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21521      * })
21522      */
21523     // private
21524     
21525     meterWidth: 300,
21526     errorMsg :'',    
21527     errors: false,
21528     imageRoot: '/',
21529     /**
21530      * @cfg {String/Object} Label for the strength meter (defaults to
21531      * 'Password strength:')
21532      */
21533     // private
21534     meterLabel: '',
21535     /**
21536      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21537      * ['Weak', 'Medium', 'Strong'])
21538      */
21539     // private    
21540     pwdStrengths: false,    
21541     // private
21542     strength: 0,
21543     // private
21544     _lastPwd: null,
21545     // private
21546     kCapitalLetter: 0,
21547     kSmallLetter: 1,
21548     kDigit: 2,
21549     kPunctuation: 3,
21550     
21551     insecure: false,
21552     // private
21553     initEvents: function ()
21554     {
21555         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21556
21557         if (this.el.is('input[type=password]') && Roo.isSafari) {
21558             this.el.on('keydown', this.SafariOnKeyDown, this);
21559         }
21560
21561         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21562     },
21563     // private
21564     onRender: function (ct, position)
21565     {
21566         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21567         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21568         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21569
21570         this.trigger.createChild({
21571                    cn: [
21572                     {
21573                     //id: 'PwdMeter',
21574                     tag: 'div',
21575                     cls: 'roo-password-meter-grey col-xs-12',
21576                     style: {
21577                         //width: 0,
21578                         //width: this.meterWidth + 'px'                                                
21579                         }
21580                     },
21581                     {                            
21582                          cls: 'roo-password-meter-text'                          
21583                     }
21584                 ]            
21585         });
21586
21587          
21588         if (this.hideTrigger) {
21589             this.trigger.setDisplayed(false);
21590         }
21591         this.setSize(this.width || '', this.height || '');
21592     },
21593     // private
21594     onDestroy: function ()
21595     {
21596         if (this.trigger) {
21597             this.trigger.removeAllListeners();
21598             this.trigger.remove();
21599         }
21600         if (this.wrap) {
21601             this.wrap.remove();
21602         }
21603         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21604     },
21605     // private
21606     checkStrength: function ()
21607     {
21608         var pwd = this.inputEl().getValue();
21609         if (pwd == this._lastPwd) {
21610             return;
21611         }
21612
21613         var strength;
21614         if (this.ClientSideStrongPassword(pwd)) {
21615             strength = 3;
21616         } else if (this.ClientSideMediumPassword(pwd)) {
21617             strength = 2;
21618         } else if (this.ClientSideWeakPassword(pwd)) {
21619             strength = 1;
21620         } else {
21621             strength = 0;
21622         }
21623         
21624         Roo.log('strength1: ' + strength);
21625         
21626         //var pm = this.trigger.child('div/div/div').dom;
21627         var pm = this.trigger.child('div/div');
21628         pm.removeClass(this.meterClass);
21629         pm.addClass(this.meterClass[strength]);
21630                 
21631         
21632         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21633                 
21634         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21635         
21636         this._lastPwd = pwd;
21637     },
21638     reset: function ()
21639     {
21640         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21641         
21642         this._lastPwd = '';
21643         
21644         var pm = this.trigger.child('div/div');
21645         pm.removeClass(this.meterClass);
21646         pm.addClass('roo-password-meter-grey');        
21647         
21648         
21649         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21650         
21651         pt.innerHTML = '';
21652         this.inputEl().dom.type='password';
21653     },
21654     // private
21655     validateValue: function (value)
21656     {
21657         
21658         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21659             return false;
21660         }
21661         if (value.length == 0) {
21662             if (this.allowBlank) {
21663                 this.clearInvalid();
21664                 return true;
21665             }
21666
21667             this.markInvalid(this.errors.PwdEmpty);
21668             this.errorMsg = this.errors.PwdEmpty;
21669             return false;
21670         }
21671         
21672         if(this.insecure){
21673             return true;
21674         }
21675         
21676         if ('[\x21-\x7e]*'.match(value)) {
21677             this.markInvalid(this.errors.PwdBadChar);
21678             this.errorMsg = this.errors.PwdBadChar;
21679             return false;
21680         }
21681         if (value.length < 6) {
21682             this.markInvalid(this.errors.PwdShort);
21683             this.errorMsg = this.errors.PwdShort;
21684             return false;
21685         }
21686         if (value.length > 16) {
21687             this.markInvalid(this.errors.PwdLong);
21688             this.errorMsg = this.errors.PwdLong;
21689             return false;
21690         }
21691         var strength;
21692         if (this.ClientSideStrongPassword(value)) {
21693             strength = 3;
21694         } else if (this.ClientSideMediumPassword(value)) {
21695             strength = 2;
21696         } else if (this.ClientSideWeakPassword(value)) {
21697             strength = 1;
21698         } else {
21699             strength = 0;
21700         }
21701
21702         
21703         if (strength < 2) {
21704             //this.markInvalid(this.errors.TooWeak);
21705             this.errorMsg = this.errors.TooWeak;
21706             //return false;
21707         }
21708         
21709         
21710         console.log('strength2: ' + strength);
21711         
21712         //var pm = this.trigger.child('div/div/div').dom;
21713         
21714         var pm = this.trigger.child('div/div');
21715         pm.removeClass(this.meterClass);
21716         pm.addClass(this.meterClass[strength]);
21717                 
21718         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21719                 
21720         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21721         
21722         this.errorMsg = ''; 
21723         return true;
21724     },
21725     // private
21726     CharacterSetChecks: function (type)
21727     {
21728         this.type = type;
21729         this.fResult = false;
21730     },
21731     // private
21732     isctype: function (character, type)
21733     {
21734         switch (type) {  
21735             case this.kCapitalLetter:
21736                 if (character >= 'A' && character <= 'Z') {
21737                     return true;
21738                 }
21739                 break;
21740             
21741             case this.kSmallLetter:
21742                 if (character >= 'a' && character <= 'z') {
21743                     return true;
21744                 }
21745                 break;
21746             
21747             case this.kDigit:
21748                 if (character >= '0' && character <= '9') {
21749                     return true;
21750                 }
21751                 break;
21752             
21753             case this.kPunctuation:
21754                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21755                     return true;
21756                 }
21757                 break;
21758             
21759             default:
21760                 return false;
21761         }
21762
21763     },
21764     // private
21765     IsLongEnough: function (pwd, size)
21766     {
21767         return !(pwd == null || isNaN(size) || pwd.length < size);
21768     },
21769     // private
21770     SpansEnoughCharacterSets: function (word, nb)
21771     {
21772         if (!this.IsLongEnough(word, nb))
21773         {
21774             return false;
21775         }
21776
21777         var characterSetChecks = new Array(
21778             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21779             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21780         );
21781         
21782         for (var index = 0; index < word.length; ++index) {
21783             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21784                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21785                     characterSetChecks[nCharSet].fResult = true;
21786                     break;
21787                 }
21788             }
21789         }
21790
21791         var nCharSets = 0;
21792         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21793             if (characterSetChecks[nCharSet].fResult) {
21794                 ++nCharSets;
21795             }
21796         }
21797
21798         if (nCharSets < nb) {
21799             return false;
21800         }
21801         return true;
21802     },
21803     // private
21804     ClientSideStrongPassword: function (pwd)
21805     {
21806         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21807     },
21808     // private
21809     ClientSideMediumPassword: function (pwd)
21810     {
21811         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21812     },
21813     // private
21814     ClientSideWeakPassword: function (pwd)
21815     {
21816         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21817     }
21818           
21819 })//<script type="text/javascript">
21820
21821 /*
21822  * Based  Ext JS Library 1.1.1
21823  * Copyright(c) 2006-2007, Ext JS, LLC.
21824  * LGPL
21825  *
21826  */
21827  
21828 /**
21829  * @class Roo.HtmlEditorCore
21830  * @extends Roo.Component
21831  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21832  *
21833  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21834  */
21835
21836 Roo.HtmlEditorCore = function(config){
21837     
21838     
21839     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21840     
21841     
21842     this.addEvents({
21843         /**
21844          * @event initialize
21845          * Fires when the editor is fully initialized (including the iframe)
21846          * @param {Roo.HtmlEditorCore} this
21847          */
21848         initialize: true,
21849         /**
21850          * @event activate
21851          * Fires when the editor is first receives the focus. Any insertion must wait
21852          * until after this event.
21853          * @param {Roo.HtmlEditorCore} this
21854          */
21855         activate: true,
21856          /**
21857          * @event beforesync
21858          * Fires before the textarea is updated with content from the editor iframe. Return false
21859          * to cancel the sync.
21860          * @param {Roo.HtmlEditorCore} this
21861          * @param {String} html
21862          */
21863         beforesync: true,
21864          /**
21865          * @event beforepush
21866          * Fires before the iframe editor is updated with content from the textarea. Return false
21867          * to cancel the push.
21868          * @param {Roo.HtmlEditorCore} this
21869          * @param {String} html
21870          */
21871         beforepush: true,
21872          /**
21873          * @event sync
21874          * Fires when the textarea is updated with content from the editor iframe.
21875          * @param {Roo.HtmlEditorCore} this
21876          * @param {String} html
21877          */
21878         sync: true,
21879          /**
21880          * @event push
21881          * Fires when the iframe editor is updated with content from the textarea.
21882          * @param {Roo.HtmlEditorCore} this
21883          * @param {String} html
21884          */
21885         push: true,
21886         
21887         /**
21888          * @event editorevent
21889          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21890          * @param {Roo.HtmlEditorCore} this
21891          */
21892         editorevent: true
21893         
21894     });
21895     
21896     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21897     
21898     // defaults : white / black...
21899     this.applyBlacklists();
21900     
21901     
21902     
21903 };
21904
21905
21906 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21907
21908
21909      /**
21910      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21911      */
21912     
21913     owner : false,
21914     
21915      /**
21916      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21917      *                        Roo.resizable.
21918      */
21919     resizable : false,
21920      /**
21921      * @cfg {Number} height (in pixels)
21922      */   
21923     height: 300,
21924    /**
21925      * @cfg {Number} width (in pixels)
21926      */   
21927     width: 500,
21928     
21929     /**
21930      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21931      * 
21932      */
21933     stylesheets: false,
21934     
21935     // id of frame..
21936     frameId: false,
21937     
21938     // private properties
21939     validationEvent : false,
21940     deferHeight: true,
21941     initialized : false,
21942     activated : false,
21943     sourceEditMode : false,
21944     onFocus : Roo.emptyFn,
21945     iframePad:3,
21946     hideMode:'offsets',
21947     
21948     clearUp: true,
21949     
21950     // blacklist + whitelisted elements..
21951     black: false,
21952     white: false,
21953      
21954     bodyCls : '',
21955
21956     /**
21957      * Protected method that will not generally be called directly. It
21958      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21959      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21960      */
21961     getDocMarkup : function(){
21962         // body styles..
21963         var st = '';
21964         
21965         // inherit styels from page...?? 
21966         if (this.stylesheets === false) {
21967             
21968             Roo.get(document.head).select('style').each(function(node) {
21969                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21970             });
21971             
21972             Roo.get(document.head).select('link').each(function(node) { 
21973                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21974             });
21975             
21976         } else if (!this.stylesheets.length) {
21977                 // simple..
21978                 st = '<style type="text/css">' +
21979                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21980                    '</style>';
21981         } else { 
21982             st = '<style type="text/css">' +
21983                     this.stylesheets +
21984                 '</style>';
21985         }
21986         
21987         st +=  '<style type="text/css">' +
21988             'IMG { cursor: pointer } ' +
21989         '</style>';
21990
21991         var cls = 'roo-htmleditor-body';
21992         
21993         if(this.bodyCls.length){
21994             cls += ' ' + this.bodyCls;
21995         }
21996         
21997         return '<html><head>' + st  +
21998             //<style type="text/css">' +
21999             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22000             //'</style>' +
22001             ' </head><body class="' +  cls + '"></body></html>';
22002     },
22003
22004     // private
22005     onRender : function(ct, position)
22006     {
22007         var _t = this;
22008         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22009         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22010         
22011         
22012         this.el.dom.style.border = '0 none';
22013         this.el.dom.setAttribute('tabIndex', -1);
22014         this.el.addClass('x-hidden hide');
22015         
22016         
22017         
22018         if(Roo.isIE){ // fix IE 1px bogus margin
22019             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22020         }
22021        
22022         
22023         this.frameId = Roo.id();
22024         
22025          
22026         
22027         var iframe = this.owner.wrap.createChild({
22028             tag: 'iframe',
22029             cls: 'form-control', // bootstrap..
22030             id: this.frameId,
22031             name: this.frameId,
22032             frameBorder : 'no',
22033             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22034         }, this.el
22035         );
22036         
22037         
22038         this.iframe = iframe.dom;
22039
22040          this.assignDocWin();
22041         
22042         this.doc.designMode = 'on';
22043        
22044         this.doc.open();
22045         this.doc.write(this.getDocMarkup());
22046         this.doc.close();
22047
22048         
22049         var task = { // must defer to wait for browser to be ready
22050             run : function(){
22051                 //console.log("run task?" + this.doc.readyState);
22052                 this.assignDocWin();
22053                 if(this.doc.body || this.doc.readyState == 'complete'){
22054                     try {
22055                         this.doc.designMode="on";
22056                     } catch (e) {
22057                         return;
22058                     }
22059                     Roo.TaskMgr.stop(task);
22060                     this.initEditor.defer(10, this);
22061                 }
22062             },
22063             interval : 10,
22064             duration: 10000,
22065             scope: this
22066         };
22067         Roo.TaskMgr.start(task);
22068
22069     },
22070
22071     // private
22072     onResize : function(w, h)
22073     {
22074          Roo.log('resize: ' +w + ',' + h );
22075         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22076         if(!this.iframe){
22077             return;
22078         }
22079         if(typeof w == 'number'){
22080             
22081             this.iframe.style.width = w + 'px';
22082         }
22083         if(typeof h == 'number'){
22084             
22085             this.iframe.style.height = h + 'px';
22086             if(this.doc){
22087                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22088             }
22089         }
22090         
22091     },
22092
22093     /**
22094      * Toggles the editor between standard and source edit mode.
22095      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22096      */
22097     toggleSourceEdit : function(sourceEditMode){
22098         
22099         this.sourceEditMode = sourceEditMode === true;
22100         
22101         if(this.sourceEditMode){
22102  
22103             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22104             
22105         }else{
22106             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22107             //this.iframe.className = '';
22108             this.deferFocus();
22109         }
22110         //this.setSize(this.owner.wrap.getSize());
22111         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22112     },
22113
22114     
22115   
22116
22117     /**
22118      * Protected method that will not generally be called directly. If you need/want
22119      * custom HTML cleanup, this is the method you should override.
22120      * @param {String} html The HTML to be cleaned
22121      * return {String} The cleaned HTML
22122      */
22123     cleanHtml : function(html){
22124         html = String(html);
22125         if(html.length > 5){
22126             if(Roo.isSafari){ // strip safari nonsense
22127                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22128             }
22129         }
22130         if(html == '&nbsp;'){
22131             html = '';
22132         }
22133         return html;
22134     },
22135
22136     /**
22137      * HTML Editor -> Textarea
22138      * Protected method that will not generally be called directly. Syncs the contents
22139      * of the editor iframe with the textarea.
22140      */
22141     syncValue : function(){
22142         if(this.initialized){
22143             var bd = (this.doc.body || this.doc.documentElement);
22144             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22145             var html = bd.innerHTML;
22146             if(Roo.isSafari){
22147                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22148                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22149                 if(m && m[1]){
22150                     html = '<div style="'+m[0]+'">' + html + '</div>';
22151                 }
22152             }
22153             html = this.cleanHtml(html);
22154             // fix up the special chars.. normaly like back quotes in word...
22155             // however we do not want to do this with chinese..
22156             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22157                 var cc = b.charCodeAt();
22158                 if (
22159                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22160                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22161                     (cc >= 0xf900 && cc < 0xfb00 )
22162                 ) {
22163                         return b;
22164                 }
22165                 return "&#"+cc+";" 
22166             });
22167             if(this.owner.fireEvent('beforesync', this, html) !== false){
22168                 this.el.dom.value = html;
22169                 this.owner.fireEvent('sync', this, html);
22170             }
22171         }
22172     },
22173
22174     /**
22175      * Protected method that will not generally be called directly. Pushes the value of the textarea
22176      * into the iframe editor.
22177      */
22178     pushValue : function(){
22179         if(this.initialized){
22180             var v = this.el.dom.value.trim();
22181             
22182 //            if(v.length < 1){
22183 //                v = '&#160;';
22184 //            }
22185             
22186             if(this.owner.fireEvent('beforepush', this, v) !== false){
22187                 var d = (this.doc.body || this.doc.documentElement);
22188                 d.innerHTML = v;
22189                 this.cleanUpPaste();
22190                 this.el.dom.value = d.innerHTML;
22191                 this.owner.fireEvent('push', this, v);
22192             }
22193         }
22194     },
22195
22196     // private
22197     deferFocus : function(){
22198         this.focus.defer(10, this);
22199     },
22200
22201     // doc'ed in Field
22202     focus : function(){
22203         if(this.win && !this.sourceEditMode){
22204             this.win.focus();
22205         }else{
22206             this.el.focus();
22207         }
22208     },
22209     
22210     assignDocWin: function()
22211     {
22212         var iframe = this.iframe;
22213         
22214          if(Roo.isIE){
22215             this.doc = iframe.contentWindow.document;
22216             this.win = iframe.contentWindow;
22217         } else {
22218 //            if (!Roo.get(this.frameId)) {
22219 //                return;
22220 //            }
22221 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22222 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22223             
22224             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22225                 return;
22226             }
22227             
22228             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22229             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22230         }
22231     },
22232     
22233     // private
22234     initEditor : function(){
22235         //console.log("INIT EDITOR");
22236         this.assignDocWin();
22237         
22238         
22239         
22240         this.doc.designMode="on";
22241         this.doc.open();
22242         this.doc.write(this.getDocMarkup());
22243         this.doc.close();
22244         
22245         var dbody = (this.doc.body || this.doc.documentElement);
22246         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22247         // this copies styles from the containing element into thsi one..
22248         // not sure why we need all of this..
22249         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22250         
22251         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22252         //ss['background-attachment'] = 'fixed'; // w3c
22253         dbody.bgProperties = 'fixed'; // ie
22254         //Roo.DomHelper.applyStyles(dbody, ss);
22255         Roo.EventManager.on(this.doc, {
22256             //'mousedown': this.onEditorEvent,
22257             'mouseup': this.onEditorEvent,
22258             'dblclick': this.onEditorEvent,
22259             'click': this.onEditorEvent,
22260             'keyup': this.onEditorEvent,
22261             buffer:100,
22262             scope: this
22263         });
22264         if(Roo.isGecko){
22265             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22266         }
22267         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22268             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22269         }
22270         this.initialized = true;
22271
22272         this.owner.fireEvent('initialize', this);
22273         this.pushValue();
22274     },
22275
22276     // private
22277     onDestroy : function(){
22278         
22279         
22280         
22281         if(this.rendered){
22282             
22283             //for (var i =0; i < this.toolbars.length;i++) {
22284             //    // fixme - ask toolbars for heights?
22285             //    this.toolbars[i].onDestroy();
22286            // }
22287             
22288             //this.wrap.dom.innerHTML = '';
22289             //this.wrap.remove();
22290         }
22291     },
22292
22293     // private
22294     onFirstFocus : function(){
22295         
22296         this.assignDocWin();
22297         
22298         
22299         this.activated = true;
22300          
22301     
22302         if(Roo.isGecko){ // prevent silly gecko errors
22303             this.win.focus();
22304             var s = this.win.getSelection();
22305             if(!s.focusNode || s.focusNode.nodeType != 3){
22306                 var r = s.getRangeAt(0);
22307                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22308                 r.collapse(true);
22309                 this.deferFocus();
22310             }
22311             try{
22312                 this.execCmd('useCSS', true);
22313                 this.execCmd('styleWithCSS', false);
22314             }catch(e){}
22315         }
22316         this.owner.fireEvent('activate', this);
22317     },
22318
22319     // private
22320     adjustFont: function(btn){
22321         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22322         //if(Roo.isSafari){ // safari
22323         //    adjust *= 2;
22324        // }
22325         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22326         if(Roo.isSafari){ // safari
22327             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22328             v =  (v < 10) ? 10 : v;
22329             v =  (v > 48) ? 48 : v;
22330             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22331             
22332         }
22333         
22334         
22335         v = Math.max(1, v+adjust);
22336         
22337         this.execCmd('FontSize', v  );
22338     },
22339
22340     onEditorEvent : function(e)
22341     {
22342         this.owner.fireEvent('editorevent', this, e);
22343       //  this.updateToolbar();
22344         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22345     },
22346
22347     insertTag : function(tg)
22348     {
22349         // could be a bit smarter... -> wrap the current selected tRoo..
22350         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22351             
22352             range = this.createRange(this.getSelection());
22353             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22354             wrappingNode.appendChild(range.extractContents());
22355             range.insertNode(wrappingNode);
22356
22357             return;
22358             
22359             
22360             
22361         }
22362         this.execCmd("formatblock",   tg);
22363         
22364     },
22365     
22366     insertText : function(txt)
22367     {
22368         
22369         
22370         var range = this.createRange();
22371         range.deleteContents();
22372                //alert(Sender.getAttribute('label'));
22373                
22374         range.insertNode(this.doc.createTextNode(txt));
22375     } ,
22376     
22377      
22378
22379     /**
22380      * Executes a Midas editor command on the editor document and performs necessary focus and
22381      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22382      * @param {String} cmd The Midas command
22383      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22384      */
22385     relayCmd : function(cmd, value){
22386         this.win.focus();
22387         this.execCmd(cmd, value);
22388         this.owner.fireEvent('editorevent', this);
22389         //this.updateToolbar();
22390         this.owner.deferFocus();
22391     },
22392
22393     /**
22394      * Executes a Midas editor command directly on the editor document.
22395      * For visual commands, you should use {@link #relayCmd} instead.
22396      * <b>This should only be called after the editor is initialized.</b>
22397      * @param {String} cmd The Midas command
22398      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22399      */
22400     execCmd : function(cmd, value){
22401         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22402         this.syncValue();
22403     },
22404  
22405  
22406    
22407     /**
22408      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22409      * to insert tRoo.
22410      * @param {String} text | dom node.. 
22411      */
22412     insertAtCursor : function(text)
22413     {
22414         
22415         if(!this.activated){
22416             return;
22417         }
22418         /*
22419         if(Roo.isIE){
22420             this.win.focus();
22421             var r = this.doc.selection.createRange();
22422             if(r){
22423                 r.collapse(true);
22424                 r.pasteHTML(text);
22425                 this.syncValue();
22426                 this.deferFocus();
22427             
22428             }
22429             return;
22430         }
22431         */
22432         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22433             this.win.focus();
22434             
22435             
22436             // from jquery ui (MIT licenced)
22437             var range, node;
22438             var win = this.win;
22439             
22440             if (win.getSelection && win.getSelection().getRangeAt) {
22441                 range = win.getSelection().getRangeAt(0);
22442                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22443                 range.insertNode(node);
22444             } else if (win.document.selection && win.document.selection.createRange) {
22445                 // no firefox support
22446                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22447                 win.document.selection.createRange().pasteHTML(txt);
22448             } else {
22449                 // no firefox support
22450                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22451                 this.execCmd('InsertHTML', txt);
22452             } 
22453             
22454             this.syncValue();
22455             
22456             this.deferFocus();
22457         }
22458     },
22459  // private
22460     mozKeyPress : function(e){
22461         if(e.ctrlKey){
22462             var c = e.getCharCode(), cmd;
22463           
22464             if(c > 0){
22465                 c = String.fromCharCode(c).toLowerCase();
22466                 switch(c){
22467                     case 'b':
22468                         cmd = 'bold';
22469                         break;
22470                     case 'i':
22471                         cmd = 'italic';
22472                         break;
22473                     
22474                     case 'u':
22475                         cmd = 'underline';
22476                         break;
22477                     
22478                     case 'v':
22479                         this.cleanUpPaste.defer(100, this);
22480                         return;
22481                         
22482                 }
22483                 if(cmd){
22484                     this.win.focus();
22485                     this.execCmd(cmd);
22486                     this.deferFocus();
22487                     e.preventDefault();
22488                 }
22489                 
22490             }
22491         }
22492     },
22493
22494     // private
22495     fixKeys : function(){ // load time branching for fastest keydown performance
22496         if(Roo.isIE){
22497             return function(e){
22498                 var k = e.getKey(), r;
22499                 if(k == e.TAB){
22500                     e.stopEvent();
22501                     r = this.doc.selection.createRange();
22502                     if(r){
22503                         r.collapse(true);
22504                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22505                         this.deferFocus();
22506                     }
22507                     return;
22508                 }
22509                 
22510                 if(k == e.ENTER){
22511                     r = this.doc.selection.createRange();
22512                     if(r){
22513                         var target = r.parentElement();
22514                         if(!target || target.tagName.toLowerCase() != 'li'){
22515                             e.stopEvent();
22516                             r.pasteHTML('<br />');
22517                             r.collapse(false);
22518                             r.select();
22519                         }
22520                     }
22521                 }
22522                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22523                     this.cleanUpPaste.defer(100, this);
22524                     return;
22525                 }
22526                 
22527                 
22528             };
22529         }else if(Roo.isOpera){
22530             return function(e){
22531                 var k = e.getKey();
22532                 if(k == e.TAB){
22533                     e.stopEvent();
22534                     this.win.focus();
22535                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22536                     this.deferFocus();
22537                 }
22538                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22539                     this.cleanUpPaste.defer(100, this);
22540                     return;
22541                 }
22542                 
22543             };
22544         }else if(Roo.isSafari){
22545             return function(e){
22546                 var k = e.getKey();
22547                 
22548                 if(k == e.TAB){
22549                     e.stopEvent();
22550                     this.execCmd('InsertText','\t');
22551                     this.deferFocus();
22552                     return;
22553                 }
22554                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22555                     this.cleanUpPaste.defer(100, this);
22556                     return;
22557                 }
22558                 
22559              };
22560         }
22561     }(),
22562     
22563     getAllAncestors: function()
22564     {
22565         var p = this.getSelectedNode();
22566         var a = [];
22567         if (!p) {
22568             a.push(p); // push blank onto stack..
22569             p = this.getParentElement();
22570         }
22571         
22572         
22573         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22574             a.push(p);
22575             p = p.parentNode;
22576         }
22577         a.push(this.doc.body);
22578         return a;
22579     },
22580     lastSel : false,
22581     lastSelNode : false,
22582     
22583     
22584     getSelection : function() 
22585     {
22586         this.assignDocWin();
22587         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22588     },
22589     
22590     getSelectedNode: function() 
22591     {
22592         // this may only work on Gecko!!!
22593         
22594         // should we cache this!!!!
22595         
22596         
22597         
22598          
22599         var range = this.createRange(this.getSelection()).cloneRange();
22600         
22601         if (Roo.isIE) {
22602             var parent = range.parentElement();
22603             while (true) {
22604                 var testRange = range.duplicate();
22605                 testRange.moveToElementText(parent);
22606                 if (testRange.inRange(range)) {
22607                     break;
22608                 }
22609                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22610                     break;
22611                 }
22612                 parent = parent.parentElement;
22613             }
22614             return parent;
22615         }
22616         
22617         // is ancestor a text element.
22618         var ac =  range.commonAncestorContainer;
22619         if (ac.nodeType == 3) {
22620             ac = ac.parentNode;
22621         }
22622         
22623         var ar = ac.childNodes;
22624          
22625         var nodes = [];
22626         var other_nodes = [];
22627         var has_other_nodes = false;
22628         for (var i=0;i<ar.length;i++) {
22629             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22630                 continue;
22631             }
22632             // fullly contained node.
22633             
22634             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22635                 nodes.push(ar[i]);
22636                 continue;
22637             }
22638             
22639             // probably selected..
22640             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22641                 other_nodes.push(ar[i]);
22642                 continue;
22643             }
22644             // outer..
22645             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22646                 continue;
22647             }
22648             
22649             
22650             has_other_nodes = true;
22651         }
22652         if (!nodes.length && other_nodes.length) {
22653             nodes= other_nodes;
22654         }
22655         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22656             return false;
22657         }
22658         
22659         return nodes[0];
22660     },
22661     createRange: function(sel)
22662     {
22663         // this has strange effects when using with 
22664         // top toolbar - not sure if it's a great idea.
22665         //this.editor.contentWindow.focus();
22666         if (typeof sel != "undefined") {
22667             try {
22668                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22669             } catch(e) {
22670                 return this.doc.createRange();
22671             }
22672         } else {
22673             return this.doc.createRange();
22674         }
22675     },
22676     getParentElement: function()
22677     {
22678         
22679         this.assignDocWin();
22680         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22681         
22682         var range = this.createRange(sel);
22683          
22684         try {
22685             var p = range.commonAncestorContainer;
22686             while (p.nodeType == 3) { // text node
22687                 p = p.parentNode;
22688             }
22689             return p;
22690         } catch (e) {
22691             return null;
22692         }
22693     
22694     },
22695     /***
22696      *
22697      * Range intersection.. the hard stuff...
22698      *  '-1' = before
22699      *  '0' = hits..
22700      *  '1' = after.
22701      *         [ -- selected range --- ]
22702      *   [fail]                        [fail]
22703      *
22704      *    basically..
22705      *      if end is before start or  hits it. fail.
22706      *      if start is after end or hits it fail.
22707      *
22708      *   if either hits (but other is outside. - then it's not 
22709      *   
22710      *    
22711      **/
22712     
22713     
22714     // @see http://www.thismuchiknow.co.uk/?p=64.
22715     rangeIntersectsNode : function(range, node)
22716     {
22717         var nodeRange = node.ownerDocument.createRange();
22718         try {
22719             nodeRange.selectNode(node);
22720         } catch (e) {
22721             nodeRange.selectNodeContents(node);
22722         }
22723     
22724         var rangeStartRange = range.cloneRange();
22725         rangeStartRange.collapse(true);
22726     
22727         var rangeEndRange = range.cloneRange();
22728         rangeEndRange.collapse(false);
22729     
22730         var nodeStartRange = nodeRange.cloneRange();
22731         nodeStartRange.collapse(true);
22732     
22733         var nodeEndRange = nodeRange.cloneRange();
22734         nodeEndRange.collapse(false);
22735     
22736         return rangeStartRange.compareBoundaryPoints(
22737                  Range.START_TO_START, nodeEndRange) == -1 &&
22738                rangeEndRange.compareBoundaryPoints(
22739                  Range.START_TO_START, nodeStartRange) == 1;
22740         
22741          
22742     },
22743     rangeCompareNode : function(range, node)
22744     {
22745         var nodeRange = node.ownerDocument.createRange();
22746         try {
22747             nodeRange.selectNode(node);
22748         } catch (e) {
22749             nodeRange.selectNodeContents(node);
22750         }
22751         
22752         
22753         range.collapse(true);
22754     
22755         nodeRange.collapse(true);
22756      
22757         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22758         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22759          
22760         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22761         
22762         var nodeIsBefore   =  ss == 1;
22763         var nodeIsAfter    = ee == -1;
22764         
22765         if (nodeIsBefore && nodeIsAfter) {
22766             return 0; // outer
22767         }
22768         if (!nodeIsBefore && nodeIsAfter) {
22769             return 1; //right trailed.
22770         }
22771         
22772         if (nodeIsBefore && !nodeIsAfter) {
22773             return 2;  // left trailed.
22774         }
22775         // fully contined.
22776         return 3;
22777     },
22778
22779     // private? - in a new class?
22780     cleanUpPaste :  function()
22781     {
22782         // cleans up the whole document..
22783         Roo.log('cleanuppaste');
22784         
22785         this.cleanUpChildren(this.doc.body);
22786         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22787         if (clean != this.doc.body.innerHTML) {
22788             this.doc.body.innerHTML = clean;
22789         }
22790         
22791     },
22792     
22793     cleanWordChars : function(input) {// change the chars to hex code
22794         var he = Roo.HtmlEditorCore;
22795         
22796         var output = input;
22797         Roo.each(he.swapCodes, function(sw) { 
22798             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22799             
22800             output = output.replace(swapper, sw[1]);
22801         });
22802         
22803         return output;
22804     },
22805     
22806     
22807     cleanUpChildren : function (n)
22808     {
22809         if (!n.childNodes.length) {
22810             return;
22811         }
22812         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22813            this.cleanUpChild(n.childNodes[i]);
22814         }
22815     },
22816     
22817     
22818         
22819     
22820     cleanUpChild : function (node)
22821     {
22822         var ed = this;
22823         //console.log(node);
22824         if (node.nodeName == "#text") {
22825             // clean up silly Windows -- stuff?
22826             return; 
22827         }
22828         if (node.nodeName == "#comment") {
22829             node.parentNode.removeChild(node);
22830             // clean up silly Windows -- stuff?
22831             return; 
22832         }
22833         var lcname = node.tagName.toLowerCase();
22834         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22835         // whitelist of tags..
22836         
22837         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22838             // remove node.
22839             node.parentNode.removeChild(node);
22840             return;
22841             
22842         }
22843         
22844         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22845         
22846         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22847         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22848         
22849         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22850         //    remove_keep_children = true;
22851         //}
22852         
22853         if (remove_keep_children) {
22854             this.cleanUpChildren(node);
22855             // inserts everything just before this node...
22856             while (node.childNodes.length) {
22857                 var cn = node.childNodes[0];
22858                 node.removeChild(cn);
22859                 node.parentNode.insertBefore(cn, node);
22860             }
22861             node.parentNode.removeChild(node);
22862             return;
22863         }
22864         
22865         if (!node.attributes || !node.attributes.length) {
22866             this.cleanUpChildren(node);
22867             return;
22868         }
22869         
22870         function cleanAttr(n,v)
22871         {
22872             
22873             if (v.match(/^\./) || v.match(/^\//)) {
22874                 return;
22875             }
22876             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22877                 return;
22878             }
22879             if (v.match(/^#/)) {
22880                 return;
22881             }
22882 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22883             node.removeAttribute(n);
22884             
22885         }
22886         
22887         var cwhite = this.cwhite;
22888         var cblack = this.cblack;
22889             
22890         function cleanStyle(n,v)
22891         {
22892             if (v.match(/expression/)) { //XSS?? should we even bother..
22893                 node.removeAttribute(n);
22894                 return;
22895             }
22896             
22897             var parts = v.split(/;/);
22898             var clean = [];
22899             
22900             Roo.each(parts, function(p) {
22901                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22902                 if (!p.length) {
22903                     return true;
22904                 }
22905                 var l = p.split(':').shift().replace(/\s+/g,'');
22906                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22907                 
22908                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22909 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22910                     //node.removeAttribute(n);
22911                     return true;
22912                 }
22913                 //Roo.log()
22914                 // only allow 'c whitelisted system attributes'
22915                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22916 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22917                     //node.removeAttribute(n);
22918                     return true;
22919                 }
22920                 
22921                 
22922                  
22923                 
22924                 clean.push(p);
22925                 return true;
22926             });
22927             if (clean.length) { 
22928                 node.setAttribute(n, clean.join(';'));
22929             } else {
22930                 node.removeAttribute(n);
22931             }
22932             
22933         }
22934         
22935         
22936         for (var i = node.attributes.length-1; i > -1 ; i--) {
22937             var a = node.attributes[i];
22938             //console.log(a);
22939             
22940             if (a.name.toLowerCase().substr(0,2)=='on')  {
22941                 node.removeAttribute(a.name);
22942                 continue;
22943             }
22944             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22945                 node.removeAttribute(a.name);
22946                 continue;
22947             }
22948             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22949                 cleanAttr(a.name,a.value); // fixme..
22950                 continue;
22951             }
22952             if (a.name == 'style') {
22953                 cleanStyle(a.name,a.value);
22954                 continue;
22955             }
22956             /// clean up MS crap..
22957             // tecnically this should be a list of valid class'es..
22958             
22959             
22960             if (a.name == 'class') {
22961                 if (a.value.match(/^Mso/)) {
22962                     node.className = '';
22963                 }
22964                 
22965                 if (a.value.match(/^body$/)) {
22966                     node.className = '';
22967                 }
22968                 continue;
22969             }
22970             
22971             // style cleanup!?
22972             // class cleanup?
22973             
22974         }
22975         
22976         
22977         this.cleanUpChildren(node);
22978         
22979         
22980     },
22981     
22982     /**
22983      * Clean up MS wordisms...
22984      */
22985     cleanWord : function(node)
22986     {
22987         
22988         
22989         if (!node) {
22990             this.cleanWord(this.doc.body);
22991             return;
22992         }
22993         if (node.nodeName == "#text") {
22994             // clean up silly Windows -- stuff?
22995             return; 
22996         }
22997         if (node.nodeName == "#comment") {
22998             node.parentNode.removeChild(node);
22999             // clean up silly Windows -- stuff?
23000             return; 
23001         }
23002         
23003         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23004             node.parentNode.removeChild(node);
23005             return;
23006         }
23007         
23008         // remove - but keep children..
23009         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
23010             while (node.childNodes.length) {
23011                 var cn = node.childNodes[0];
23012                 node.removeChild(cn);
23013                 node.parentNode.insertBefore(cn, node);
23014             }
23015             node.parentNode.removeChild(node);
23016             this.iterateChildren(node, this.cleanWord);
23017             return;
23018         }
23019         // clean styles
23020         if (node.className.length) {
23021             
23022             var cn = node.className.split(/\W+/);
23023             var cna = [];
23024             Roo.each(cn, function(cls) {
23025                 if (cls.match(/Mso[a-zA-Z]+/)) {
23026                     return;
23027                 }
23028                 cna.push(cls);
23029             });
23030             node.className = cna.length ? cna.join(' ') : '';
23031             if (!cna.length) {
23032                 node.removeAttribute("class");
23033             }
23034         }
23035         
23036         if (node.hasAttribute("lang")) {
23037             node.removeAttribute("lang");
23038         }
23039         
23040         if (node.hasAttribute("style")) {
23041             
23042             var styles = node.getAttribute("style").split(";");
23043             var nstyle = [];
23044             Roo.each(styles, function(s) {
23045                 if (!s.match(/:/)) {
23046                     return;
23047                 }
23048                 var kv = s.split(":");
23049                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23050                     return;
23051                 }
23052                 // what ever is left... we allow.
23053                 nstyle.push(s);
23054             });
23055             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23056             if (!nstyle.length) {
23057                 node.removeAttribute('style');
23058             }
23059         }
23060         this.iterateChildren(node, this.cleanWord);
23061         
23062         
23063         
23064     },
23065     /**
23066      * iterateChildren of a Node, calling fn each time, using this as the scole..
23067      * @param {DomNode} node node to iterate children of.
23068      * @param {Function} fn method of this class to call on each item.
23069      */
23070     iterateChildren : function(node, fn)
23071     {
23072         if (!node.childNodes.length) {
23073                 return;
23074         }
23075         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23076            fn.call(this, node.childNodes[i])
23077         }
23078     },
23079     
23080     
23081     /**
23082      * cleanTableWidths.
23083      *
23084      * Quite often pasting from word etc.. results in tables with column and widths.
23085      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23086      *
23087      */
23088     cleanTableWidths : function(node)
23089     {
23090          
23091          
23092         if (!node) {
23093             this.cleanTableWidths(this.doc.body);
23094             return;
23095         }
23096         
23097         // ignore list...
23098         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23099             return; 
23100         }
23101         Roo.log(node.tagName);
23102         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23103             this.iterateChildren(node, this.cleanTableWidths);
23104             return;
23105         }
23106         if (node.hasAttribute('width')) {
23107             node.removeAttribute('width');
23108         }
23109         
23110          
23111         if (node.hasAttribute("style")) {
23112             // pretty basic...
23113             
23114             var styles = node.getAttribute("style").split(";");
23115             var nstyle = [];
23116             Roo.each(styles, function(s) {
23117                 if (!s.match(/:/)) {
23118                     return;
23119                 }
23120                 var kv = s.split(":");
23121                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23122                     return;
23123                 }
23124                 // what ever is left... we allow.
23125                 nstyle.push(s);
23126             });
23127             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23128             if (!nstyle.length) {
23129                 node.removeAttribute('style');
23130             }
23131         }
23132         
23133         this.iterateChildren(node, this.cleanTableWidths);
23134         
23135         
23136     },
23137     
23138     
23139     
23140     
23141     domToHTML : function(currentElement, depth, nopadtext) {
23142         
23143         depth = depth || 0;
23144         nopadtext = nopadtext || false;
23145     
23146         if (!currentElement) {
23147             return this.domToHTML(this.doc.body);
23148         }
23149         
23150         //Roo.log(currentElement);
23151         var j;
23152         var allText = false;
23153         var nodeName = currentElement.nodeName;
23154         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23155         
23156         if  (nodeName == '#text') {
23157             
23158             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23159         }
23160         
23161         
23162         var ret = '';
23163         if (nodeName != 'BODY') {
23164              
23165             var i = 0;
23166             // Prints the node tagName, such as <A>, <IMG>, etc
23167             if (tagName) {
23168                 var attr = [];
23169                 for(i = 0; i < currentElement.attributes.length;i++) {
23170                     // quoting?
23171                     var aname = currentElement.attributes.item(i).name;
23172                     if (!currentElement.attributes.item(i).value.length) {
23173                         continue;
23174                     }
23175                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23176                 }
23177                 
23178                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23179             } 
23180             else {
23181                 
23182                 // eack
23183             }
23184         } else {
23185             tagName = false;
23186         }
23187         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23188             return ret;
23189         }
23190         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23191             nopadtext = true;
23192         }
23193         
23194         
23195         // Traverse the tree
23196         i = 0;
23197         var currentElementChild = currentElement.childNodes.item(i);
23198         var allText = true;
23199         var innerHTML  = '';
23200         lastnode = '';
23201         while (currentElementChild) {
23202             // Formatting code (indent the tree so it looks nice on the screen)
23203             var nopad = nopadtext;
23204             if (lastnode == 'SPAN') {
23205                 nopad  = true;
23206             }
23207             // text
23208             if  (currentElementChild.nodeName == '#text') {
23209                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23210                 toadd = nopadtext ? toadd : toadd.trim();
23211                 if (!nopad && toadd.length > 80) {
23212                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23213                 }
23214                 innerHTML  += toadd;
23215                 
23216                 i++;
23217                 currentElementChild = currentElement.childNodes.item(i);
23218                 lastNode = '';
23219                 continue;
23220             }
23221             allText = false;
23222             
23223             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23224                 
23225             // Recursively traverse the tree structure of the child node
23226             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23227             lastnode = currentElementChild.nodeName;
23228             i++;
23229             currentElementChild=currentElement.childNodes.item(i);
23230         }
23231         
23232         ret += innerHTML;
23233         
23234         if (!allText) {
23235                 // The remaining code is mostly for formatting the tree
23236             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23237         }
23238         
23239         
23240         if (tagName) {
23241             ret+= "</"+tagName+">";
23242         }
23243         return ret;
23244         
23245     },
23246         
23247     applyBlacklists : function()
23248     {
23249         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23250         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23251         
23252         this.white = [];
23253         this.black = [];
23254         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23255             if (b.indexOf(tag) > -1) {
23256                 return;
23257             }
23258             this.white.push(tag);
23259             
23260         }, this);
23261         
23262         Roo.each(w, function(tag) {
23263             if (b.indexOf(tag) > -1) {
23264                 return;
23265             }
23266             if (this.white.indexOf(tag) > -1) {
23267                 return;
23268             }
23269             this.white.push(tag);
23270             
23271         }, this);
23272         
23273         
23274         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23275             if (w.indexOf(tag) > -1) {
23276                 return;
23277             }
23278             this.black.push(tag);
23279             
23280         }, this);
23281         
23282         Roo.each(b, function(tag) {
23283             if (w.indexOf(tag) > -1) {
23284                 return;
23285             }
23286             if (this.black.indexOf(tag) > -1) {
23287                 return;
23288             }
23289             this.black.push(tag);
23290             
23291         }, this);
23292         
23293         
23294         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23295         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23296         
23297         this.cwhite = [];
23298         this.cblack = [];
23299         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23300             if (b.indexOf(tag) > -1) {
23301                 return;
23302             }
23303             this.cwhite.push(tag);
23304             
23305         }, this);
23306         
23307         Roo.each(w, function(tag) {
23308             if (b.indexOf(tag) > -1) {
23309                 return;
23310             }
23311             if (this.cwhite.indexOf(tag) > -1) {
23312                 return;
23313             }
23314             this.cwhite.push(tag);
23315             
23316         }, this);
23317         
23318         
23319         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23320             if (w.indexOf(tag) > -1) {
23321                 return;
23322             }
23323             this.cblack.push(tag);
23324             
23325         }, this);
23326         
23327         Roo.each(b, function(tag) {
23328             if (w.indexOf(tag) > -1) {
23329                 return;
23330             }
23331             if (this.cblack.indexOf(tag) > -1) {
23332                 return;
23333             }
23334             this.cblack.push(tag);
23335             
23336         }, this);
23337     },
23338     
23339     setStylesheets : function(stylesheets)
23340     {
23341         if(typeof(stylesheets) == 'string'){
23342             Roo.get(this.iframe.contentDocument.head).createChild({
23343                 tag : 'link',
23344                 rel : 'stylesheet',
23345                 type : 'text/css',
23346                 href : stylesheets
23347             });
23348             
23349             return;
23350         }
23351         var _this = this;
23352      
23353         Roo.each(stylesheets, function(s) {
23354             if(!s.length){
23355                 return;
23356             }
23357             
23358             Roo.get(_this.iframe.contentDocument.head).createChild({
23359                 tag : 'link',
23360                 rel : 'stylesheet',
23361                 type : 'text/css',
23362                 href : s
23363             });
23364         });
23365
23366         
23367     },
23368     
23369     removeStylesheets : function()
23370     {
23371         var _this = this;
23372         
23373         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23374             s.remove();
23375         });
23376     },
23377     
23378     setStyle : function(style)
23379     {
23380         Roo.get(this.iframe.contentDocument.head).createChild({
23381             tag : 'style',
23382             type : 'text/css',
23383             html : style
23384         });
23385
23386         return;
23387     }
23388     
23389     // hide stuff that is not compatible
23390     /**
23391      * @event blur
23392      * @hide
23393      */
23394     /**
23395      * @event change
23396      * @hide
23397      */
23398     /**
23399      * @event focus
23400      * @hide
23401      */
23402     /**
23403      * @event specialkey
23404      * @hide
23405      */
23406     /**
23407      * @cfg {String} fieldClass @hide
23408      */
23409     /**
23410      * @cfg {String} focusClass @hide
23411      */
23412     /**
23413      * @cfg {String} autoCreate @hide
23414      */
23415     /**
23416      * @cfg {String} inputType @hide
23417      */
23418     /**
23419      * @cfg {String} invalidClass @hide
23420      */
23421     /**
23422      * @cfg {String} invalidText @hide
23423      */
23424     /**
23425      * @cfg {String} msgFx @hide
23426      */
23427     /**
23428      * @cfg {String} validateOnBlur @hide
23429      */
23430 });
23431
23432 Roo.HtmlEditorCore.white = [
23433         'area', 'br', 'img', 'input', 'hr', 'wbr',
23434         
23435        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23436        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23437        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23438        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23439        'table',   'ul',         'xmp', 
23440        
23441        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23442       'thead',   'tr', 
23443      
23444       'dir', 'menu', 'ol', 'ul', 'dl',
23445        
23446       'embed',  'object'
23447 ];
23448
23449
23450 Roo.HtmlEditorCore.black = [
23451     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23452         'applet', // 
23453         'base',   'basefont', 'bgsound', 'blink',  'body', 
23454         'frame',  'frameset', 'head',    'html',   'ilayer', 
23455         'iframe', 'layer',  'link',     'meta',    'object',   
23456         'script', 'style' ,'title',  'xml' // clean later..
23457 ];
23458 Roo.HtmlEditorCore.clean = [
23459     'script', 'style', 'title', 'xml'
23460 ];
23461 Roo.HtmlEditorCore.remove = [
23462     'font'
23463 ];
23464 // attributes..
23465
23466 Roo.HtmlEditorCore.ablack = [
23467     'on'
23468 ];
23469     
23470 Roo.HtmlEditorCore.aclean = [ 
23471     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23472 ];
23473
23474 // protocols..
23475 Roo.HtmlEditorCore.pwhite= [
23476         'http',  'https',  'mailto'
23477 ];
23478
23479 // white listed style attributes.
23480 Roo.HtmlEditorCore.cwhite= [
23481       //  'text-align', /// default is to allow most things..
23482       
23483          
23484 //        'font-size'//??
23485 ];
23486
23487 // black listed style attributes.
23488 Roo.HtmlEditorCore.cblack= [
23489       //  'font-size' -- this can be set by the project 
23490 ];
23491
23492
23493 Roo.HtmlEditorCore.swapCodes   =[ 
23494     [    8211, "--" ], 
23495     [    8212, "--" ], 
23496     [    8216,  "'" ],  
23497     [    8217, "'" ],  
23498     [    8220, '"' ],  
23499     [    8221, '"' ],  
23500     [    8226, "*" ],  
23501     [    8230, "..." ]
23502 ]; 
23503
23504     /*
23505  * - LGPL
23506  *
23507  * HtmlEditor
23508  * 
23509  */
23510
23511 /**
23512  * @class Roo.bootstrap.HtmlEditor
23513  * @extends Roo.bootstrap.TextArea
23514  * Bootstrap HtmlEditor class
23515
23516  * @constructor
23517  * Create a new HtmlEditor
23518  * @param {Object} config The config object
23519  */
23520
23521 Roo.bootstrap.HtmlEditor = function(config){
23522     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23523     if (!this.toolbars) {
23524         this.toolbars = [];
23525     }
23526     
23527     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23528     this.addEvents({
23529             /**
23530              * @event initialize
23531              * Fires when the editor is fully initialized (including the iframe)
23532              * @param {HtmlEditor} this
23533              */
23534             initialize: true,
23535             /**
23536              * @event activate
23537              * Fires when the editor is first receives the focus. Any insertion must wait
23538              * until after this event.
23539              * @param {HtmlEditor} this
23540              */
23541             activate: true,
23542              /**
23543              * @event beforesync
23544              * Fires before the textarea is updated with content from the editor iframe. Return false
23545              * to cancel the sync.
23546              * @param {HtmlEditor} this
23547              * @param {String} html
23548              */
23549             beforesync: true,
23550              /**
23551              * @event beforepush
23552              * Fires before the iframe editor is updated with content from the textarea. Return false
23553              * to cancel the push.
23554              * @param {HtmlEditor} this
23555              * @param {String} html
23556              */
23557             beforepush: true,
23558              /**
23559              * @event sync
23560              * Fires when the textarea is updated with content from the editor iframe.
23561              * @param {HtmlEditor} this
23562              * @param {String} html
23563              */
23564             sync: true,
23565              /**
23566              * @event push
23567              * Fires when the iframe editor is updated with content from the textarea.
23568              * @param {HtmlEditor} this
23569              * @param {String} html
23570              */
23571             push: true,
23572              /**
23573              * @event editmodechange
23574              * Fires when the editor switches edit modes
23575              * @param {HtmlEditor} this
23576              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23577              */
23578             editmodechange: true,
23579             /**
23580              * @event editorevent
23581              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23582              * @param {HtmlEditor} this
23583              */
23584             editorevent: true,
23585             /**
23586              * @event firstfocus
23587              * Fires when on first focus - needed by toolbars..
23588              * @param {HtmlEditor} this
23589              */
23590             firstfocus: true,
23591             /**
23592              * @event autosave
23593              * Auto save the htmlEditor value as a file into Events
23594              * @param {HtmlEditor} this
23595              */
23596             autosave: true,
23597             /**
23598              * @event savedpreview
23599              * preview the saved version of htmlEditor
23600              * @param {HtmlEditor} this
23601              */
23602             savedpreview: true
23603         });
23604 };
23605
23606
23607 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23608     
23609     
23610       /**
23611      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23612      */
23613     toolbars : false,
23614     
23615      /**
23616     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23617     */
23618     btns : [],
23619    
23620      /**
23621      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23622      *                        Roo.resizable.
23623      */
23624     resizable : false,
23625      /**
23626      * @cfg {Number} height (in pixels)
23627      */   
23628     height: 300,
23629    /**
23630      * @cfg {Number} width (in pixels)
23631      */   
23632     width: false,
23633     
23634     /**
23635      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23636      * 
23637      */
23638     stylesheets: false,
23639     
23640     // id of frame..
23641     frameId: false,
23642     
23643     // private properties
23644     validationEvent : false,
23645     deferHeight: true,
23646     initialized : false,
23647     activated : false,
23648     
23649     onFocus : Roo.emptyFn,
23650     iframePad:3,
23651     hideMode:'offsets',
23652     
23653     tbContainer : false,
23654     
23655     bodyCls : '',
23656     
23657     toolbarContainer :function() {
23658         return this.wrap.select('.x-html-editor-tb',true).first();
23659     },
23660
23661     /**
23662      * Protected method that will not generally be called directly. It
23663      * is called when the editor creates its toolbar. Override this method if you need to
23664      * add custom toolbar buttons.
23665      * @param {HtmlEditor} editor
23666      */
23667     createToolbar : function(){
23668         Roo.log('renewing');
23669         Roo.log("create toolbars");
23670         
23671         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23672         this.toolbars[0].render(this.toolbarContainer());
23673         
23674         return;
23675         
23676 //        if (!editor.toolbars || !editor.toolbars.length) {
23677 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23678 //        }
23679 //        
23680 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23681 //            editor.toolbars[i] = Roo.factory(
23682 //                    typeof(editor.toolbars[i]) == 'string' ?
23683 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23684 //                Roo.bootstrap.HtmlEditor);
23685 //            editor.toolbars[i].init(editor);
23686 //        }
23687     },
23688
23689      
23690     // private
23691     onRender : function(ct, position)
23692     {
23693        // Roo.log("Call onRender: " + this.xtype);
23694         var _t = this;
23695         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23696       
23697         this.wrap = this.inputEl().wrap({
23698             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23699         });
23700         
23701         this.editorcore.onRender(ct, position);
23702          
23703         if (this.resizable) {
23704             this.resizeEl = new Roo.Resizable(this.wrap, {
23705                 pinned : true,
23706                 wrap: true,
23707                 dynamic : true,
23708                 minHeight : this.height,
23709                 height: this.height,
23710                 handles : this.resizable,
23711                 width: this.width,
23712                 listeners : {
23713                     resize : function(r, w, h) {
23714                         _t.onResize(w,h); // -something
23715                     }
23716                 }
23717             });
23718             
23719         }
23720         this.createToolbar(this);
23721        
23722         
23723         if(!this.width && this.resizable){
23724             this.setSize(this.wrap.getSize());
23725         }
23726         if (this.resizeEl) {
23727             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23728             // should trigger onReize..
23729         }
23730         
23731     },
23732
23733     // private
23734     onResize : function(w, h)
23735     {
23736         Roo.log('resize: ' +w + ',' + h );
23737         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23738         var ew = false;
23739         var eh = false;
23740         
23741         if(this.inputEl() ){
23742             if(typeof w == 'number'){
23743                 var aw = w - this.wrap.getFrameWidth('lr');
23744                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23745                 ew = aw;
23746             }
23747             if(typeof h == 'number'){
23748                  var tbh = -11;  // fixme it needs to tool bar size!
23749                 for (var i =0; i < this.toolbars.length;i++) {
23750                     // fixme - ask toolbars for heights?
23751                     tbh += this.toolbars[i].el.getHeight();
23752                     //if (this.toolbars[i].footer) {
23753                     //    tbh += this.toolbars[i].footer.el.getHeight();
23754                     //}
23755                 }
23756               
23757                 
23758                 
23759                 
23760                 
23761                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23762                 ah -= 5; // knock a few pixes off for look..
23763                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23764                 var eh = ah;
23765             }
23766         }
23767         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23768         this.editorcore.onResize(ew,eh);
23769         
23770     },
23771
23772     /**
23773      * Toggles the editor between standard and source edit mode.
23774      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23775      */
23776     toggleSourceEdit : function(sourceEditMode)
23777     {
23778         this.editorcore.toggleSourceEdit(sourceEditMode);
23779         
23780         if(this.editorcore.sourceEditMode){
23781             Roo.log('editor - showing textarea');
23782             
23783 //            Roo.log('in');
23784 //            Roo.log(this.syncValue());
23785             this.syncValue();
23786             this.inputEl().removeClass(['hide', 'x-hidden']);
23787             this.inputEl().dom.removeAttribute('tabIndex');
23788             this.inputEl().focus();
23789         }else{
23790             Roo.log('editor - hiding textarea');
23791 //            Roo.log('out')
23792 //            Roo.log(this.pushValue()); 
23793             this.pushValue();
23794             
23795             this.inputEl().addClass(['hide', 'x-hidden']);
23796             this.inputEl().dom.setAttribute('tabIndex', -1);
23797             //this.deferFocus();
23798         }
23799          
23800         if(this.resizable){
23801             this.setSize(this.wrap.getSize());
23802         }
23803         
23804         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23805     },
23806  
23807     // private (for BoxComponent)
23808     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23809
23810     // private (for BoxComponent)
23811     getResizeEl : function(){
23812         return this.wrap;
23813     },
23814
23815     // private (for BoxComponent)
23816     getPositionEl : function(){
23817         return this.wrap;
23818     },
23819
23820     // private
23821     initEvents : function(){
23822         this.originalValue = this.getValue();
23823     },
23824
23825 //    /**
23826 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23827 //     * @method
23828 //     */
23829 //    markInvalid : Roo.emptyFn,
23830 //    /**
23831 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23832 //     * @method
23833 //     */
23834 //    clearInvalid : Roo.emptyFn,
23835
23836     setValue : function(v){
23837         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23838         this.editorcore.pushValue();
23839     },
23840
23841      
23842     // private
23843     deferFocus : function(){
23844         this.focus.defer(10, this);
23845     },
23846
23847     // doc'ed in Field
23848     focus : function(){
23849         this.editorcore.focus();
23850         
23851     },
23852       
23853
23854     // private
23855     onDestroy : function(){
23856         
23857         
23858         
23859         if(this.rendered){
23860             
23861             for (var i =0; i < this.toolbars.length;i++) {
23862                 // fixme - ask toolbars for heights?
23863                 this.toolbars[i].onDestroy();
23864             }
23865             
23866             this.wrap.dom.innerHTML = '';
23867             this.wrap.remove();
23868         }
23869     },
23870
23871     // private
23872     onFirstFocus : function(){
23873         //Roo.log("onFirstFocus");
23874         this.editorcore.onFirstFocus();
23875          for (var i =0; i < this.toolbars.length;i++) {
23876             this.toolbars[i].onFirstFocus();
23877         }
23878         
23879     },
23880     
23881     // private
23882     syncValue : function()
23883     {   
23884         this.editorcore.syncValue();
23885     },
23886     
23887     pushValue : function()
23888     {   
23889         this.editorcore.pushValue();
23890     }
23891      
23892     
23893     // hide stuff that is not compatible
23894     /**
23895      * @event blur
23896      * @hide
23897      */
23898     /**
23899      * @event change
23900      * @hide
23901      */
23902     /**
23903      * @event focus
23904      * @hide
23905      */
23906     /**
23907      * @event specialkey
23908      * @hide
23909      */
23910     /**
23911      * @cfg {String} fieldClass @hide
23912      */
23913     /**
23914      * @cfg {String} focusClass @hide
23915      */
23916     /**
23917      * @cfg {String} autoCreate @hide
23918      */
23919     /**
23920      * @cfg {String} inputType @hide
23921      */
23922     /**
23923      * @cfg {String} invalidClass @hide
23924      */
23925     /**
23926      * @cfg {String} invalidText @hide
23927      */
23928     /**
23929      * @cfg {String} msgFx @hide
23930      */
23931     /**
23932      * @cfg {String} validateOnBlur @hide
23933      */
23934 });
23935  
23936     
23937    
23938    
23939    
23940       
23941 Roo.namespace('Roo.bootstrap.htmleditor');
23942 /**
23943  * @class Roo.bootstrap.HtmlEditorToolbar1
23944  * Basic Toolbar
23945  * 
23946  * Usage:
23947  *
23948  new Roo.bootstrap.HtmlEditor({
23949     ....
23950     toolbars : [
23951         new Roo.bootstrap.HtmlEditorToolbar1({
23952             disable : { fonts: 1 , format: 1, ..., ... , ...],
23953             btns : [ .... ]
23954         })
23955     }
23956      
23957  * 
23958  * @cfg {Object} disable List of elements to disable..
23959  * @cfg {Array} btns List of additional buttons.
23960  * 
23961  * 
23962  * NEEDS Extra CSS? 
23963  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23964  */
23965  
23966 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23967 {
23968     
23969     Roo.apply(this, config);
23970     
23971     // default disabled, based on 'good practice'..
23972     this.disable = this.disable || {};
23973     Roo.applyIf(this.disable, {
23974         fontSize : true,
23975         colors : true,
23976         specialElements : true
23977     });
23978     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23979     
23980     this.editor = config.editor;
23981     this.editorcore = config.editor.editorcore;
23982     
23983     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23984     
23985     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23986     // dont call parent... till later.
23987 }
23988 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23989      
23990     bar : true,
23991     
23992     editor : false,
23993     editorcore : false,
23994     
23995     
23996     formats : [
23997         "p" ,  
23998         "h1","h2","h3","h4","h5","h6", 
23999         "pre", "code", 
24000         "abbr", "acronym", "address", "cite", "samp", "var",
24001         'div','span'
24002     ],
24003     
24004     onRender : function(ct, position)
24005     {
24006        // Roo.log("Call onRender: " + this.xtype);
24007         
24008        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24009        Roo.log(this.el);
24010        this.el.dom.style.marginBottom = '0';
24011        var _this = this;
24012        var editorcore = this.editorcore;
24013        var editor= this.editor;
24014        
24015        var children = [];
24016        var btn = function(id,cmd , toggle, handler, html){
24017        
24018             var  event = toggle ? 'toggle' : 'click';
24019        
24020             var a = {
24021                 size : 'sm',
24022                 xtype: 'Button',
24023                 xns: Roo.bootstrap,
24024                 //glyphicon : id,
24025                 fa: id,
24026                 cmd : id || cmd,
24027                 enableToggle:toggle !== false,
24028                 html : html || '',
24029                 pressed : toggle ? false : null,
24030                 listeners : {}
24031             };
24032             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24033                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24034             };
24035             children.push(a);
24036             return a;
24037        }
24038        
24039     //    var cb_box = function...
24040         
24041         var style = {
24042                 xtype: 'Button',
24043                 size : 'sm',
24044                 xns: Roo.bootstrap,
24045                 fa : 'font',
24046                 //html : 'submit'
24047                 menu : {
24048                     xtype: 'Menu',
24049                     xns: Roo.bootstrap,
24050                     items:  []
24051                 }
24052         };
24053         Roo.each(this.formats, function(f) {
24054             style.menu.items.push({
24055                 xtype :'MenuItem',
24056                 xns: Roo.bootstrap,
24057                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24058                 tagname : f,
24059                 listeners : {
24060                     click : function()
24061                     {
24062                         editorcore.insertTag(this.tagname);
24063                         editor.focus();
24064                     }
24065                 }
24066                 
24067             });
24068         });
24069         children.push(style);   
24070         
24071         btn('bold',false,true);
24072         btn('italic',false,true);
24073         btn('align-left', 'justifyleft',true);
24074         btn('align-center', 'justifycenter',true);
24075         btn('align-right' , 'justifyright',true);
24076         btn('link', false, false, function(btn) {
24077             //Roo.log("create link?");
24078             var url = prompt(this.createLinkText, this.defaultLinkValue);
24079             if(url && url != 'http:/'+'/'){
24080                 this.editorcore.relayCmd('createlink', url);
24081             }
24082         }),
24083         btn('list','insertunorderedlist',true);
24084         btn('pencil', false,true, function(btn){
24085                 Roo.log(this);
24086                 this.toggleSourceEdit(btn.pressed);
24087         });
24088         
24089         if (this.editor.btns.length > 0) {
24090             for (var i = 0; i<this.editor.btns.length; i++) {
24091                 children.push(this.editor.btns[i]);
24092             }
24093         }
24094         
24095         /*
24096         var cog = {
24097                 xtype: 'Button',
24098                 size : 'sm',
24099                 xns: Roo.bootstrap,
24100                 glyphicon : 'cog',
24101                 //html : 'submit'
24102                 menu : {
24103                     xtype: 'Menu',
24104                     xns: Roo.bootstrap,
24105                     items:  []
24106                 }
24107         };
24108         
24109         cog.menu.items.push({
24110             xtype :'MenuItem',
24111             xns: Roo.bootstrap,
24112             html : Clean styles,
24113             tagname : f,
24114             listeners : {
24115                 click : function()
24116                 {
24117                     editorcore.insertTag(this.tagname);
24118                     editor.focus();
24119                 }
24120             }
24121             
24122         });
24123        */
24124         
24125          
24126        this.xtype = 'NavSimplebar';
24127         
24128         for(var i=0;i< children.length;i++) {
24129             
24130             this.buttons.add(this.addxtypeChild(children[i]));
24131             
24132         }
24133         
24134         editor.on('editorevent', this.updateToolbar, this);
24135     },
24136     onBtnClick : function(id)
24137     {
24138        this.editorcore.relayCmd(id);
24139        this.editorcore.focus();
24140     },
24141     
24142     /**
24143      * Protected method that will not generally be called directly. It triggers
24144      * a toolbar update by reading the markup state of the current selection in the editor.
24145      */
24146     updateToolbar: function(){
24147
24148         if(!this.editorcore.activated){
24149             this.editor.onFirstFocus(); // is this neeed?
24150             return;
24151         }
24152
24153         var btns = this.buttons; 
24154         var doc = this.editorcore.doc;
24155         btns.get('bold').setActive(doc.queryCommandState('bold'));
24156         btns.get('italic').setActive(doc.queryCommandState('italic'));
24157         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24158         
24159         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24160         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24161         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24162         
24163         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24164         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24165          /*
24166         
24167         var ans = this.editorcore.getAllAncestors();
24168         if (this.formatCombo) {
24169             
24170             
24171             var store = this.formatCombo.store;
24172             this.formatCombo.setValue("");
24173             for (var i =0; i < ans.length;i++) {
24174                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24175                     // select it..
24176                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24177                     break;
24178                 }
24179             }
24180         }
24181         
24182         
24183         
24184         // hides menus... - so this cant be on a menu...
24185         Roo.bootstrap.MenuMgr.hideAll();
24186         */
24187         Roo.bootstrap.MenuMgr.hideAll();
24188         //this.editorsyncValue();
24189     },
24190     onFirstFocus: function() {
24191         this.buttons.each(function(item){
24192            item.enable();
24193         });
24194     },
24195     toggleSourceEdit : function(sourceEditMode){
24196         
24197           
24198         if(sourceEditMode){
24199             Roo.log("disabling buttons");
24200            this.buttons.each( function(item){
24201                 if(item.cmd != 'pencil'){
24202                     item.disable();
24203                 }
24204             });
24205           
24206         }else{
24207             Roo.log("enabling buttons");
24208             if(this.editorcore.initialized){
24209                 this.buttons.each( function(item){
24210                     item.enable();
24211                 });
24212             }
24213             
24214         }
24215         Roo.log("calling toggole on editor");
24216         // tell the editor that it's been pressed..
24217         this.editor.toggleSourceEdit(sourceEditMode);
24218        
24219     }
24220 });
24221
24222
24223
24224
24225
24226 /**
24227  * @class Roo.bootstrap.Table.AbstractSelectionModel
24228  * @extends Roo.util.Observable
24229  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24230  * implemented by descendant classes.  This class should not be directly instantiated.
24231  * @constructor
24232  */
24233 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24234     this.locked = false;
24235     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24236 };
24237
24238
24239 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24240     /** @ignore Called by the grid automatically. Do not call directly. */
24241     init : function(grid){
24242         this.grid = grid;
24243         this.initEvents();
24244     },
24245
24246     /**
24247      * Locks the selections.
24248      */
24249     lock : function(){
24250         this.locked = true;
24251     },
24252
24253     /**
24254      * Unlocks the selections.
24255      */
24256     unlock : function(){
24257         this.locked = false;
24258     },
24259
24260     /**
24261      * Returns true if the selections are locked.
24262      * @return {Boolean}
24263      */
24264     isLocked : function(){
24265         return this.locked;
24266     }
24267 });
24268 /**
24269  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24270  * @class Roo.bootstrap.Table.RowSelectionModel
24271  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24272  * It supports multiple selections and keyboard selection/navigation. 
24273  * @constructor
24274  * @param {Object} config
24275  */
24276
24277 Roo.bootstrap.Table.RowSelectionModel = function(config){
24278     Roo.apply(this, config);
24279     this.selections = new Roo.util.MixedCollection(false, function(o){
24280         return o.id;
24281     });
24282
24283     this.last = false;
24284     this.lastActive = false;
24285
24286     this.addEvents({
24287         /**
24288              * @event selectionchange
24289              * Fires when the selection changes
24290              * @param {SelectionModel} this
24291              */
24292             "selectionchange" : true,
24293         /**
24294              * @event afterselectionchange
24295              * Fires after the selection changes (eg. by key press or clicking)
24296              * @param {SelectionModel} this
24297              */
24298             "afterselectionchange" : true,
24299         /**
24300              * @event beforerowselect
24301              * Fires when a row is selected being selected, return false to cancel.
24302              * @param {SelectionModel} this
24303              * @param {Number} rowIndex The selected index
24304              * @param {Boolean} keepExisting False if other selections will be cleared
24305              */
24306             "beforerowselect" : true,
24307         /**
24308              * @event rowselect
24309              * Fires when a row is selected.
24310              * @param {SelectionModel} this
24311              * @param {Number} rowIndex The selected index
24312              * @param {Roo.data.Record} r The record
24313              */
24314             "rowselect" : true,
24315         /**
24316              * @event rowdeselect
24317              * Fires when a row is deselected.
24318              * @param {SelectionModel} this
24319              * @param {Number} rowIndex The selected index
24320              */
24321         "rowdeselect" : true
24322     });
24323     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24324     this.locked = false;
24325  };
24326
24327 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24328     /**
24329      * @cfg {Boolean} singleSelect
24330      * True to allow selection of only one row at a time (defaults to false)
24331      */
24332     singleSelect : false,
24333
24334     // private
24335     initEvents : function()
24336     {
24337
24338         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24339         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24340         //}else{ // allow click to work like normal
24341          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24342         //}
24343         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24344         this.grid.on("rowclick", this.handleMouseDown, this);
24345         
24346         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24347             "up" : function(e){
24348                 if(!e.shiftKey){
24349                     this.selectPrevious(e.shiftKey);
24350                 }else if(this.last !== false && this.lastActive !== false){
24351                     var last = this.last;
24352                     this.selectRange(this.last,  this.lastActive-1);
24353                     this.grid.getView().focusRow(this.lastActive);
24354                     if(last !== false){
24355                         this.last = last;
24356                     }
24357                 }else{
24358                     this.selectFirstRow();
24359                 }
24360                 this.fireEvent("afterselectionchange", this);
24361             },
24362             "down" : function(e){
24363                 if(!e.shiftKey){
24364                     this.selectNext(e.shiftKey);
24365                 }else if(this.last !== false && this.lastActive !== false){
24366                     var last = this.last;
24367                     this.selectRange(this.last,  this.lastActive+1);
24368                     this.grid.getView().focusRow(this.lastActive);
24369                     if(last !== false){
24370                         this.last = last;
24371                     }
24372                 }else{
24373                     this.selectFirstRow();
24374                 }
24375                 this.fireEvent("afterselectionchange", this);
24376             },
24377             scope: this
24378         });
24379         this.grid.store.on('load', function(){
24380             this.selections.clear();
24381         },this);
24382         /*
24383         var view = this.grid.view;
24384         view.on("refresh", this.onRefresh, this);
24385         view.on("rowupdated", this.onRowUpdated, this);
24386         view.on("rowremoved", this.onRemove, this);
24387         */
24388     },
24389
24390     // private
24391     onRefresh : function()
24392     {
24393         var ds = this.grid.store, i, v = this.grid.view;
24394         var s = this.selections;
24395         s.each(function(r){
24396             if((i = ds.indexOfId(r.id)) != -1){
24397                 v.onRowSelect(i);
24398             }else{
24399                 s.remove(r);
24400             }
24401         });
24402     },
24403
24404     // private
24405     onRemove : function(v, index, r){
24406         this.selections.remove(r);
24407     },
24408
24409     // private
24410     onRowUpdated : function(v, index, r){
24411         if(this.isSelected(r)){
24412             v.onRowSelect(index);
24413         }
24414     },
24415
24416     /**
24417      * Select records.
24418      * @param {Array} records The records to select
24419      * @param {Boolean} keepExisting (optional) True to keep existing selections
24420      */
24421     selectRecords : function(records, keepExisting)
24422     {
24423         if(!keepExisting){
24424             this.clearSelections();
24425         }
24426             var ds = this.grid.store;
24427         for(var i = 0, len = records.length; i < len; i++){
24428             this.selectRow(ds.indexOf(records[i]), true);
24429         }
24430     },
24431
24432     /**
24433      * Gets the number of selected rows.
24434      * @return {Number}
24435      */
24436     getCount : function(){
24437         return this.selections.length;
24438     },
24439
24440     /**
24441      * Selects the first row in the grid.
24442      */
24443     selectFirstRow : function(){
24444         this.selectRow(0);
24445     },
24446
24447     /**
24448      * Select the last row.
24449      * @param {Boolean} keepExisting (optional) True to keep existing selections
24450      */
24451     selectLastRow : function(keepExisting){
24452         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24453         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24454     },
24455
24456     /**
24457      * Selects the row immediately following the last selected row.
24458      * @param {Boolean} keepExisting (optional) True to keep existing selections
24459      */
24460     selectNext : function(keepExisting)
24461     {
24462             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24463             this.selectRow(this.last+1, keepExisting);
24464             this.grid.getView().focusRow(this.last);
24465         }
24466     },
24467
24468     /**
24469      * Selects the row that precedes the last selected row.
24470      * @param {Boolean} keepExisting (optional) True to keep existing selections
24471      */
24472     selectPrevious : function(keepExisting){
24473         if(this.last){
24474             this.selectRow(this.last-1, keepExisting);
24475             this.grid.getView().focusRow(this.last);
24476         }
24477     },
24478
24479     /**
24480      * Returns the selected records
24481      * @return {Array} Array of selected records
24482      */
24483     getSelections : function(){
24484         return [].concat(this.selections.items);
24485     },
24486
24487     /**
24488      * Returns the first selected record.
24489      * @return {Record}
24490      */
24491     getSelected : function(){
24492         return this.selections.itemAt(0);
24493     },
24494
24495
24496     /**
24497      * Clears all selections.
24498      */
24499     clearSelections : function(fast)
24500     {
24501         if(this.locked) {
24502             return;
24503         }
24504         if(fast !== true){
24505                 var ds = this.grid.store;
24506             var s = this.selections;
24507             s.each(function(r){
24508                 this.deselectRow(ds.indexOfId(r.id));
24509             }, this);
24510             s.clear();
24511         }else{
24512             this.selections.clear();
24513         }
24514         this.last = false;
24515     },
24516
24517
24518     /**
24519      * Selects all rows.
24520      */
24521     selectAll : function(){
24522         if(this.locked) {
24523             return;
24524         }
24525         this.selections.clear();
24526         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24527             this.selectRow(i, true);
24528         }
24529     },
24530
24531     /**
24532      * Returns True if there is a selection.
24533      * @return {Boolean}
24534      */
24535     hasSelection : function(){
24536         return this.selections.length > 0;
24537     },
24538
24539     /**
24540      * Returns True if the specified row is selected.
24541      * @param {Number/Record} record The record or index of the record to check
24542      * @return {Boolean}
24543      */
24544     isSelected : function(index){
24545             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24546         return (r && this.selections.key(r.id) ? true : false);
24547     },
24548
24549     /**
24550      * Returns True if the specified record id is selected.
24551      * @param {String} id The id of record to check
24552      * @return {Boolean}
24553      */
24554     isIdSelected : function(id){
24555         return (this.selections.key(id) ? true : false);
24556     },
24557
24558
24559     // private
24560     handleMouseDBClick : function(e, t){
24561         
24562     },
24563     // private
24564     handleMouseDown : function(e, t)
24565     {
24566             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24567         if(this.isLocked() || rowIndex < 0 ){
24568             return;
24569         };
24570         if(e.shiftKey && this.last !== false){
24571             var last = this.last;
24572             this.selectRange(last, rowIndex, e.ctrlKey);
24573             this.last = last; // reset the last
24574             t.focus();
24575     
24576         }else{
24577             var isSelected = this.isSelected(rowIndex);
24578             //Roo.log("select row:" + rowIndex);
24579             if(isSelected){
24580                 this.deselectRow(rowIndex);
24581             } else {
24582                         this.selectRow(rowIndex, true);
24583             }
24584     
24585             /*
24586                 if(e.button !== 0 && isSelected){
24587                 alert('rowIndex 2: ' + rowIndex);
24588                     view.focusRow(rowIndex);
24589                 }else if(e.ctrlKey && isSelected){
24590                     this.deselectRow(rowIndex);
24591                 }else if(!isSelected){
24592                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24593                     view.focusRow(rowIndex);
24594                 }
24595             */
24596         }
24597         this.fireEvent("afterselectionchange", this);
24598     },
24599     // private
24600     handleDragableRowClick :  function(grid, rowIndex, e) 
24601     {
24602         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24603             this.selectRow(rowIndex, false);
24604             grid.view.focusRow(rowIndex);
24605              this.fireEvent("afterselectionchange", this);
24606         }
24607     },
24608     
24609     /**
24610      * Selects multiple rows.
24611      * @param {Array} rows Array of the indexes of the row to select
24612      * @param {Boolean} keepExisting (optional) True to keep existing selections
24613      */
24614     selectRows : function(rows, keepExisting){
24615         if(!keepExisting){
24616             this.clearSelections();
24617         }
24618         for(var i = 0, len = rows.length; i < len; i++){
24619             this.selectRow(rows[i], true);
24620         }
24621     },
24622
24623     /**
24624      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24625      * @param {Number} startRow The index of the first row in the range
24626      * @param {Number} endRow The index of the last row in the range
24627      * @param {Boolean} keepExisting (optional) True to retain existing selections
24628      */
24629     selectRange : function(startRow, endRow, keepExisting){
24630         if(this.locked) {
24631             return;
24632         }
24633         if(!keepExisting){
24634             this.clearSelections();
24635         }
24636         if(startRow <= endRow){
24637             for(var i = startRow; i <= endRow; i++){
24638                 this.selectRow(i, true);
24639             }
24640         }else{
24641             for(var i = startRow; i >= endRow; i--){
24642                 this.selectRow(i, true);
24643             }
24644         }
24645     },
24646
24647     /**
24648      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24649      * @param {Number} startRow The index of the first row in the range
24650      * @param {Number} endRow The index of the last row in the range
24651      */
24652     deselectRange : function(startRow, endRow, preventViewNotify){
24653         if(this.locked) {
24654             return;
24655         }
24656         for(var i = startRow; i <= endRow; i++){
24657             this.deselectRow(i, preventViewNotify);
24658         }
24659     },
24660
24661     /**
24662      * Selects a row.
24663      * @param {Number} row The index of the row to select
24664      * @param {Boolean} keepExisting (optional) True to keep existing selections
24665      */
24666     selectRow : function(index, keepExisting, preventViewNotify)
24667     {
24668             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24669             return;
24670         }
24671         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24672             if(!keepExisting || this.singleSelect){
24673                 this.clearSelections();
24674             }
24675             
24676             var r = this.grid.store.getAt(index);
24677             //console.log('selectRow - record id :' + r.id);
24678             
24679             this.selections.add(r);
24680             this.last = this.lastActive = index;
24681             if(!preventViewNotify){
24682                 var proxy = new Roo.Element(
24683                                 this.grid.getRowDom(index)
24684                 );
24685                 proxy.addClass('bg-info info');
24686             }
24687             this.fireEvent("rowselect", this, index, r);
24688             this.fireEvent("selectionchange", this);
24689         }
24690     },
24691
24692     /**
24693      * Deselects a row.
24694      * @param {Number} row The index of the row to deselect
24695      */
24696     deselectRow : function(index, preventViewNotify)
24697     {
24698         if(this.locked) {
24699             return;
24700         }
24701         if(this.last == index){
24702             this.last = false;
24703         }
24704         if(this.lastActive == index){
24705             this.lastActive = false;
24706         }
24707         
24708         var r = this.grid.store.getAt(index);
24709         if (!r) {
24710             return;
24711         }
24712         
24713         this.selections.remove(r);
24714         //.console.log('deselectRow - record id :' + r.id);
24715         if(!preventViewNotify){
24716         
24717             var proxy = new Roo.Element(
24718                 this.grid.getRowDom(index)
24719             );
24720             proxy.removeClass('bg-info info');
24721         }
24722         this.fireEvent("rowdeselect", this, index);
24723         this.fireEvent("selectionchange", this);
24724     },
24725
24726     // private
24727     restoreLast : function(){
24728         if(this._last){
24729             this.last = this._last;
24730         }
24731     },
24732
24733     // private
24734     acceptsNav : function(row, col, cm){
24735         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24736     },
24737
24738     // private
24739     onEditorKey : function(field, e){
24740         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24741         if(k == e.TAB){
24742             e.stopEvent();
24743             ed.completeEdit();
24744             if(e.shiftKey){
24745                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24746             }else{
24747                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24748             }
24749         }else if(k == e.ENTER && !e.ctrlKey){
24750             e.stopEvent();
24751             ed.completeEdit();
24752             if(e.shiftKey){
24753                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24754             }else{
24755                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24756             }
24757         }else if(k == e.ESC){
24758             ed.cancelEdit();
24759         }
24760         if(newCell){
24761             g.startEditing(newCell[0], newCell[1]);
24762         }
24763     }
24764 });
24765 /*
24766  * Based on:
24767  * Ext JS Library 1.1.1
24768  * Copyright(c) 2006-2007, Ext JS, LLC.
24769  *
24770  * Originally Released Under LGPL - original licence link has changed is not relivant.
24771  *
24772  * Fork - LGPL
24773  * <script type="text/javascript">
24774  */
24775  
24776 /**
24777  * @class Roo.bootstrap.PagingToolbar
24778  * @extends Roo.bootstrap.NavSimplebar
24779  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24780  * @constructor
24781  * Create a new PagingToolbar
24782  * @param {Object} config The config object
24783  * @param {Roo.data.Store} store
24784  */
24785 Roo.bootstrap.PagingToolbar = function(config)
24786 {
24787     // old args format still supported... - xtype is prefered..
24788         // created from xtype...
24789     
24790     this.ds = config.dataSource;
24791     
24792     if (config.store && !this.ds) {
24793         this.store= Roo.factory(config.store, Roo.data);
24794         this.ds = this.store;
24795         this.ds.xmodule = this.xmodule || false;
24796     }
24797     
24798     this.toolbarItems = [];
24799     if (config.items) {
24800         this.toolbarItems = config.items;
24801     }
24802     
24803     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24804     
24805     this.cursor = 0;
24806     
24807     if (this.ds) { 
24808         this.bind(this.ds);
24809     }
24810     
24811     if (Roo.bootstrap.version == 4) {
24812         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24813     } else {
24814         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24815     }
24816     
24817 };
24818
24819 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24820     /**
24821      * @cfg {Roo.data.Store} dataSource
24822      * The underlying data store providing the paged data
24823      */
24824     /**
24825      * @cfg {String/HTMLElement/Element} container
24826      * container The id or element that will contain the toolbar
24827      */
24828     /**
24829      * @cfg {Boolean} displayInfo
24830      * True to display the displayMsg (defaults to false)
24831      */
24832     /**
24833      * @cfg {Number} pageSize
24834      * The number of records to display per page (defaults to 20)
24835      */
24836     pageSize: 20,
24837     /**
24838      * @cfg {String} displayMsg
24839      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24840      */
24841     displayMsg : 'Displaying {0} - {1} of {2}',
24842     /**
24843      * @cfg {String} emptyMsg
24844      * The message to display when no records are found (defaults to "No data to display")
24845      */
24846     emptyMsg : 'No data to display',
24847     /**
24848      * Customizable piece of the default paging text (defaults to "Page")
24849      * @type String
24850      */
24851     beforePageText : "Page",
24852     /**
24853      * Customizable piece of the default paging text (defaults to "of %0")
24854      * @type String
24855      */
24856     afterPageText : "of {0}",
24857     /**
24858      * Customizable piece of the default paging text (defaults to "First Page")
24859      * @type String
24860      */
24861     firstText : "First Page",
24862     /**
24863      * Customizable piece of the default paging text (defaults to "Previous Page")
24864      * @type String
24865      */
24866     prevText : "Previous Page",
24867     /**
24868      * Customizable piece of the default paging text (defaults to "Next Page")
24869      * @type String
24870      */
24871     nextText : "Next Page",
24872     /**
24873      * Customizable piece of the default paging text (defaults to "Last Page")
24874      * @type String
24875      */
24876     lastText : "Last Page",
24877     /**
24878      * Customizable piece of the default paging text (defaults to "Refresh")
24879      * @type String
24880      */
24881     refreshText : "Refresh",
24882
24883     buttons : false,
24884     // private
24885     onRender : function(ct, position) 
24886     {
24887         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24888         this.navgroup.parentId = this.id;
24889         this.navgroup.onRender(this.el, null);
24890         // add the buttons to the navgroup
24891         
24892         if(this.displayInfo){
24893             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24894             this.displayEl = this.el.select('.x-paging-info', true).first();
24895 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24896 //            this.displayEl = navel.el.select('span',true).first();
24897         }
24898         
24899         var _this = this;
24900         
24901         if(this.buttons){
24902             Roo.each(_this.buttons, function(e){ // this might need to use render????
24903                Roo.factory(e).render(_this.el);
24904             });
24905         }
24906             
24907         Roo.each(_this.toolbarItems, function(e) {
24908             _this.navgroup.addItem(e);
24909         });
24910         
24911         
24912         this.first = this.navgroup.addItem({
24913             tooltip: this.firstText,
24914             cls: "prev btn-outline-secondary",
24915             html : ' <i class="fa fa-step-backward"></i>',
24916             disabled: true,
24917             preventDefault: true,
24918             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24919         });
24920         
24921         this.prev =  this.navgroup.addItem({
24922             tooltip: this.prevText,
24923             cls: "prev btn-outline-secondary",
24924             html : ' <i class="fa fa-backward"></i>',
24925             disabled: true,
24926             preventDefault: true,
24927             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24928         });
24929     //this.addSeparator();
24930         
24931         
24932         var field = this.navgroup.addItem( {
24933             tagtype : 'span',
24934             cls : 'x-paging-position  btn-outline-secondary',
24935              disabled: true,
24936             html : this.beforePageText  +
24937                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24938                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24939          } ); //?? escaped?
24940         
24941         this.field = field.el.select('input', true).first();
24942         this.field.on("keydown", this.onPagingKeydown, this);
24943         this.field.on("focus", function(){this.dom.select();});
24944     
24945     
24946         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24947         //this.field.setHeight(18);
24948         //this.addSeparator();
24949         this.next = this.navgroup.addItem({
24950             tooltip: this.nextText,
24951             cls: "next btn-outline-secondary",
24952             html : ' <i class="fa fa-forward"></i>',
24953             disabled: true,
24954             preventDefault: true,
24955             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24956         });
24957         this.last = this.navgroup.addItem({
24958             tooltip: this.lastText,
24959             html : ' <i class="fa fa-step-forward"></i>',
24960             cls: "next btn-outline-secondary",
24961             disabled: true,
24962             preventDefault: true,
24963             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24964         });
24965     //this.addSeparator();
24966         this.loading = this.navgroup.addItem({
24967             tooltip: this.refreshText,
24968             cls: "btn-outline-secondary",
24969             html : ' <i class="fa fa-refresh"></i>',
24970             preventDefault: true,
24971             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24972         });
24973         
24974     },
24975
24976     // private
24977     updateInfo : function(){
24978         if(this.displayEl){
24979             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24980             var msg = count == 0 ?
24981                 this.emptyMsg :
24982                 String.format(
24983                     this.displayMsg,
24984                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24985                 );
24986             this.displayEl.update(msg);
24987         }
24988     },
24989
24990     // private
24991     onLoad : function(ds, r, o)
24992     {
24993         this.cursor = o.params.start ? o.params.start : 0;
24994         
24995         var d = this.getPageData(),
24996             ap = d.activePage,
24997             ps = d.pages;
24998         
24999         
25000         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25001         this.field.dom.value = ap;
25002         this.first.setDisabled(ap == 1);
25003         this.prev.setDisabled(ap == 1);
25004         this.next.setDisabled(ap == ps);
25005         this.last.setDisabled(ap == ps);
25006         this.loading.enable();
25007         this.updateInfo();
25008     },
25009
25010     // private
25011     getPageData : function(){
25012         var total = this.ds.getTotalCount();
25013         return {
25014             total : total,
25015             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25016             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25017         };
25018     },
25019
25020     // private
25021     onLoadError : function(){
25022         this.loading.enable();
25023     },
25024
25025     // private
25026     onPagingKeydown : function(e){
25027         var k = e.getKey();
25028         var d = this.getPageData();
25029         if(k == e.RETURN){
25030             var v = this.field.dom.value, pageNum;
25031             if(!v || isNaN(pageNum = parseInt(v, 10))){
25032                 this.field.dom.value = d.activePage;
25033                 return;
25034             }
25035             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25036             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25037             e.stopEvent();
25038         }
25039         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))
25040         {
25041           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25042           this.field.dom.value = pageNum;
25043           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25044           e.stopEvent();
25045         }
25046         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25047         {
25048           var v = this.field.dom.value, pageNum; 
25049           var increment = (e.shiftKey) ? 10 : 1;
25050           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25051                 increment *= -1;
25052           }
25053           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25054             this.field.dom.value = d.activePage;
25055             return;
25056           }
25057           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25058           {
25059             this.field.dom.value = parseInt(v, 10) + increment;
25060             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25061             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25062           }
25063           e.stopEvent();
25064         }
25065     },
25066
25067     // private
25068     beforeLoad : function(){
25069         if(this.loading){
25070             this.loading.disable();
25071         }
25072     },
25073
25074     // private
25075     onClick : function(which){
25076         
25077         var ds = this.ds;
25078         if (!ds) {
25079             return;
25080         }
25081         
25082         switch(which){
25083             case "first":
25084                 ds.load({params:{start: 0, limit: this.pageSize}});
25085             break;
25086             case "prev":
25087                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25088             break;
25089             case "next":
25090                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25091             break;
25092             case "last":
25093                 var total = ds.getTotalCount();
25094                 var extra = total % this.pageSize;
25095                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25096                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25097             break;
25098             case "refresh":
25099                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25100             break;
25101         }
25102     },
25103
25104     /**
25105      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25106      * @param {Roo.data.Store} store The data store to unbind
25107      */
25108     unbind : function(ds){
25109         ds.un("beforeload", this.beforeLoad, this);
25110         ds.un("load", this.onLoad, this);
25111         ds.un("loadexception", this.onLoadError, this);
25112         ds.un("remove", this.updateInfo, this);
25113         ds.un("add", this.updateInfo, this);
25114         this.ds = undefined;
25115     },
25116
25117     /**
25118      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25119      * @param {Roo.data.Store} store The data store to bind
25120      */
25121     bind : function(ds){
25122         ds.on("beforeload", this.beforeLoad, this);
25123         ds.on("load", this.onLoad, this);
25124         ds.on("loadexception", this.onLoadError, this);
25125         ds.on("remove", this.updateInfo, this);
25126         ds.on("add", this.updateInfo, this);
25127         this.ds = ds;
25128     }
25129 });/*
25130  * - LGPL
25131  *
25132  * element
25133  * 
25134  */
25135
25136 /**
25137  * @class Roo.bootstrap.MessageBar
25138  * @extends Roo.bootstrap.Component
25139  * Bootstrap MessageBar class
25140  * @cfg {String} html contents of the MessageBar
25141  * @cfg {String} weight (info | success | warning | danger) default info
25142  * @cfg {String} beforeClass insert the bar before the given class
25143  * @cfg {Boolean} closable (true | false) default false
25144  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25145  * 
25146  * @constructor
25147  * Create a new Element
25148  * @param {Object} config The config object
25149  */
25150
25151 Roo.bootstrap.MessageBar = function(config){
25152     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25153 };
25154
25155 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25156     
25157     html: '',
25158     weight: 'info',
25159     closable: false,
25160     fixed: false,
25161     beforeClass: 'bootstrap-sticky-wrap',
25162     
25163     getAutoCreate : function(){
25164         
25165         var cfg = {
25166             tag: 'div',
25167             cls: 'alert alert-dismissable alert-' + this.weight,
25168             cn: [
25169                 {
25170                     tag: 'span',
25171                     cls: 'message',
25172                     html: this.html || ''
25173                 }
25174             ]
25175         };
25176         
25177         if(this.fixed){
25178             cfg.cls += ' alert-messages-fixed';
25179         }
25180         
25181         if(this.closable){
25182             cfg.cn.push({
25183                 tag: 'button',
25184                 cls: 'close',
25185                 html: 'x'
25186             });
25187         }
25188         
25189         return cfg;
25190     },
25191     
25192     onRender : function(ct, position)
25193     {
25194         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25195         
25196         if(!this.el){
25197             var cfg = Roo.apply({},  this.getAutoCreate());
25198             cfg.id = Roo.id();
25199             
25200             if (this.cls) {
25201                 cfg.cls += ' ' + this.cls;
25202             }
25203             if (this.style) {
25204                 cfg.style = this.style;
25205             }
25206             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25207             
25208             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25209         }
25210         
25211         this.el.select('>button.close').on('click', this.hide, this);
25212         
25213     },
25214     
25215     show : function()
25216     {
25217         if (!this.rendered) {
25218             this.render();
25219         }
25220         
25221         this.el.show();
25222         
25223         this.fireEvent('show', this);
25224         
25225     },
25226     
25227     hide : function()
25228     {
25229         if (!this.rendered) {
25230             this.render();
25231         }
25232         
25233         this.el.hide();
25234         
25235         this.fireEvent('hide', this);
25236     },
25237     
25238     update : function()
25239     {
25240 //        var e = this.el.dom.firstChild;
25241 //        
25242 //        if(this.closable){
25243 //            e = e.nextSibling;
25244 //        }
25245 //        
25246 //        e.data = this.html || '';
25247
25248         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25249     }
25250    
25251 });
25252
25253  
25254
25255      /*
25256  * - LGPL
25257  *
25258  * Graph
25259  * 
25260  */
25261
25262
25263 /**
25264  * @class Roo.bootstrap.Graph
25265  * @extends Roo.bootstrap.Component
25266  * Bootstrap Graph class
25267 > Prameters
25268  -sm {number} sm 4
25269  -md {number} md 5
25270  @cfg {String} graphtype  bar | vbar | pie
25271  @cfg {number} g_x coodinator | centre x (pie)
25272  @cfg {number} g_y coodinator | centre y (pie)
25273  @cfg {number} g_r radius (pie)
25274  @cfg {number} g_height height of the chart (respected by all elements in the set)
25275  @cfg {number} g_width width of the chart (respected by all elements in the set)
25276  @cfg {Object} title The title of the chart
25277     
25278  -{Array}  values
25279  -opts (object) options for the chart 
25280      o {
25281      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25282      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25283      o vgutter (number)
25284      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.
25285      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25286      o to
25287      o stretch (boolean)
25288      o }
25289  -opts (object) options for the pie
25290      o{
25291      o cut
25292      o startAngle (number)
25293      o endAngle (number)
25294      } 
25295  *
25296  * @constructor
25297  * Create a new Input
25298  * @param {Object} config The config object
25299  */
25300
25301 Roo.bootstrap.Graph = function(config){
25302     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25303     
25304     this.addEvents({
25305         // img events
25306         /**
25307          * @event click
25308          * The img click event for the img.
25309          * @param {Roo.EventObject} e
25310          */
25311         "click" : true
25312     });
25313 };
25314
25315 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25316     
25317     sm: 4,
25318     md: 5,
25319     graphtype: 'bar',
25320     g_height: 250,
25321     g_width: 400,
25322     g_x: 50,
25323     g_y: 50,
25324     g_r: 30,
25325     opts:{
25326         //g_colors: this.colors,
25327         g_type: 'soft',
25328         g_gutter: '20%'
25329
25330     },
25331     title : false,
25332
25333     getAutoCreate : function(){
25334         
25335         var cfg = {
25336             tag: 'div',
25337             html : null
25338         };
25339         
25340         
25341         return  cfg;
25342     },
25343
25344     onRender : function(ct,position){
25345         
25346         
25347         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25348         
25349         if (typeof(Raphael) == 'undefined') {
25350             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25351             return;
25352         }
25353         
25354         this.raphael = Raphael(this.el.dom);
25355         
25356                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25357                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25358                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25359                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25360                 /*
25361                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25362                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25363                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25364                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25365                 
25366                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25367                 r.barchart(330, 10, 300, 220, data1);
25368                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25369                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25370                 */
25371                 
25372                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25373                 // r.barchart(30, 30, 560, 250,  xdata, {
25374                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25375                 //     axis : "0 0 1 1",
25376                 //     axisxlabels :  xdata
25377                 //     //yvalues : cols,
25378                    
25379                 // });
25380 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25381 //        
25382 //        this.load(null,xdata,{
25383 //                axis : "0 0 1 1",
25384 //                axisxlabels :  xdata
25385 //                });
25386
25387     },
25388
25389     load : function(graphtype,xdata,opts)
25390     {
25391         this.raphael.clear();
25392         if(!graphtype) {
25393             graphtype = this.graphtype;
25394         }
25395         if(!opts){
25396             opts = this.opts;
25397         }
25398         var r = this.raphael,
25399             fin = function () {
25400                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25401             },
25402             fout = function () {
25403                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25404             },
25405             pfin = function() {
25406                 this.sector.stop();
25407                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25408
25409                 if (this.label) {
25410                     this.label[0].stop();
25411                     this.label[0].attr({ r: 7.5 });
25412                     this.label[1].attr({ "font-weight": 800 });
25413                 }
25414             },
25415             pfout = function() {
25416                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25417
25418                 if (this.label) {
25419                     this.label[0].animate({ r: 5 }, 500, "bounce");
25420                     this.label[1].attr({ "font-weight": 400 });
25421                 }
25422             };
25423
25424         switch(graphtype){
25425             case 'bar':
25426                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25427                 break;
25428             case 'hbar':
25429                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25430                 break;
25431             case 'pie':
25432 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25433 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25434 //            
25435                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25436                 
25437                 break;
25438
25439         }
25440         
25441         if(this.title){
25442             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25443         }
25444         
25445     },
25446     
25447     setTitle: function(o)
25448     {
25449         this.title = o;
25450     },
25451     
25452     initEvents: function() {
25453         
25454         if(!this.href){
25455             this.el.on('click', this.onClick, this);
25456         }
25457     },
25458     
25459     onClick : function(e)
25460     {
25461         Roo.log('img onclick');
25462         this.fireEvent('click', this, e);
25463     }
25464    
25465 });
25466
25467  
25468 /*
25469  * - LGPL
25470  *
25471  * numberBox
25472  * 
25473  */
25474 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25475
25476 /**
25477  * @class Roo.bootstrap.dash.NumberBox
25478  * @extends Roo.bootstrap.Component
25479  * Bootstrap NumberBox class
25480  * @cfg {String} headline Box headline
25481  * @cfg {String} content Box content
25482  * @cfg {String} icon Box icon
25483  * @cfg {String} footer Footer text
25484  * @cfg {String} fhref Footer href
25485  * 
25486  * @constructor
25487  * Create a new NumberBox
25488  * @param {Object} config The config object
25489  */
25490
25491
25492 Roo.bootstrap.dash.NumberBox = function(config){
25493     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25494     
25495 };
25496
25497 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25498     
25499     headline : '',
25500     content : '',
25501     icon : '',
25502     footer : '',
25503     fhref : '',
25504     ficon : '',
25505     
25506     getAutoCreate : function(){
25507         
25508         var cfg = {
25509             tag : 'div',
25510             cls : 'small-box ',
25511             cn : [
25512                 {
25513                     tag : 'div',
25514                     cls : 'inner',
25515                     cn :[
25516                         {
25517                             tag : 'h3',
25518                             cls : 'roo-headline',
25519                             html : this.headline
25520                         },
25521                         {
25522                             tag : 'p',
25523                             cls : 'roo-content',
25524                             html : this.content
25525                         }
25526                     ]
25527                 }
25528             ]
25529         };
25530         
25531         if(this.icon){
25532             cfg.cn.push({
25533                 tag : 'div',
25534                 cls : 'icon',
25535                 cn :[
25536                     {
25537                         tag : 'i',
25538                         cls : 'ion ' + this.icon
25539                     }
25540                 ]
25541             });
25542         }
25543         
25544         if(this.footer){
25545             var footer = {
25546                 tag : 'a',
25547                 cls : 'small-box-footer',
25548                 href : this.fhref || '#',
25549                 html : this.footer
25550             };
25551             
25552             cfg.cn.push(footer);
25553             
25554         }
25555         
25556         return  cfg;
25557     },
25558
25559     onRender : function(ct,position){
25560         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25561
25562
25563        
25564                 
25565     },
25566
25567     setHeadline: function (value)
25568     {
25569         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25570     },
25571     
25572     setFooter: function (value, href)
25573     {
25574         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25575         
25576         if(href){
25577             this.el.select('a.small-box-footer',true).first().attr('href', href);
25578         }
25579         
25580     },
25581
25582     setContent: function (value)
25583     {
25584         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25585     },
25586
25587     initEvents: function() 
25588     {   
25589         
25590     }
25591     
25592 });
25593
25594  
25595 /*
25596  * - LGPL
25597  *
25598  * TabBox
25599  * 
25600  */
25601 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25602
25603 /**
25604  * @class Roo.bootstrap.dash.TabBox
25605  * @extends Roo.bootstrap.Component
25606  * Bootstrap TabBox class
25607  * @cfg {String} title Title of the TabBox
25608  * @cfg {String} icon Icon of the TabBox
25609  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25610  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25611  * 
25612  * @constructor
25613  * Create a new TabBox
25614  * @param {Object} config The config object
25615  */
25616
25617
25618 Roo.bootstrap.dash.TabBox = function(config){
25619     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25620     this.addEvents({
25621         // raw events
25622         /**
25623          * @event addpane
25624          * When a pane is added
25625          * @param {Roo.bootstrap.dash.TabPane} pane
25626          */
25627         "addpane" : true,
25628         /**
25629          * @event activatepane
25630          * When a pane is activated
25631          * @param {Roo.bootstrap.dash.TabPane} pane
25632          */
25633         "activatepane" : true
25634         
25635          
25636     });
25637     
25638     this.panes = [];
25639 };
25640
25641 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25642
25643     title : '',
25644     icon : false,
25645     showtabs : true,
25646     tabScrollable : false,
25647     
25648     getChildContainer : function()
25649     {
25650         return this.el.select('.tab-content', true).first();
25651     },
25652     
25653     getAutoCreate : function(){
25654         
25655         var header = {
25656             tag: 'li',
25657             cls: 'pull-left header',
25658             html: this.title,
25659             cn : []
25660         };
25661         
25662         if(this.icon){
25663             header.cn.push({
25664                 tag: 'i',
25665                 cls: 'fa ' + this.icon
25666             });
25667         }
25668         
25669         var h = {
25670             tag: 'ul',
25671             cls: 'nav nav-tabs pull-right',
25672             cn: [
25673                 header
25674             ]
25675         };
25676         
25677         if(this.tabScrollable){
25678             h = {
25679                 tag: 'div',
25680                 cls: 'tab-header',
25681                 cn: [
25682                     {
25683                         tag: 'ul',
25684                         cls: 'nav nav-tabs pull-right',
25685                         cn: [
25686                             header
25687                         ]
25688                     }
25689                 ]
25690             };
25691         }
25692         
25693         var cfg = {
25694             tag: 'div',
25695             cls: 'nav-tabs-custom',
25696             cn: [
25697                 h,
25698                 {
25699                     tag: 'div',
25700                     cls: 'tab-content no-padding',
25701                     cn: []
25702                 }
25703             ]
25704         };
25705
25706         return  cfg;
25707     },
25708     initEvents : function()
25709     {
25710         //Roo.log('add add pane handler');
25711         this.on('addpane', this.onAddPane, this);
25712     },
25713      /**
25714      * Updates the box title
25715      * @param {String} html to set the title to.
25716      */
25717     setTitle : function(value)
25718     {
25719         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25720     },
25721     onAddPane : function(pane)
25722     {
25723         this.panes.push(pane);
25724         //Roo.log('addpane');
25725         //Roo.log(pane);
25726         // tabs are rendere left to right..
25727         if(!this.showtabs){
25728             return;
25729         }
25730         
25731         var ctr = this.el.select('.nav-tabs', true).first();
25732          
25733          
25734         var existing = ctr.select('.nav-tab',true);
25735         var qty = existing.getCount();;
25736         
25737         
25738         var tab = ctr.createChild({
25739             tag : 'li',
25740             cls : 'nav-tab' + (qty ? '' : ' active'),
25741             cn : [
25742                 {
25743                     tag : 'a',
25744                     href:'#',
25745                     html : pane.title
25746                 }
25747             ]
25748         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25749         pane.tab = tab;
25750         
25751         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25752         if (!qty) {
25753             pane.el.addClass('active');
25754         }
25755         
25756                 
25757     },
25758     onTabClick : function(ev,un,ob,pane)
25759     {
25760         //Roo.log('tab - prev default');
25761         ev.preventDefault();
25762         
25763         
25764         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25765         pane.tab.addClass('active');
25766         //Roo.log(pane.title);
25767         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25768         // technically we should have a deactivate event.. but maybe add later.
25769         // and it should not de-activate the selected tab...
25770         this.fireEvent('activatepane', pane);
25771         pane.el.addClass('active');
25772         pane.fireEvent('activate');
25773         
25774         
25775     },
25776     
25777     getActivePane : function()
25778     {
25779         var r = false;
25780         Roo.each(this.panes, function(p) {
25781             if(p.el.hasClass('active')){
25782                 r = p;
25783                 return false;
25784             }
25785             
25786             return;
25787         });
25788         
25789         return r;
25790     }
25791     
25792     
25793 });
25794
25795  
25796 /*
25797  * - LGPL
25798  *
25799  * Tab pane
25800  * 
25801  */
25802 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25803 /**
25804  * @class Roo.bootstrap.TabPane
25805  * @extends Roo.bootstrap.Component
25806  * Bootstrap TabPane class
25807  * @cfg {Boolean} active (false | true) Default false
25808  * @cfg {String} title title of panel
25809
25810  * 
25811  * @constructor
25812  * Create a new TabPane
25813  * @param {Object} config The config object
25814  */
25815
25816 Roo.bootstrap.dash.TabPane = function(config){
25817     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25818     
25819     this.addEvents({
25820         // raw events
25821         /**
25822          * @event activate
25823          * When a pane is activated
25824          * @param {Roo.bootstrap.dash.TabPane} pane
25825          */
25826         "activate" : true
25827          
25828     });
25829 };
25830
25831 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25832     
25833     active : false,
25834     title : '',
25835     
25836     // the tabBox that this is attached to.
25837     tab : false,
25838      
25839     getAutoCreate : function() 
25840     {
25841         var cfg = {
25842             tag: 'div',
25843             cls: 'tab-pane'
25844         };
25845         
25846         if(this.active){
25847             cfg.cls += ' active';
25848         }
25849         
25850         return cfg;
25851     },
25852     initEvents  : function()
25853     {
25854         //Roo.log('trigger add pane handler');
25855         this.parent().fireEvent('addpane', this)
25856     },
25857     
25858      /**
25859      * Updates the tab title 
25860      * @param {String} html to set the title to.
25861      */
25862     setTitle: function(str)
25863     {
25864         if (!this.tab) {
25865             return;
25866         }
25867         this.title = str;
25868         this.tab.select('a', true).first().dom.innerHTML = str;
25869         
25870     }
25871     
25872     
25873     
25874 });
25875
25876  
25877
25878
25879  /*
25880  * - LGPL
25881  *
25882  * menu
25883  * 
25884  */
25885 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25886
25887 /**
25888  * @class Roo.bootstrap.menu.Menu
25889  * @extends Roo.bootstrap.Component
25890  * Bootstrap Menu class - container for Menu
25891  * @cfg {String} html Text of the menu
25892  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25893  * @cfg {String} icon Font awesome icon
25894  * @cfg {String} pos Menu align to (top | bottom) default bottom
25895  * 
25896  * 
25897  * @constructor
25898  * Create a new Menu
25899  * @param {Object} config The config object
25900  */
25901
25902
25903 Roo.bootstrap.menu.Menu = function(config){
25904     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25905     
25906     this.addEvents({
25907         /**
25908          * @event beforeshow
25909          * Fires before this menu is displayed
25910          * @param {Roo.bootstrap.menu.Menu} this
25911          */
25912         beforeshow : true,
25913         /**
25914          * @event beforehide
25915          * Fires before this menu is hidden
25916          * @param {Roo.bootstrap.menu.Menu} this
25917          */
25918         beforehide : true,
25919         /**
25920          * @event show
25921          * Fires after this menu is displayed
25922          * @param {Roo.bootstrap.menu.Menu} this
25923          */
25924         show : true,
25925         /**
25926          * @event hide
25927          * Fires after this menu is hidden
25928          * @param {Roo.bootstrap.menu.Menu} this
25929          */
25930         hide : true,
25931         /**
25932          * @event click
25933          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25934          * @param {Roo.bootstrap.menu.Menu} this
25935          * @param {Roo.EventObject} e
25936          */
25937         click : true
25938     });
25939     
25940 };
25941
25942 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25943     
25944     submenu : false,
25945     html : '',
25946     weight : 'default',
25947     icon : false,
25948     pos : 'bottom',
25949     
25950     
25951     getChildContainer : function() {
25952         if(this.isSubMenu){
25953             return this.el;
25954         }
25955         
25956         return this.el.select('ul.dropdown-menu', true).first();  
25957     },
25958     
25959     getAutoCreate : function()
25960     {
25961         var text = [
25962             {
25963                 tag : 'span',
25964                 cls : 'roo-menu-text',
25965                 html : this.html
25966             }
25967         ];
25968         
25969         if(this.icon){
25970             text.unshift({
25971                 tag : 'i',
25972                 cls : 'fa ' + this.icon
25973             })
25974         }
25975         
25976         
25977         var cfg = {
25978             tag : 'div',
25979             cls : 'btn-group',
25980             cn : [
25981                 {
25982                     tag : 'button',
25983                     cls : 'dropdown-button btn btn-' + this.weight,
25984                     cn : text
25985                 },
25986                 {
25987                     tag : 'button',
25988                     cls : 'dropdown-toggle btn btn-' + this.weight,
25989                     cn : [
25990                         {
25991                             tag : 'span',
25992                             cls : 'caret'
25993                         }
25994                     ]
25995                 },
25996                 {
25997                     tag : 'ul',
25998                     cls : 'dropdown-menu'
25999                 }
26000             ]
26001             
26002         };
26003         
26004         if(this.pos == 'top'){
26005             cfg.cls += ' dropup';
26006         }
26007         
26008         if(this.isSubMenu){
26009             cfg = {
26010                 tag : 'ul',
26011                 cls : 'dropdown-menu'
26012             }
26013         }
26014         
26015         return cfg;
26016     },
26017     
26018     onRender : function(ct, position)
26019     {
26020         this.isSubMenu = ct.hasClass('dropdown-submenu');
26021         
26022         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26023     },
26024     
26025     initEvents : function() 
26026     {
26027         if(this.isSubMenu){
26028             return;
26029         }
26030         
26031         this.hidden = true;
26032         
26033         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26034         this.triggerEl.on('click', this.onTriggerPress, this);
26035         
26036         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26037         this.buttonEl.on('click', this.onClick, this);
26038         
26039     },
26040     
26041     list : function()
26042     {
26043         if(this.isSubMenu){
26044             return this.el;
26045         }
26046         
26047         return this.el.select('ul.dropdown-menu', true).first();
26048     },
26049     
26050     onClick : function(e)
26051     {
26052         this.fireEvent("click", this, e);
26053     },
26054     
26055     onTriggerPress  : function(e)
26056     {   
26057         if (this.isVisible()) {
26058             this.hide();
26059         } else {
26060             this.show();
26061         }
26062     },
26063     
26064     isVisible : function(){
26065         return !this.hidden;
26066     },
26067     
26068     show : function()
26069     {
26070         this.fireEvent("beforeshow", this);
26071         
26072         this.hidden = false;
26073         this.el.addClass('open');
26074         
26075         Roo.get(document).on("mouseup", this.onMouseUp, this);
26076         
26077         this.fireEvent("show", this);
26078         
26079         
26080     },
26081     
26082     hide : function()
26083     {
26084         this.fireEvent("beforehide", this);
26085         
26086         this.hidden = true;
26087         this.el.removeClass('open');
26088         
26089         Roo.get(document).un("mouseup", this.onMouseUp);
26090         
26091         this.fireEvent("hide", this);
26092     },
26093     
26094     onMouseUp : function()
26095     {
26096         this.hide();
26097     }
26098     
26099 });
26100
26101  
26102  /*
26103  * - LGPL
26104  *
26105  * menu item
26106  * 
26107  */
26108 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26109
26110 /**
26111  * @class Roo.bootstrap.menu.Item
26112  * @extends Roo.bootstrap.Component
26113  * Bootstrap MenuItem class
26114  * @cfg {Boolean} submenu (true | false) default false
26115  * @cfg {String} html text of the item
26116  * @cfg {String} href the link
26117  * @cfg {Boolean} disable (true | false) default false
26118  * @cfg {Boolean} preventDefault (true | false) default true
26119  * @cfg {String} icon Font awesome icon
26120  * @cfg {String} pos Submenu align to (left | right) default right 
26121  * 
26122  * 
26123  * @constructor
26124  * Create a new Item
26125  * @param {Object} config The config object
26126  */
26127
26128
26129 Roo.bootstrap.menu.Item = function(config){
26130     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26131     this.addEvents({
26132         /**
26133          * @event mouseover
26134          * Fires when the mouse is hovering over this menu
26135          * @param {Roo.bootstrap.menu.Item} this
26136          * @param {Roo.EventObject} e
26137          */
26138         mouseover : true,
26139         /**
26140          * @event mouseout
26141          * Fires when the mouse exits this menu
26142          * @param {Roo.bootstrap.menu.Item} this
26143          * @param {Roo.EventObject} e
26144          */
26145         mouseout : true,
26146         // raw events
26147         /**
26148          * @event click
26149          * The raw click event for the entire grid.
26150          * @param {Roo.EventObject} e
26151          */
26152         click : true
26153     });
26154 };
26155
26156 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26157     
26158     submenu : false,
26159     href : '',
26160     html : '',
26161     preventDefault: true,
26162     disable : false,
26163     icon : false,
26164     pos : 'right',
26165     
26166     getAutoCreate : function()
26167     {
26168         var text = [
26169             {
26170                 tag : 'span',
26171                 cls : 'roo-menu-item-text',
26172                 html : this.html
26173             }
26174         ];
26175         
26176         if(this.icon){
26177             text.unshift({
26178                 tag : 'i',
26179                 cls : 'fa ' + this.icon
26180             })
26181         }
26182         
26183         var cfg = {
26184             tag : 'li',
26185             cn : [
26186                 {
26187                     tag : 'a',
26188                     href : this.href || '#',
26189                     cn : text
26190                 }
26191             ]
26192         };
26193         
26194         if(this.disable){
26195             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26196         }
26197         
26198         if(this.submenu){
26199             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26200             
26201             if(this.pos == 'left'){
26202                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26203             }
26204         }
26205         
26206         return cfg;
26207     },
26208     
26209     initEvents : function() 
26210     {
26211         this.el.on('mouseover', this.onMouseOver, this);
26212         this.el.on('mouseout', this.onMouseOut, this);
26213         
26214         this.el.select('a', true).first().on('click', this.onClick, this);
26215         
26216     },
26217     
26218     onClick : function(e)
26219     {
26220         if(this.preventDefault){
26221             e.preventDefault();
26222         }
26223         
26224         this.fireEvent("click", this, e);
26225     },
26226     
26227     onMouseOver : function(e)
26228     {
26229         if(this.submenu && this.pos == 'left'){
26230             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26231         }
26232         
26233         this.fireEvent("mouseover", this, e);
26234     },
26235     
26236     onMouseOut : function(e)
26237     {
26238         this.fireEvent("mouseout", this, e);
26239     }
26240 });
26241
26242  
26243
26244  /*
26245  * - LGPL
26246  *
26247  * menu separator
26248  * 
26249  */
26250 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26251
26252 /**
26253  * @class Roo.bootstrap.menu.Separator
26254  * @extends Roo.bootstrap.Component
26255  * Bootstrap Separator class
26256  * 
26257  * @constructor
26258  * Create a new Separator
26259  * @param {Object} config The config object
26260  */
26261
26262
26263 Roo.bootstrap.menu.Separator = function(config){
26264     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26265 };
26266
26267 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26268     
26269     getAutoCreate : function(){
26270         var cfg = {
26271             tag : 'li',
26272             cls: 'divider'
26273         };
26274         
26275         return cfg;
26276     }
26277    
26278 });
26279
26280  
26281
26282  /*
26283  * - LGPL
26284  *
26285  * Tooltip
26286  * 
26287  */
26288
26289 /**
26290  * @class Roo.bootstrap.Tooltip
26291  * Bootstrap Tooltip class
26292  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26293  * to determine which dom element triggers the tooltip.
26294  * 
26295  * It needs to add support for additional attributes like tooltip-position
26296  * 
26297  * @constructor
26298  * Create a new Toolti
26299  * @param {Object} config The config object
26300  */
26301
26302 Roo.bootstrap.Tooltip = function(config){
26303     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26304     
26305     this.alignment = Roo.bootstrap.Tooltip.alignment;
26306     
26307     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26308         this.alignment = config.alignment;
26309     }
26310     
26311 };
26312
26313 Roo.apply(Roo.bootstrap.Tooltip, {
26314     /**
26315      * @function init initialize tooltip monitoring.
26316      * @static
26317      */
26318     currentEl : false,
26319     currentTip : false,
26320     currentRegion : false,
26321     
26322     //  init : delay?
26323     
26324     init : function()
26325     {
26326         Roo.get(document).on('mouseover', this.enter ,this);
26327         Roo.get(document).on('mouseout', this.leave, this);
26328          
26329         
26330         this.currentTip = new Roo.bootstrap.Tooltip();
26331     },
26332     
26333     enter : function(ev)
26334     {
26335         var dom = ev.getTarget();
26336         
26337         //Roo.log(['enter',dom]);
26338         var el = Roo.fly(dom);
26339         if (this.currentEl) {
26340             //Roo.log(dom);
26341             //Roo.log(this.currentEl);
26342             //Roo.log(this.currentEl.contains(dom));
26343             if (this.currentEl == el) {
26344                 return;
26345             }
26346             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26347                 return;
26348             }
26349
26350         }
26351         
26352         if (this.currentTip.el) {
26353             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26354         }    
26355         //Roo.log(ev);
26356         
26357         if(!el || el.dom == document){
26358             return;
26359         }
26360         
26361         var bindEl = el;
26362         
26363         // you can not look for children, as if el is the body.. then everythign is the child..
26364         if (!el.attr('tooltip')) { //
26365             if (!el.select("[tooltip]").elements.length) {
26366                 return;
26367             }
26368             // is the mouse over this child...?
26369             bindEl = el.select("[tooltip]").first();
26370             var xy = ev.getXY();
26371             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26372                 //Roo.log("not in region.");
26373                 return;
26374             }
26375             //Roo.log("child element over..");
26376             
26377         }
26378         this.currentEl = bindEl;
26379         this.currentTip.bind(bindEl);
26380         this.currentRegion = Roo.lib.Region.getRegion(dom);
26381         this.currentTip.enter();
26382         
26383     },
26384     leave : function(ev)
26385     {
26386         var dom = ev.getTarget();
26387         //Roo.log(['leave',dom]);
26388         if (!this.currentEl) {
26389             return;
26390         }
26391         
26392         
26393         if (dom != this.currentEl.dom) {
26394             return;
26395         }
26396         var xy = ev.getXY();
26397         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26398             return;
26399         }
26400         // only activate leave if mouse cursor is outside... bounding box..
26401         
26402         
26403         
26404         
26405         if (this.currentTip) {
26406             this.currentTip.leave();
26407         }
26408         //Roo.log('clear currentEl');
26409         this.currentEl = false;
26410         
26411         
26412     },
26413     alignment : {
26414         'left' : ['r-l', [-2,0], 'right'],
26415         'right' : ['l-r', [2,0], 'left'],
26416         'bottom' : ['t-b', [0,2], 'top'],
26417         'top' : [ 'b-t', [0,-2], 'bottom']
26418     }
26419     
26420 });
26421
26422
26423 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26424     
26425     
26426     bindEl : false,
26427     
26428     delay : null, // can be { show : 300 , hide: 500}
26429     
26430     timeout : null,
26431     
26432     hoverState : null, //???
26433     
26434     placement : 'bottom', 
26435     
26436     alignment : false,
26437     
26438     getAutoCreate : function(){
26439     
26440         var cfg = {
26441            cls : 'tooltip',
26442            role : 'tooltip',
26443            cn : [
26444                 {
26445                     cls : 'tooltip-arrow'
26446                 },
26447                 {
26448                     cls : 'tooltip-inner'
26449                 }
26450            ]
26451         };
26452         
26453         return cfg;
26454     },
26455     bind : function(el)
26456     {
26457         this.bindEl = el;
26458     },
26459       
26460     
26461     enter : function () {
26462        
26463         if (this.timeout != null) {
26464             clearTimeout(this.timeout);
26465         }
26466         
26467         this.hoverState = 'in';
26468          //Roo.log("enter - show");
26469         if (!this.delay || !this.delay.show) {
26470             this.show();
26471             return;
26472         }
26473         var _t = this;
26474         this.timeout = setTimeout(function () {
26475             if (_t.hoverState == 'in') {
26476                 _t.show();
26477             }
26478         }, this.delay.show);
26479     },
26480     leave : function()
26481     {
26482         clearTimeout(this.timeout);
26483     
26484         this.hoverState = 'out';
26485          if (!this.delay || !this.delay.hide) {
26486             this.hide();
26487             return;
26488         }
26489        
26490         var _t = this;
26491         this.timeout = setTimeout(function () {
26492             //Roo.log("leave - timeout");
26493             
26494             if (_t.hoverState == 'out') {
26495                 _t.hide();
26496                 Roo.bootstrap.Tooltip.currentEl = false;
26497             }
26498         }, delay);
26499     },
26500     
26501     show : function (msg)
26502     {
26503         if (!this.el) {
26504             this.render(document.body);
26505         }
26506         // set content.
26507         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26508         
26509         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26510         
26511         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26512         
26513         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26514         
26515         var placement = typeof this.placement == 'function' ?
26516             this.placement.call(this, this.el, on_el) :
26517             this.placement;
26518             
26519         var autoToken = /\s?auto?\s?/i;
26520         var autoPlace = autoToken.test(placement);
26521         if (autoPlace) {
26522             placement = placement.replace(autoToken, '') || 'top';
26523         }
26524         
26525         //this.el.detach()
26526         //this.el.setXY([0,0]);
26527         this.el.show();
26528         //this.el.dom.style.display='block';
26529         
26530         //this.el.appendTo(on_el);
26531         
26532         var p = this.getPosition();
26533         var box = this.el.getBox();
26534         
26535         if (autoPlace) {
26536             // fixme..
26537         }
26538         
26539         var align = this.alignment[placement];
26540         
26541         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26542         
26543         if(placement == 'top' || placement == 'bottom'){
26544             if(xy[0] < 0){
26545                 placement = 'right';
26546             }
26547             
26548             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26549                 placement = 'left';
26550             }
26551             
26552             var scroll = Roo.select('body', true).first().getScroll();
26553             
26554             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26555                 placement = 'top';
26556             }
26557             
26558             align = this.alignment[placement];
26559         }
26560         
26561         this.el.alignTo(this.bindEl, align[0],align[1]);
26562         //var arrow = this.el.select('.arrow',true).first();
26563         //arrow.set(align[2], 
26564         
26565         this.el.addClass(placement);
26566         
26567         this.el.addClass('in fade');
26568         
26569         this.hoverState = null;
26570         
26571         if (this.el.hasClass('fade')) {
26572             // fade it?
26573         }
26574         
26575     },
26576     hide : function()
26577     {
26578          
26579         if (!this.el) {
26580             return;
26581         }
26582         //this.el.setXY([0,0]);
26583         this.el.removeClass('in');
26584         //this.el.hide();
26585         
26586     }
26587     
26588 });
26589  
26590
26591  /*
26592  * - LGPL
26593  *
26594  * Location Picker
26595  * 
26596  */
26597
26598 /**
26599  * @class Roo.bootstrap.LocationPicker
26600  * @extends Roo.bootstrap.Component
26601  * Bootstrap LocationPicker class
26602  * @cfg {Number} latitude Position when init default 0
26603  * @cfg {Number} longitude Position when init default 0
26604  * @cfg {Number} zoom default 15
26605  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26606  * @cfg {Boolean} mapTypeControl default false
26607  * @cfg {Boolean} disableDoubleClickZoom default false
26608  * @cfg {Boolean} scrollwheel default true
26609  * @cfg {Boolean} streetViewControl default false
26610  * @cfg {Number} radius default 0
26611  * @cfg {String} locationName
26612  * @cfg {Boolean} draggable default true
26613  * @cfg {Boolean} enableAutocomplete default false
26614  * @cfg {Boolean} enableReverseGeocode default true
26615  * @cfg {String} markerTitle
26616  * 
26617  * @constructor
26618  * Create a new LocationPicker
26619  * @param {Object} config The config object
26620  */
26621
26622
26623 Roo.bootstrap.LocationPicker = function(config){
26624     
26625     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26626     
26627     this.addEvents({
26628         /**
26629          * @event initial
26630          * Fires when the picker initialized.
26631          * @param {Roo.bootstrap.LocationPicker} this
26632          * @param {Google Location} location
26633          */
26634         initial : true,
26635         /**
26636          * @event positionchanged
26637          * Fires when the picker position changed.
26638          * @param {Roo.bootstrap.LocationPicker} this
26639          * @param {Google Location} location
26640          */
26641         positionchanged : true,
26642         /**
26643          * @event resize
26644          * Fires when the map resize.
26645          * @param {Roo.bootstrap.LocationPicker} this
26646          */
26647         resize : true,
26648         /**
26649          * @event show
26650          * Fires when the map show.
26651          * @param {Roo.bootstrap.LocationPicker} this
26652          */
26653         show : true,
26654         /**
26655          * @event hide
26656          * Fires when the map hide.
26657          * @param {Roo.bootstrap.LocationPicker} this
26658          */
26659         hide : true,
26660         /**
26661          * @event mapClick
26662          * Fires when click the map.
26663          * @param {Roo.bootstrap.LocationPicker} this
26664          * @param {Map event} e
26665          */
26666         mapClick : true,
26667         /**
26668          * @event mapRightClick
26669          * Fires when right click the map.
26670          * @param {Roo.bootstrap.LocationPicker} this
26671          * @param {Map event} e
26672          */
26673         mapRightClick : true,
26674         /**
26675          * @event markerClick
26676          * Fires when click the marker.
26677          * @param {Roo.bootstrap.LocationPicker} this
26678          * @param {Map event} e
26679          */
26680         markerClick : true,
26681         /**
26682          * @event markerRightClick
26683          * Fires when right click the marker.
26684          * @param {Roo.bootstrap.LocationPicker} this
26685          * @param {Map event} e
26686          */
26687         markerRightClick : true,
26688         /**
26689          * @event OverlayViewDraw
26690          * Fires when OverlayView Draw
26691          * @param {Roo.bootstrap.LocationPicker} this
26692          */
26693         OverlayViewDraw : true,
26694         /**
26695          * @event OverlayViewOnAdd
26696          * Fires when OverlayView Draw
26697          * @param {Roo.bootstrap.LocationPicker} this
26698          */
26699         OverlayViewOnAdd : true,
26700         /**
26701          * @event OverlayViewOnRemove
26702          * Fires when OverlayView Draw
26703          * @param {Roo.bootstrap.LocationPicker} this
26704          */
26705         OverlayViewOnRemove : true,
26706         /**
26707          * @event OverlayViewShow
26708          * Fires when OverlayView Draw
26709          * @param {Roo.bootstrap.LocationPicker} this
26710          * @param {Pixel} cpx
26711          */
26712         OverlayViewShow : true,
26713         /**
26714          * @event OverlayViewHide
26715          * Fires when OverlayView Draw
26716          * @param {Roo.bootstrap.LocationPicker} this
26717          */
26718         OverlayViewHide : true,
26719         /**
26720          * @event loadexception
26721          * Fires when load google lib failed.
26722          * @param {Roo.bootstrap.LocationPicker} this
26723          */
26724         loadexception : true
26725     });
26726         
26727 };
26728
26729 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26730     
26731     gMapContext: false,
26732     
26733     latitude: 0,
26734     longitude: 0,
26735     zoom: 15,
26736     mapTypeId: false,
26737     mapTypeControl: false,
26738     disableDoubleClickZoom: false,
26739     scrollwheel: true,
26740     streetViewControl: false,
26741     radius: 0,
26742     locationName: '',
26743     draggable: true,
26744     enableAutocomplete: false,
26745     enableReverseGeocode: true,
26746     markerTitle: '',
26747     
26748     getAutoCreate: function()
26749     {
26750
26751         var cfg = {
26752             tag: 'div',
26753             cls: 'roo-location-picker'
26754         };
26755         
26756         return cfg
26757     },
26758     
26759     initEvents: function(ct, position)
26760     {       
26761         if(!this.el.getWidth() || this.isApplied()){
26762             return;
26763         }
26764         
26765         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26766         
26767         this.initial();
26768     },
26769     
26770     initial: function()
26771     {
26772         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26773             this.fireEvent('loadexception', this);
26774             return;
26775         }
26776         
26777         if(!this.mapTypeId){
26778             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26779         }
26780         
26781         this.gMapContext = this.GMapContext();
26782         
26783         this.initOverlayView();
26784         
26785         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26786         
26787         var _this = this;
26788                 
26789         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26790             _this.setPosition(_this.gMapContext.marker.position);
26791         });
26792         
26793         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26794             _this.fireEvent('mapClick', this, event);
26795             
26796         });
26797
26798         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26799             _this.fireEvent('mapRightClick', this, event);
26800             
26801         });
26802         
26803         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26804             _this.fireEvent('markerClick', this, event);
26805             
26806         });
26807
26808         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26809             _this.fireEvent('markerRightClick', this, event);
26810             
26811         });
26812         
26813         this.setPosition(this.gMapContext.location);
26814         
26815         this.fireEvent('initial', this, this.gMapContext.location);
26816     },
26817     
26818     initOverlayView: function()
26819     {
26820         var _this = this;
26821         
26822         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26823             
26824             draw: function()
26825             {
26826                 _this.fireEvent('OverlayViewDraw', _this);
26827             },
26828             
26829             onAdd: function()
26830             {
26831                 _this.fireEvent('OverlayViewOnAdd', _this);
26832             },
26833             
26834             onRemove: function()
26835             {
26836                 _this.fireEvent('OverlayViewOnRemove', _this);
26837             },
26838             
26839             show: function(cpx)
26840             {
26841                 _this.fireEvent('OverlayViewShow', _this, cpx);
26842             },
26843             
26844             hide: function()
26845             {
26846                 _this.fireEvent('OverlayViewHide', _this);
26847             }
26848             
26849         });
26850     },
26851     
26852     fromLatLngToContainerPixel: function(event)
26853     {
26854         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26855     },
26856     
26857     isApplied: function() 
26858     {
26859         return this.getGmapContext() == false ? false : true;
26860     },
26861     
26862     getGmapContext: function() 
26863     {
26864         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26865     },
26866     
26867     GMapContext: function() 
26868     {
26869         var position = new google.maps.LatLng(this.latitude, this.longitude);
26870         
26871         var _map = new google.maps.Map(this.el.dom, {
26872             center: position,
26873             zoom: this.zoom,
26874             mapTypeId: this.mapTypeId,
26875             mapTypeControl: this.mapTypeControl,
26876             disableDoubleClickZoom: this.disableDoubleClickZoom,
26877             scrollwheel: this.scrollwheel,
26878             streetViewControl: this.streetViewControl,
26879             locationName: this.locationName,
26880             draggable: this.draggable,
26881             enableAutocomplete: this.enableAutocomplete,
26882             enableReverseGeocode: this.enableReverseGeocode
26883         });
26884         
26885         var _marker = new google.maps.Marker({
26886             position: position,
26887             map: _map,
26888             title: this.markerTitle,
26889             draggable: this.draggable
26890         });
26891         
26892         return {
26893             map: _map,
26894             marker: _marker,
26895             circle: null,
26896             location: position,
26897             radius: this.radius,
26898             locationName: this.locationName,
26899             addressComponents: {
26900                 formatted_address: null,
26901                 addressLine1: null,
26902                 addressLine2: null,
26903                 streetName: null,
26904                 streetNumber: null,
26905                 city: null,
26906                 district: null,
26907                 state: null,
26908                 stateOrProvince: null
26909             },
26910             settings: this,
26911             domContainer: this.el.dom,
26912             geodecoder: new google.maps.Geocoder()
26913         };
26914     },
26915     
26916     drawCircle: function(center, radius, options) 
26917     {
26918         if (this.gMapContext.circle != null) {
26919             this.gMapContext.circle.setMap(null);
26920         }
26921         if (radius > 0) {
26922             radius *= 1;
26923             options = Roo.apply({}, options, {
26924                 strokeColor: "#0000FF",
26925                 strokeOpacity: .35,
26926                 strokeWeight: 2,
26927                 fillColor: "#0000FF",
26928                 fillOpacity: .2
26929             });
26930             
26931             options.map = this.gMapContext.map;
26932             options.radius = radius;
26933             options.center = center;
26934             this.gMapContext.circle = new google.maps.Circle(options);
26935             return this.gMapContext.circle;
26936         }
26937         
26938         return null;
26939     },
26940     
26941     setPosition: function(location) 
26942     {
26943         this.gMapContext.location = location;
26944         this.gMapContext.marker.setPosition(location);
26945         this.gMapContext.map.panTo(location);
26946         this.drawCircle(location, this.gMapContext.radius, {});
26947         
26948         var _this = this;
26949         
26950         if (this.gMapContext.settings.enableReverseGeocode) {
26951             this.gMapContext.geodecoder.geocode({
26952                 latLng: this.gMapContext.location
26953             }, function(results, status) {
26954                 
26955                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26956                     _this.gMapContext.locationName = results[0].formatted_address;
26957                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26958                     
26959                     _this.fireEvent('positionchanged', this, location);
26960                 }
26961             });
26962             
26963             return;
26964         }
26965         
26966         this.fireEvent('positionchanged', this, location);
26967     },
26968     
26969     resize: function()
26970     {
26971         google.maps.event.trigger(this.gMapContext.map, "resize");
26972         
26973         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26974         
26975         this.fireEvent('resize', this);
26976     },
26977     
26978     setPositionByLatLng: function(latitude, longitude)
26979     {
26980         this.setPosition(new google.maps.LatLng(latitude, longitude));
26981     },
26982     
26983     getCurrentPosition: function() 
26984     {
26985         return {
26986             latitude: this.gMapContext.location.lat(),
26987             longitude: this.gMapContext.location.lng()
26988         };
26989     },
26990     
26991     getAddressName: function() 
26992     {
26993         return this.gMapContext.locationName;
26994     },
26995     
26996     getAddressComponents: function() 
26997     {
26998         return this.gMapContext.addressComponents;
26999     },
27000     
27001     address_component_from_google_geocode: function(address_components) 
27002     {
27003         var result = {};
27004         
27005         for (var i = 0; i < address_components.length; i++) {
27006             var component = address_components[i];
27007             if (component.types.indexOf("postal_code") >= 0) {
27008                 result.postalCode = component.short_name;
27009             } else if (component.types.indexOf("street_number") >= 0) {
27010                 result.streetNumber = component.short_name;
27011             } else if (component.types.indexOf("route") >= 0) {
27012                 result.streetName = component.short_name;
27013             } else if (component.types.indexOf("neighborhood") >= 0) {
27014                 result.city = component.short_name;
27015             } else if (component.types.indexOf("locality") >= 0) {
27016                 result.city = component.short_name;
27017             } else if (component.types.indexOf("sublocality") >= 0) {
27018                 result.district = component.short_name;
27019             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27020                 result.stateOrProvince = component.short_name;
27021             } else if (component.types.indexOf("country") >= 0) {
27022                 result.country = component.short_name;
27023             }
27024         }
27025         
27026         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27027         result.addressLine2 = "";
27028         return result;
27029     },
27030     
27031     setZoomLevel: function(zoom)
27032     {
27033         this.gMapContext.map.setZoom(zoom);
27034     },
27035     
27036     show: function()
27037     {
27038         if(!this.el){
27039             return;
27040         }
27041         
27042         this.el.show();
27043         
27044         this.resize();
27045         
27046         this.fireEvent('show', this);
27047     },
27048     
27049     hide: function()
27050     {
27051         if(!this.el){
27052             return;
27053         }
27054         
27055         this.el.hide();
27056         
27057         this.fireEvent('hide', this);
27058     }
27059     
27060 });
27061
27062 Roo.apply(Roo.bootstrap.LocationPicker, {
27063     
27064     OverlayView : function(map, options)
27065     {
27066         options = options || {};
27067         
27068         this.setMap(map);
27069     }
27070     
27071     
27072 });/*
27073  * - LGPL
27074  *
27075  * Alert
27076  * 
27077  */
27078
27079 /**
27080  * @class Roo.bootstrap.Alert
27081  * @extends Roo.bootstrap.Component
27082  * Bootstrap Alert class
27083  * @cfg {String} title The title of alert
27084  * @cfg {String} html The content of alert
27085  * @cfg {String} weight (  success | info | warning | danger )
27086  * @cfg {String} faicon font-awesomeicon
27087  * 
27088  * @constructor
27089  * Create a new alert
27090  * @param {Object} config The config object
27091  */
27092
27093
27094 Roo.bootstrap.Alert = function(config){
27095     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27096     
27097 };
27098
27099 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27100     
27101     title: '',
27102     html: '',
27103     weight: false,
27104     faicon: false,
27105     
27106     getAutoCreate : function()
27107     {
27108         
27109         var cfg = {
27110             tag : 'div',
27111             cls : 'alert',
27112             cn : [
27113                 {
27114                     tag : 'i',
27115                     cls : 'roo-alert-icon'
27116                     
27117                 },
27118                 {
27119                     tag : 'b',
27120                     cls : 'roo-alert-title',
27121                     html : this.title
27122                 },
27123                 {
27124                     tag : 'span',
27125                     cls : 'roo-alert-text',
27126                     html : this.html
27127                 }
27128             ]
27129         };
27130         
27131         if(this.faicon){
27132             cfg.cn[0].cls += ' fa ' + this.faicon;
27133         }
27134         
27135         if(this.weight){
27136             cfg.cls += ' alert-' + this.weight;
27137         }
27138         
27139         return cfg;
27140     },
27141     
27142     initEvents: function() 
27143     {
27144         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27145     },
27146     
27147     setTitle : function(str)
27148     {
27149         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27150     },
27151     
27152     setText : function(str)
27153     {
27154         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27155     },
27156     
27157     setWeight : function(weight)
27158     {
27159         if(this.weight){
27160             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27161         }
27162         
27163         this.weight = weight;
27164         
27165         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27166     },
27167     
27168     setIcon : function(icon)
27169     {
27170         if(this.faicon){
27171             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27172         }
27173         
27174         this.faicon = icon;
27175         
27176         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27177     },
27178     
27179     hide: function() 
27180     {
27181         this.el.hide();   
27182     },
27183     
27184     show: function() 
27185     {  
27186         this.el.show();   
27187     }
27188     
27189 });
27190
27191  
27192 /*
27193 * Licence: LGPL
27194 */
27195
27196 /**
27197  * @class Roo.bootstrap.UploadCropbox
27198  * @extends Roo.bootstrap.Component
27199  * Bootstrap UploadCropbox class
27200  * @cfg {String} emptyText show when image has been loaded
27201  * @cfg {String} rotateNotify show when image too small to rotate
27202  * @cfg {Number} errorTimeout default 3000
27203  * @cfg {Number} minWidth default 300
27204  * @cfg {Number} minHeight default 300
27205  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27206  * @cfg {Boolean} isDocument (true|false) default false
27207  * @cfg {String} url action url
27208  * @cfg {String} paramName default 'imageUpload'
27209  * @cfg {String} method default POST
27210  * @cfg {Boolean} loadMask (true|false) default true
27211  * @cfg {Boolean} loadingText default 'Loading...'
27212  * 
27213  * @constructor
27214  * Create a new UploadCropbox
27215  * @param {Object} config The config object
27216  */
27217
27218 Roo.bootstrap.UploadCropbox = function(config){
27219     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27220     
27221     this.addEvents({
27222         /**
27223          * @event beforeselectfile
27224          * Fire before select file
27225          * @param {Roo.bootstrap.UploadCropbox} this
27226          */
27227         "beforeselectfile" : true,
27228         /**
27229          * @event initial
27230          * Fire after initEvent
27231          * @param {Roo.bootstrap.UploadCropbox} this
27232          */
27233         "initial" : true,
27234         /**
27235          * @event crop
27236          * Fire after initEvent
27237          * @param {Roo.bootstrap.UploadCropbox} this
27238          * @param {String} data
27239          */
27240         "crop" : true,
27241         /**
27242          * @event prepare
27243          * Fire when preparing the file data
27244          * @param {Roo.bootstrap.UploadCropbox} this
27245          * @param {Object} file
27246          */
27247         "prepare" : true,
27248         /**
27249          * @event exception
27250          * Fire when get exception
27251          * @param {Roo.bootstrap.UploadCropbox} this
27252          * @param {XMLHttpRequest} xhr
27253          */
27254         "exception" : true,
27255         /**
27256          * @event beforeloadcanvas
27257          * Fire before load the canvas
27258          * @param {Roo.bootstrap.UploadCropbox} this
27259          * @param {String} src
27260          */
27261         "beforeloadcanvas" : true,
27262         /**
27263          * @event trash
27264          * Fire when trash image
27265          * @param {Roo.bootstrap.UploadCropbox} this
27266          */
27267         "trash" : true,
27268         /**
27269          * @event download
27270          * Fire when download the image
27271          * @param {Roo.bootstrap.UploadCropbox} this
27272          */
27273         "download" : true,
27274         /**
27275          * @event footerbuttonclick
27276          * Fire when footerbuttonclick
27277          * @param {Roo.bootstrap.UploadCropbox} this
27278          * @param {String} type
27279          */
27280         "footerbuttonclick" : true,
27281         /**
27282          * @event resize
27283          * Fire when resize
27284          * @param {Roo.bootstrap.UploadCropbox} this
27285          */
27286         "resize" : true,
27287         /**
27288          * @event rotate
27289          * Fire when rotate the image
27290          * @param {Roo.bootstrap.UploadCropbox} this
27291          * @param {String} pos
27292          */
27293         "rotate" : true,
27294         /**
27295          * @event inspect
27296          * Fire when inspect the file
27297          * @param {Roo.bootstrap.UploadCropbox} this
27298          * @param {Object} file
27299          */
27300         "inspect" : true,
27301         /**
27302          * @event upload
27303          * Fire when xhr upload the file
27304          * @param {Roo.bootstrap.UploadCropbox} this
27305          * @param {Object} data
27306          */
27307         "upload" : true,
27308         /**
27309          * @event arrange
27310          * Fire when arrange the file data
27311          * @param {Roo.bootstrap.UploadCropbox} this
27312          * @param {Object} formData
27313          */
27314         "arrange" : true
27315     });
27316     
27317     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27318 };
27319
27320 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27321     
27322     emptyText : 'Click to upload image',
27323     rotateNotify : 'Image is too small to rotate',
27324     errorTimeout : 3000,
27325     scale : 0,
27326     baseScale : 1,
27327     rotate : 0,
27328     dragable : false,
27329     pinching : false,
27330     mouseX : 0,
27331     mouseY : 0,
27332     cropData : false,
27333     minWidth : 300,
27334     minHeight : 300,
27335     file : false,
27336     exif : {},
27337     baseRotate : 1,
27338     cropType : 'image/jpeg',
27339     buttons : false,
27340     canvasLoaded : false,
27341     isDocument : false,
27342     method : 'POST',
27343     paramName : 'imageUpload',
27344     loadMask : true,
27345     loadingText : 'Loading...',
27346     maskEl : false,
27347     
27348     getAutoCreate : function()
27349     {
27350         var cfg = {
27351             tag : 'div',
27352             cls : 'roo-upload-cropbox',
27353             cn : [
27354                 {
27355                     tag : 'input',
27356                     cls : 'roo-upload-cropbox-selector',
27357                     type : 'file'
27358                 },
27359                 {
27360                     tag : 'div',
27361                     cls : 'roo-upload-cropbox-body',
27362                     style : 'cursor:pointer',
27363                     cn : [
27364                         {
27365                             tag : 'div',
27366                             cls : 'roo-upload-cropbox-preview'
27367                         },
27368                         {
27369                             tag : 'div',
27370                             cls : 'roo-upload-cropbox-thumb'
27371                         },
27372                         {
27373                             tag : 'div',
27374                             cls : 'roo-upload-cropbox-empty-notify',
27375                             html : this.emptyText
27376                         },
27377                         {
27378                             tag : 'div',
27379                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27380                             html : this.rotateNotify
27381                         }
27382                     ]
27383                 },
27384                 {
27385                     tag : 'div',
27386                     cls : 'roo-upload-cropbox-footer',
27387                     cn : {
27388                         tag : 'div',
27389                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27390                         cn : []
27391                     }
27392                 }
27393             ]
27394         };
27395         
27396         return cfg;
27397     },
27398     
27399     onRender : function(ct, position)
27400     {
27401         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27402         
27403         if (this.buttons.length) {
27404             
27405             Roo.each(this.buttons, function(bb) {
27406                 
27407                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27408                 
27409                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27410                 
27411             }, this);
27412         }
27413         
27414         if(this.loadMask){
27415             this.maskEl = this.el;
27416         }
27417     },
27418     
27419     initEvents : function()
27420     {
27421         this.urlAPI = (window.createObjectURL && window) || 
27422                                 (window.URL && URL.revokeObjectURL && URL) || 
27423                                 (window.webkitURL && webkitURL);
27424                         
27425         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27426         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27427         
27428         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27429         this.selectorEl.hide();
27430         
27431         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27432         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27433         
27434         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27435         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27436         this.thumbEl.hide();
27437         
27438         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27439         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27440         
27441         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27442         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27443         this.errorEl.hide();
27444         
27445         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27446         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27447         this.footerEl.hide();
27448         
27449         this.setThumbBoxSize();
27450         
27451         this.bind();
27452         
27453         this.resize();
27454         
27455         this.fireEvent('initial', this);
27456     },
27457
27458     bind : function()
27459     {
27460         var _this = this;
27461         
27462         window.addEventListener("resize", function() { _this.resize(); } );
27463         
27464         this.bodyEl.on('click', this.beforeSelectFile, this);
27465         
27466         if(Roo.isTouch){
27467             this.bodyEl.on('touchstart', this.onTouchStart, this);
27468             this.bodyEl.on('touchmove', this.onTouchMove, this);
27469             this.bodyEl.on('touchend', this.onTouchEnd, this);
27470         }
27471         
27472         if(!Roo.isTouch){
27473             this.bodyEl.on('mousedown', this.onMouseDown, this);
27474             this.bodyEl.on('mousemove', this.onMouseMove, this);
27475             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27476             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27477             Roo.get(document).on('mouseup', this.onMouseUp, this);
27478         }
27479         
27480         this.selectorEl.on('change', this.onFileSelected, this);
27481     },
27482     
27483     reset : function()
27484     {    
27485         this.scale = 0;
27486         this.baseScale = 1;
27487         this.rotate = 0;
27488         this.baseRotate = 1;
27489         this.dragable = false;
27490         this.pinching = false;
27491         this.mouseX = 0;
27492         this.mouseY = 0;
27493         this.cropData = false;
27494         this.notifyEl.dom.innerHTML = this.emptyText;
27495         
27496         this.selectorEl.dom.value = '';
27497         
27498     },
27499     
27500     resize : function()
27501     {
27502         if(this.fireEvent('resize', this) != false){
27503             this.setThumbBoxPosition();
27504             this.setCanvasPosition();
27505         }
27506     },
27507     
27508     onFooterButtonClick : function(e, el, o, type)
27509     {
27510         switch (type) {
27511             case 'rotate-left' :
27512                 this.onRotateLeft(e);
27513                 break;
27514             case 'rotate-right' :
27515                 this.onRotateRight(e);
27516                 break;
27517             case 'picture' :
27518                 this.beforeSelectFile(e);
27519                 break;
27520             case 'trash' :
27521                 this.trash(e);
27522                 break;
27523             case 'crop' :
27524                 this.crop(e);
27525                 break;
27526             case 'download' :
27527                 this.download(e);
27528                 break;
27529             default :
27530                 break;
27531         }
27532         
27533         this.fireEvent('footerbuttonclick', this, type);
27534     },
27535     
27536     beforeSelectFile : function(e)
27537     {
27538         e.preventDefault();
27539         
27540         if(this.fireEvent('beforeselectfile', this) != false){
27541             this.selectorEl.dom.click();
27542         }
27543     },
27544     
27545     onFileSelected : function(e)
27546     {
27547         e.preventDefault();
27548         
27549         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27550             return;
27551         }
27552         
27553         var file = this.selectorEl.dom.files[0];
27554         
27555         if(this.fireEvent('inspect', this, file) != false){
27556             this.prepare(file);
27557         }
27558         
27559     },
27560     
27561     trash : function(e)
27562     {
27563         this.fireEvent('trash', this);
27564     },
27565     
27566     download : function(e)
27567     {
27568         this.fireEvent('download', this);
27569     },
27570     
27571     loadCanvas : function(src)
27572     {   
27573         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27574             
27575             this.reset();
27576             
27577             this.imageEl = document.createElement('img');
27578             
27579             var _this = this;
27580             
27581             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27582             
27583             this.imageEl.src = src;
27584         }
27585     },
27586     
27587     onLoadCanvas : function()
27588     {   
27589         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27590         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27591         
27592         this.bodyEl.un('click', this.beforeSelectFile, this);
27593         
27594         this.notifyEl.hide();
27595         this.thumbEl.show();
27596         this.footerEl.show();
27597         
27598         this.baseRotateLevel();
27599         
27600         if(this.isDocument){
27601             this.setThumbBoxSize();
27602         }
27603         
27604         this.setThumbBoxPosition();
27605         
27606         this.baseScaleLevel();
27607         
27608         this.draw();
27609         
27610         this.resize();
27611         
27612         this.canvasLoaded = true;
27613         
27614         if(this.loadMask){
27615             this.maskEl.unmask();
27616         }
27617         
27618     },
27619     
27620     setCanvasPosition : function()
27621     {   
27622         if(!this.canvasEl){
27623             return;
27624         }
27625         
27626         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27627         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27628         
27629         this.previewEl.setLeft(pw);
27630         this.previewEl.setTop(ph);
27631         
27632     },
27633     
27634     onMouseDown : function(e)
27635     {   
27636         e.stopEvent();
27637         
27638         this.dragable = true;
27639         this.pinching = false;
27640         
27641         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27642             this.dragable = false;
27643             return;
27644         }
27645         
27646         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27647         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27648         
27649     },
27650     
27651     onMouseMove : function(e)
27652     {   
27653         e.stopEvent();
27654         
27655         if(!this.canvasLoaded){
27656             return;
27657         }
27658         
27659         if (!this.dragable){
27660             return;
27661         }
27662         
27663         var minX = Math.ceil(this.thumbEl.getLeft(true));
27664         var minY = Math.ceil(this.thumbEl.getTop(true));
27665         
27666         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27667         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27668         
27669         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27670         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27671         
27672         x = x - this.mouseX;
27673         y = y - this.mouseY;
27674         
27675         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27676         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27677         
27678         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27679         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27680         
27681         this.previewEl.setLeft(bgX);
27682         this.previewEl.setTop(bgY);
27683         
27684         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27685         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27686     },
27687     
27688     onMouseUp : function(e)
27689     {   
27690         e.stopEvent();
27691         
27692         this.dragable = false;
27693     },
27694     
27695     onMouseWheel : function(e)
27696     {   
27697         e.stopEvent();
27698         
27699         this.startScale = this.scale;
27700         
27701         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27702         
27703         if(!this.zoomable()){
27704             this.scale = this.startScale;
27705             return;
27706         }
27707         
27708         this.draw();
27709         
27710         return;
27711     },
27712     
27713     zoomable : function()
27714     {
27715         var minScale = this.thumbEl.getWidth() / this.minWidth;
27716         
27717         if(this.minWidth < this.minHeight){
27718             minScale = this.thumbEl.getHeight() / this.minHeight;
27719         }
27720         
27721         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27722         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27723         
27724         if(
27725                 this.isDocument &&
27726                 (this.rotate == 0 || this.rotate == 180) && 
27727                 (
27728                     width > this.imageEl.OriginWidth || 
27729                     height > this.imageEl.OriginHeight ||
27730                     (width < this.minWidth && height < this.minHeight)
27731                 )
27732         ){
27733             return false;
27734         }
27735         
27736         if(
27737                 this.isDocument &&
27738                 (this.rotate == 90 || this.rotate == 270) && 
27739                 (
27740                     width > this.imageEl.OriginWidth || 
27741                     height > this.imageEl.OriginHeight ||
27742                     (width < this.minHeight && height < this.minWidth)
27743                 )
27744         ){
27745             return false;
27746         }
27747         
27748         if(
27749                 !this.isDocument &&
27750                 (this.rotate == 0 || this.rotate == 180) && 
27751                 (
27752                     width < this.minWidth || 
27753                     width > this.imageEl.OriginWidth || 
27754                     height < this.minHeight || 
27755                     height > this.imageEl.OriginHeight
27756                 )
27757         ){
27758             return false;
27759         }
27760         
27761         if(
27762                 !this.isDocument &&
27763                 (this.rotate == 90 || this.rotate == 270) && 
27764                 (
27765                     width < this.minHeight || 
27766                     width > this.imageEl.OriginWidth || 
27767                     height < this.minWidth || 
27768                     height > this.imageEl.OriginHeight
27769                 )
27770         ){
27771             return false;
27772         }
27773         
27774         return true;
27775         
27776     },
27777     
27778     onRotateLeft : function(e)
27779     {   
27780         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27781             
27782             var minScale = this.thumbEl.getWidth() / this.minWidth;
27783             
27784             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27785             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27786             
27787             this.startScale = this.scale;
27788             
27789             while (this.getScaleLevel() < minScale){
27790             
27791                 this.scale = this.scale + 1;
27792                 
27793                 if(!this.zoomable()){
27794                     break;
27795                 }
27796                 
27797                 if(
27798                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27799                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27800                 ){
27801                     continue;
27802                 }
27803                 
27804                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27805
27806                 this.draw();
27807                 
27808                 return;
27809             }
27810             
27811             this.scale = this.startScale;
27812             
27813             this.onRotateFail();
27814             
27815             return false;
27816         }
27817         
27818         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27819
27820         if(this.isDocument){
27821             this.setThumbBoxSize();
27822             this.setThumbBoxPosition();
27823             this.setCanvasPosition();
27824         }
27825         
27826         this.draw();
27827         
27828         this.fireEvent('rotate', this, 'left');
27829         
27830     },
27831     
27832     onRotateRight : function(e)
27833     {
27834         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27835             
27836             var minScale = this.thumbEl.getWidth() / this.minWidth;
27837         
27838             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27839             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27840             
27841             this.startScale = this.scale;
27842             
27843             while (this.getScaleLevel() < minScale){
27844             
27845                 this.scale = this.scale + 1;
27846                 
27847                 if(!this.zoomable()){
27848                     break;
27849                 }
27850                 
27851                 if(
27852                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27853                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27854                 ){
27855                     continue;
27856                 }
27857                 
27858                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27859
27860                 this.draw();
27861                 
27862                 return;
27863             }
27864             
27865             this.scale = this.startScale;
27866             
27867             this.onRotateFail();
27868             
27869             return false;
27870         }
27871         
27872         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27873
27874         if(this.isDocument){
27875             this.setThumbBoxSize();
27876             this.setThumbBoxPosition();
27877             this.setCanvasPosition();
27878         }
27879         
27880         this.draw();
27881         
27882         this.fireEvent('rotate', this, 'right');
27883     },
27884     
27885     onRotateFail : function()
27886     {
27887         this.errorEl.show(true);
27888         
27889         var _this = this;
27890         
27891         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27892     },
27893     
27894     draw : function()
27895     {
27896         this.previewEl.dom.innerHTML = '';
27897         
27898         var canvasEl = document.createElement("canvas");
27899         
27900         var contextEl = canvasEl.getContext("2d");
27901         
27902         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27903         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27904         var center = this.imageEl.OriginWidth / 2;
27905         
27906         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27907             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27908             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27909             center = this.imageEl.OriginHeight / 2;
27910         }
27911         
27912         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27913         
27914         contextEl.translate(center, center);
27915         contextEl.rotate(this.rotate * Math.PI / 180);
27916
27917         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27918         
27919         this.canvasEl = document.createElement("canvas");
27920         
27921         this.contextEl = this.canvasEl.getContext("2d");
27922         
27923         switch (this.rotate) {
27924             case 0 :
27925                 
27926                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27927                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27928                 
27929                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27930                 
27931                 break;
27932             case 90 : 
27933                 
27934                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27935                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27936                 
27937                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27938                     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);
27939                     break;
27940                 }
27941                 
27942                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27943                 
27944                 break;
27945             case 180 :
27946                 
27947                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27948                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27949                 
27950                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27951                     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);
27952                     break;
27953                 }
27954                 
27955                 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);
27956                 
27957                 break;
27958             case 270 :
27959                 
27960                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27961                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27962         
27963                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27964                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27965                     break;
27966                 }
27967                 
27968                 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);
27969                 
27970                 break;
27971             default : 
27972                 break;
27973         }
27974         
27975         this.previewEl.appendChild(this.canvasEl);
27976         
27977         this.setCanvasPosition();
27978     },
27979     
27980     crop : function()
27981     {
27982         if(!this.canvasLoaded){
27983             return;
27984         }
27985         
27986         var imageCanvas = document.createElement("canvas");
27987         
27988         var imageContext = imageCanvas.getContext("2d");
27989         
27990         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27991         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27992         
27993         var center = imageCanvas.width / 2;
27994         
27995         imageContext.translate(center, center);
27996         
27997         imageContext.rotate(this.rotate * Math.PI / 180);
27998         
27999         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28000         
28001         var canvas = document.createElement("canvas");
28002         
28003         var context = canvas.getContext("2d");
28004                 
28005         canvas.width = this.minWidth;
28006         canvas.height = this.minHeight;
28007
28008         switch (this.rotate) {
28009             case 0 :
28010                 
28011                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28012                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28013                 
28014                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28015                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28016                 
28017                 var targetWidth = this.minWidth - 2 * x;
28018                 var targetHeight = this.minHeight - 2 * y;
28019                 
28020                 var scale = 1;
28021                 
28022                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28023                     scale = targetWidth / width;
28024                 }
28025                 
28026                 if(x > 0 && y == 0){
28027                     scale = targetHeight / height;
28028                 }
28029                 
28030                 if(x > 0 && y > 0){
28031                     scale = targetWidth / width;
28032                     
28033                     if(width < height){
28034                         scale = targetHeight / height;
28035                     }
28036                 }
28037                 
28038                 context.scale(scale, scale);
28039                 
28040                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28041                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28042
28043                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28044                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28045
28046                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28047                 
28048                 break;
28049             case 90 : 
28050                 
28051                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28052                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28053                 
28054                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28055                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28056                 
28057                 var targetWidth = this.minWidth - 2 * x;
28058                 var targetHeight = this.minHeight - 2 * y;
28059                 
28060                 var scale = 1;
28061                 
28062                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28063                     scale = targetWidth / width;
28064                 }
28065                 
28066                 if(x > 0 && y == 0){
28067                     scale = targetHeight / height;
28068                 }
28069                 
28070                 if(x > 0 && y > 0){
28071                     scale = targetWidth / width;
28072                     
28073                     if(width < height){
28074                         scale = targetHeight / height;
28075                     }
28076                 }
28077                 
28078                 context.scale(scale, scale);
28079                 
28080                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28081                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28082
28083                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28084                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28085                 
28086                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28087                 
28088                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28089                 
28090                 break;
28091             case 180 :
28092                 
28093                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28094                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28095                 
28096                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28097                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28098                 
28099                 var targetWidth = this.minWidth - 2 * x;
28100                 var targetHeight = this.minHeight - 2 * y;
28101                 
28102                 var scale = 1;
28103                 
28104                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28105                     scale = targetWidth / width;
28106                 }
28107                 
28108                 if(x > 0 && y == 0){
28109                     scale = targetHeight / height;
28110                 }
28111                 
28112                 if(x > 0 && y > 0){
28113                     scale = targetWidth / width;
28114                     
28115                     if(width < height){
28116                         scale = targetHeight / height;
28117                     }
28118                 }
28119                 
28120                 context.scale(scale, scale);
28121                 
28122                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28123                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28124
28125                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28126                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28127
28128                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28129                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28130                 
28131                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28132                 
28133                 break;
28134             case 270 :
28135                 
28136                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28137                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28138                 
28139                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28140                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28141                 
28142                 var targetWidth = this.minWidth - 2 * x;
28143                 var targetHeight = this.minHeight - 2 * y;
28144                 
28145                 var scale = 1;
28146                 
28147                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28148                     scale = targetWidth / width;
28149                 }
28150                 
28151                 if(x > 0 && y == 0){
28152                     scale = targetHeight / height;
28153                 }
28154                 
28155                 if(x > 0 && y > 0){
28156                     scale = targetWidth / width;
28157                     
28158                     if(width < height){
28159                         scale = targetHeight / height;
28160                     }
28161                 }
28162                 
28163                 context.scale(scale, scale);
28164                 
28165                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28166                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28167
28168                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28169                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28170                 
28171                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28172                 
28173                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28174                 
28175                 break;
28176             default : 
28177                 break;
28178         }
28179         
28180         this.cropData = canvas.toDataURL(this.cropType);
28181         
28182         if(this.fireEvent('crop', this, this.cropData) !== false){
28183             this.process(this.file, this.cropData);
28184         }
28185         
28186         return;
28187         
28188     },
28189     
28190     setThumbBoxSize : function()
28191     {
28192         var width, height;
28193         
28194         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28195             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28196             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28197             
28198             this.minWidth = width;
28199             this.minHeight = height;
28200             
28201             if(this.rotate == 90 || this.rotate == 270){
28202                 this.minWidth = height;
28203                 this.minHeight = width;
28204             }
28205         }
28206         
28207         height = 300;
28208         width = Math.ceil(this.minWidth * height / this.minHeight);
28209         
28210         if(this.minWidth > this.minHeight){
28211             width = 300;
28212             height = Math.ceil(this.minHeight * width / this.minWidth);
28213         }
28214         
28215         this.thumbEl.setStyle({
28216             width : width + 'px',
28217             height : height + 'px'
28218         });
28219
28220         return;
28221             
28222     },
28223     
28224     setThumbBoxPosition : function()
28225     {
28226         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28227         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28228         
28229         this.thumbEl.setLeft(x);
28230         this.thumbEl.setTop(y);
28231         
28232     },
28233     
28234     baseRotateLevel : function()
28235     {
28236         this.baseRotate = 1;
28237         
28238         if(
28239                 typeof(this.exif) != 'undefined' &&
28240                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28241                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28242         ){
28243             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28244         }
28245         
28246         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28247         
28248     },
28249     
28250     baseScaleLevel : function()
28251     {
28252         var width, height;
28253         
28254         if(this.isDocument){
28255             
28256             if(this.baseRotate == 6 || this.baseRotate == 8){
28257             
28258                 height = this.thumbEl.getHeight();
28259                 this.baseScale = height / this.imageEl.OriginWidth;
28260
28261                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28262                     width = this.thumbEl.getWidth();
28263                     this.baseScale = width / this.imageEl.OriginHeight;
28264                 }
28265
28266                 return;
28267             }
28268
28269             height = this.thumbEl.getHeight();
28270             this.baseScale = height / this.imageEl.OriginHeight;
28271
28272             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28273                 width = this.thumbEl.getWidth();
28274                 this.baseScale = width / this.imageEl.OriginWidth;
28275             }
28276
28277             return;
28278         }
28279         
28280         if(this.baseRotate == 6 || this.baseRotate == 8){
28281             
28282             width = this.thumbEl.getHeight();
28283             this.baseScale = width / this.imageEl.OriginHeight;
28284             
28285             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28286                 height = this.thumbEl.getWidth();
28287                 this.baseScale = height / this.imageEl.OriginHeight;
28288             }
28289             
28290             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28291                 height = this.thumbEl.getWidth();
28292                 this.baseScale = height / this.imageEl.OriginHeight;
28293                 
28294                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28295                     width = this.thumbEl.getHeight();
28296                     this.baseScale = width / this.imageEl.OriginWidth;
28297                 }
28298             }
28299             
28300             return;
28301         }
28302         
28303         width = this.thumbEl.getWidth();
28304         this.baseScale = width / this.imageEl.OriginWidth;
28305         
28306         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28307             height = this.thumbEl.getHeight();
28308             this.baseScale = height / this.imageEl.OriginHeight;
28309         }
28310         
28311         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28312             
28313             height = this.thumbEl.getHeight();
28314             this.baseScale = height / this.imageEl.OriginHeight;
28315             
28316             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28317                 width = this.thumbEl.getWidth();
28318                 this.baseScale = width / this.imageEl.OriginWidth;
28319             }
28320             
28321         }
28322         
28323         return;
28324     },
28325     
28326     getScaleLevel : function()
28327     {
28328         return this.baseScale * Math.pow(1.1, this.scale);
28329     },
28330     
28331     onTouchStart : function(e)
28332     {
28333         if(!this.canvasLoaded){
28334             this.beforeSelectFile(e);
28335             return;
28336         }
28337         
28338         var touches = e.browserEvent.touches;
28339         
28340         if(!touches){
28341             return;
28342         }
28343         
28344         if(touches.length == 1){
28345             this.onMouseDown(e);
28346             return;
28347         }
28348         
28349         if(touches.length != 2){
28350             return;
28351         }
28352         
28353         var coords = [];
28354         
28355         for(var i = 0, finger; finger = touches[i]; i++){
28356             coords.push(finger.pageX, finger.pageY);
28357         }
28358         
28359         var x = Math.pow(coords[0] - coords[2], 2);
28360         var y = Math.pow(coords[1] - coords[3], 2);
28361         
28362         this.startDistance = Math.sqrt(x + y);
28363         
28364         this.startScale = this.scale;
28365         
28366         this.pinching = true;
28367         this.dragable = false;
28368         
28369     },
28370     
28371     onTouchMove : function(e)
28372     {
28373         if(!this.pinching && !this.dragable){
28374             return;
28375         }
28376         
28377         var touches = e.browserEvent.touches;
28378         
28379         if(!touches){
28380             return;
28381         }
28382         
28383         if(this.dragable){
28384             this.onMouseMove(e);
28385             return;
28386         }
28387         
28388         var coords = [];
28389         
28390         for(var i = 0, finger; finger = touches[i]; i++){
28391             coords.push(finger.pageX, finger.pageY);
28392         }
28393         
28394         var x = Math.pow(coords[0] - coords[2], 2);
28395         var y = Math.pow(coords[1] - coords[3], 2);
28396         
28397         this.endDistance = Math.sqrt(x + y);
28398         
28399         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28400         
28401         if(!this.zoomable()){
28402             this.scale = this.startScale;
28403             return;
28404         }
28405         
28406         this.draw();
28407         
28408     },
28409     
28410     onTouchEnd : function(e)
28411     {
28412         this.pinching = false;
28413         this.dragable = false;
28414         
28415     },
28416     
28417     process : function(file, crop)
28418     {
28419         if(this.loadMask){
28420             this.maskEl.mask(this.loadingText);
28421         }
28422         
28423         this.xhr = new XMLHttpRequest();
28424         
28425         file.xhr = this.xhr;
28426
28427         this.xhr.open(this.method, this.url, true);
28428         
28429         var headers = {
28430             "Accept": "application/json",
28431             "Cache-Control": "no-cache",
28432             "X-Requested-With": "XMLHttpRequest"
28433         };
28434         
28435         for (var headerName in headers) {
28436             var headerValue = headers[headerName];
28437             if (headerValue) {
28438                 this.xhr.setRequestHeader(headerName, headerValue);
28439             }
28440         }
28441         
28442         var _this = this;
28443         
28444         this.xhr.onload = function()
28445         {
28446             _this.xhrOnLoad(_this.xhr);
28447         }
28448         
28449         this.xhr.onerror = function()
28450         {
28451             _this.xhrOnError(_this.xhr);
28452         }
28453         
28454         var formData = new FormData();
28455
28456         formData.append('returnHTML', 'NO');
28457         
28458         if(crop){
28459             formData.append('crop', crop);
28460         }
28461         
28462         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28463             formData.append(this.paramName, file, file.name);
28464         }
28465         
28466         if(typeof(file.filename) != 'undefined'){
28467             formData.append('filename', file.filename);
28468         }
28469         
28470         if(typeof(file.mimetype) != 'undefined'){
28471             formData.append('mimetype', file.mimetype);
28472         }
28473         
28474         if(this.fireEvent('arrange', this, formData) != false){
28475             this.xhr.send(formData);
28476         };
28477     },
28478     
28479     xhrOnLoad : function(xhr)
28480     {
28481         if(this.loadMask){
28482             this.maskEl.unmask();
28483         }
28484         
28485         if (xhr.readyState !== 4) {
28486             this.fireEvent('exception', this, xhr);
28487             return;
28488         }
28489
28490         var response = Roo.decode(xhr.responseText);
28491         
28492         if(!response.success){
28493             this.fireEvent('exception', this, xhr);
28494             return;
28495         }
28496         
28497         var response = Roo.decode(xhr.responseText);
28498         
28499         this.fireEvent('upload', this, response);
28500         
28501     },
28502     
28503     xhrOnError : function()
28504     {
28505         if(this.loadMask){
28506             this.maskEl.unmask();
28507         }
28508         
28509         Roo.log('xhr on error');
28510         
28511         var response = Roo.decode(xhr.responseText);
28512           
28513         Roo.log(response);
28514         
28515     },
28516     
28517     prepare : function(file)
28518     {   
28519         if(this.loadMask){
28520             this.maskEl.mask(this.loadingText);
28521         }
28522         
28523         this.file = false;
28524         this.exif = {};
28525         
28526         if(typeof(file) === 'string'){
28527             this.loadCanvas(file);
28528             return;
28529         }
28530         
28531         if(!file || !this.urlAPI){
28532             return;
28533         }
28534         
28535         this.file = file;
28536         this.cropType = file.type;
28537         
28538         var _this = this;
28539         
28540         if(this.fireEvent('prepare', this, this.file) != false){
28541             
28542             var reader = new FileReader();
28543             
28544             reader.onload = function (e) {
28545                 if (e.target.error) {
28546                     Roo.log(e.target.error);
28547                     return;
28548                 }
28549                 
28550                 var buffer = e.target.result,
28551                     dataView = new DataView(buffer),
28552                     offset = 2,
28553                     maxOffset = dataView.byteLength - 4,
28554                     markerBytes,
28555                     markerLength;
28556                 
28557                 if (dataView.getUint16(0) === 0xffd8) {
28558                     while (offset < maxOffset) {
28559                         markerBytes = dataView.getUint16(offset);
28560                         
28561                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28562                             markerLength = dataView.getUint16(offset + 2) + 2;
28563                             if (offset + markerLength > dataView.byteLength) {
28564                                 Roo.log('Invalid meta data: Invalid segment size.');
28565                                 break;
28566                             }
28567                             
28568                             if(markerBytes == 0xffe1){
28569                                 _this.parseExifData(
28570                                     dataView,
28571                                     offset,
28572                                     markerLength
28573                                 );
28574                             }
28575                             
28576                             offset += markerLength;
28577                             
28578                             continue;
28579                         }
28580                         
28581                         break;
28582                     }
28583                     
28584                 }
28585                 
28586                 var url = _this.urlAPI.createObjectURL(_this.file);
28587                 
28588                 _this.loadCanvas(url);
28589                 
28590                 return;
28591             }
28592             
28593             reader.readAsArrayBuffer(this.file);
28594             
28595         }
28596         
28597     },
28598     
28599     parseExifData : function(dataView, offset, length)
28600     {
28601         var tiffOffset = offset + 10,
28602             littleEndian,
28603             dirOffset;
28604     
28605         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28606             // No Exif data, might be XMP data instead
28607             return;
28608         }
28609         
28610         // Check for the ASCII code for "Exif" (0x45786966):
28611         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28612             // No Exif data, might be XMP data instead
28613             return;
28614         }
28615         if (tiffOffset + 8 > dataView.byteLength) {
28616             Roo.log('Invalid Exif data: Invalid segment size.');
28617             return;
28618         }
28619         // Check for the two null bytes:
28620         if (dataView.getUint16(offset + 8) !== 0x0000) {
28621             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28622             return;
28623         }
28624         // Check the byte alignment:
28625         switch (dataView.getUint16(tiffOffset)) {
28626         case 0x4949:
28627             littleEndian = true;
28628             break;
28629         case 0x4D4D:
28630             littleEndian = false;
28631             break;
28632         default:
28633             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28634             return;
28635         }
28636         // Check for the TIFF tag marker (0x002A):
28637         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28638             Roo.log('Invalid Exif data: Missing TIFF marker.');
28639             return;
28640         }
28641         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28642         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28643         
28644         this.parseExifTags(
28645             dataView,
28646             tiffOffset,
28647             tiffOffset + dirOffset,
28648             littleEndian
28649         );
28650     },
28651     
28652     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28653     {
28654         var tagsNumber,
28655             dirEndOffset,
28656             i;
28657         if (dirOffset + 6 > dataView.byteLength) {
28658             Roo.log('Invalid Exif data: Invalid directory offset.');
28659             return;
28660         }
28661         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28662         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28663         if (dirEndOffset + 4 > dataView.byteLength) {
28664             Roo.log('Invalid Exif data: Invalid directory size.');
28665             return;
28666         }
28667         for (i = 0; i < tagsNumber; i += 1) {
28668             this.parseExifTag(
28669                 dataView,
28670                 tiffOffset,
28671                 dirOffset + 2 + 12 * i, // tag offset
28672                 littleEndian
28673             );
28674         }
28675         // Return the offset to the next directory:
28676         return dataView.getUint32(dirEndOffset, littleEndian);
28677     },
28678     
28679     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28680     {
28681         var tag = dataView.getUint16(offset, littleEndian);
28682         
28683         this.exif[tag] = this.getExifValue(
28684             dataView,
28685             tiffOffset,
28686             offset,
28687             dataView.getUint16(offset + 2, littleEndian), // tag type
28688             dataView.getUint32(offset + 4, littleEndian), // tag length
28689             littleEndian
28690         );
28691     },
28692     
28693     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28694     {
28695         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28696             tagSize,
28697             dataOffset,
28698             values,
28699             i,
28700             str,
28701             c;
28702     
28703         if (!tagType) {
28704             Roo.log('Invalid Exif data: Invalid tag type.');
28705             return;
28706         }
28707         
28708         tagSize = tagType.size * length;
28709         // Determine if the value is contained in the dataOffset bytes,
28710         // or if the value at the dataOffset is a pointer to the actual data:
28711         dataOffset = tagSize > 4 ?
28712                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28713         if (dataOffset + tagSize > dataView.byteLength) {
28714             Roo.log('Invalid Exif data: Invalid data offset.');
28715             return;
28716         }
28717         if (length === 1) {
28718             return tagType.getValue(dataView, dataOffset, littleEndian);
28719         }
28720         values = [];
28721         for (i = 0; i < length; i += 1) {
28722             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28723         }
28724         
28725         if (tagType.ascii) {
28726             str = '';
28727             // Concatenate the chars:
28728             for (i = 0; i < values.length; i += 1) {
28729                 c = values[i];
28730                 // Ignore the terminating NULL byte(s):
28731                 if (c === '\u0000') {
28732                     break;
28733                 }
28734                 str += c;
28735             }
28736             return str;
28737         }
28738         return values;
28739     }
28740     
28741 });
28742
28743 Roo.apply(Roo.bootstrap.UploadCropbox, {
28744     tags : {
28745         'Orientation': 0x0112
28746     },
28747     
28748     Orientation: {
28749             1: 0, //'top-left',
28750 //            2: 'top-right',
28751             3: 180, //'bottom-right',
28752 //            4: 'bottom-left',
28753 //            5: 'left-top',
28754             6: 90, //'right-top',
28755 //            7: 'right-bottom',
28756             8: 270 //'left-bottom'
28757     },
28758     
28759     exifTagTypes : {
28760         // byte, 8-bit unsigned int:
28761         1: {
28762             getValue: function (dataView, dataOffset) {
28763                 return dataView.getUint8(dataOffset);
28764             },
28765             size: 1
28766         },
28767         // ascii, 8-bit byte:
28768         2: {
28769             getValue: function (dataView, dataOffset) {
28770                 return String.fromCharCode(dataView.getUint8(dataOffset));
28771             },
28772             size: 1,
28773             ascii: true
28774         },
28775         // short, 16 bit int:
28776         3: {
28777             getValue: function (dataView, dataOffset, littleEndian) {
28778                 return dataView.getUint16(dataOffset, littleEndian);
28779             },
28780             size: 2
28781         },
28782         // long, 32 bit int:
28783         4: {
28784             getValue: function (dataView, dataOffset, littleEndian) {
28785                 return dataView.getUint32(dataOffset, littleEndian);
28786             },
28787             size: 4
28788         },
28789         // rational = two long values, first is numerator, second is denominator:
28790         5: {
28791             getValue: function (dataView, dataOffset, littleEndian) {
28792                 return dataView.getUint32(dataOffset, littleEndian) /
28793                     dataView.getUint32(dataOffset + 4, littleEndian);
28794             },
28795             size: 8
28796         },
28797         // slong, 32 bit signed int:
28798         9: {
28799             getValue: function (dataView, dataOffset, littleEndian) {
28800                 return dataView.getInt32(dataOffset, littleEndian);
28801             },
28802             size: 4
28803         },
28804         // srational, two slongs, first is numerator, second is denominator:
28805         10: {
28806             getValue: function (dataView, dataOffset, littleEndian) {
28807                 return dataView.getInt32(dataOffset, littleEndian) /
28808                     dataView.getInt32(dataOffset + 4, littleEndian);
28809             },
28810             size: 8
28811         }
28812     },
28813     
28814     footer : {
28815         STANDARD : [
28816             {
28817                 tag : 'div',
28818                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28819                 action : 'rotate-left',
28820                 cn : [
28821                     {
28822                         tag : 'button',
28823                         cls : 'btn btn-default',
28824                         html : '<i class="fa fa-undo"></i>'
28825                     }
28826                 ]
28827             },
28828             {
28829                 tag : 'div',
28830                 cls : 'btn-group roo-upload-cropbox-picture',
28831                 action : 'picture',
28832                 cn : [
28833                     {
28834                         tag : 'button',
28835                         cls : 'btn btn-default',
28836                         html : '<i class="fa fa-picture-o"></i>'
28837                     }
28838                 ]
28839             },
28840             {
28841                 tag : 'div',
28842                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28843                 action : 'rotate-right',
28844                 cn : [
28845                     {
28846                         tag : 'button',
28847                         cls : 'btn btn-default',
28848                         html : '<i class="fa fa-repeat"></i>'
28849                     }
28850                 ]
28851             }
28852         ],
28853         DOCUMENT : [
28854             {
28855                 tag : 'div',
28856                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28857                 action : 'rotate-left',
28858                 cn : [
28859                     {
28860                         tag : 'button',
28861                         cls : 'btn btn-default',
28862                         html : '<i class="fa fa-undo"></i>'
28863                     }
28864                 ]
28865             },
28866             {
28867                 tag : 'div',
28868                 cls : 'btn-group roo-upload-cropbox-download',
28869                 action : 'download',
28870                 cn : [
28871                     {
28872                         tag : 'button',
28873                         cls : 'btn btn-default',
28874                         html : '<i class="fa fa-download"></i>'
28875                     }
28876                 ]
28877             },
28878             {
28879                 tag : 'div',
28880                 cls : 'btn-group roo-upload-cropbox-crop',
28881                 action : 'crop',
28882                 cn : [
28883                     {
28884                         tag : 'button',
28885                         cls : 'btn btn-default',
28886                         html : '<i class="fa fa-crop"></i>'
28887                     }
28888                 ]
28889             },
28890             {
28891                 tag : 'div',
28892                 cls : 'btn-group roo-upload-cropbox-trash',
28893                 action : 'trash',
28894                 cn : [
28895                     {
28896                         tag : 'button',
28897                         cls : 'btn btn-default',
28898                         html : '<i class="fa fa-trash"></i>'
28899                     }
28900                 ]
28901             },
28902             {
28903                 tag : 'div',
28904                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28905                 action : 'rotate-right',
28906                 cn : [
28907                     {
28908                         tag : 'button',
28909                         cls : 'btn btn-default',
28910                         html : '<i class="fa fa-repeat"></i>'
28911                     }
28912                 ]
28913             }
28914         ],
28915         ROTATOR : [
28916             {
28917                 tag : 'div',
28918                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28919                 action : 'rotate-left',
28920                 cn : [
28921                     {
28922                         tag : 'button',
28923                         cls : 'btn btn-default',
28924                         html : '<i class="fa fa-undo"></i>'
28925                     }
28926                 ]
28927             },
28928             {
28929                 tag : 'div',
28930                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28931                 action : 'rotate-right',
28932                 cn : [
28933                     {
28934                         tag : 'button',
28935                         cls : 'btn btn-default',
28936                         html : '<i class="fa fa-repeat"></i>'
28937                     }
28938                 ]
28939             }
28940         ]
28941     }
28942 });
28943
28944 /*
28945 * Licence: LGPL
28946 */
28947
28948 /**
28949  * @class Roo.bootstrap.DocumentManager
28950  * @extends Roo.bootstrap.Component
28951  * Bootstrap DocumentManager class
28952  * @cfg {String} paramName default 'imageUpload'
28953  * @cfg {String} toolTipName default 'filename'
28954  * @cfg {String} method default POST
28955  * @cfg {String} url action url
28956  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28957  * @cfg {Boolean} multiple multiple upload default true
28958  * @cfg {Number} thumbSize default 300
28959  * @cfg {String} fieldLabel
28960  * @cfg {Number} labelWidth default 4
28961  * @cfg {String} labelAlign (left|top) default left
28962  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28963 * @cfg {Number} labellg set the width of label (1-12)
28964  * @cfg {Number} labelmd set the width of label (1-12)
28965  * @cfg {Number} labelsm set the width of label (1-12)
28966  * @cfg {Number} labelxs set the width of label (1-12)
28967  * 
28968  * @constructor
28969  * Create a new DocumentManager
28970  * @param {Object} config The config object
28971  */
28972
28973 Roo.bootstrap.DocumentManager = function(config){
28974     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28975     
28976     this.files = [];
28977     this.delegates = [];
28978     
28979     this.addEvents({
28980         /**
28981          * @event initial
28982          * Fire when initial the DocumentManager
28983          * @param {Roo.bootstrap.DocumentManager} this
28984          */
28985         "initial" : true,
28986         /**
28987          * @event inspect
28988          * inspect selected file
28989          * @param {Roo.bootstrap.DocumentManager} this
28990          * @param {File} file
28991          */
28992         "inspect" : true,
28993         /**
28994          * @event exception
28995          * Fire when xhr load exception
28996          * @param {Roo.bootstrap.DocumentManager} this
28997          * @param {XMLHttpRequest} xhr
28998          */
28999         "exception" : true,
29000         /**
29001          * @event afterupload
29002          * Fire when xhr load exception
29003          * @param {Roo.bootstrap.DocumentManager} this
29004          * @param {XMLHttpRequest} xhr
29005          */
29006         "afterupload" : true,
29007         /**
29008          * @event prepare
29009          * prepare the form data
29010          * @param {Roo.bootstrap.DocumentManager} this
29011          * @param {Object} formData
29012          */
29013         "prepare" : true,
29014         /**
29015          * @event remove
29016          * Fire when remove the file
29017          * @param {Roo.bootstrap.DocumentManager} this
29018          * @param {Object} file
29019          */
29020         "remove" : true,
29021         /**
29022          * @event refresh
29023          * Fire after refresh the file
29024          * @param {Roo.bootstrap.DocumentManager} this
29025          */
29026         "refresh" : true,
29027         /**
29028          * @event click
29029          * Fire after click the image
29030          * @param {Roo.bootstrap.DocumentManager} this
29031          * @param {Object} file
29032          */
29033         "click" : true,
29034         /**
29035          * @event edit
29036          * Fire when upload a image and editable set to true
29037          * @param {Roo.bootstrap.DocumentManager} this
29038          * @param {Object} file
29039          */
29040         "edit" : true,
29041         /**
29042          * @event beforeselectfile
29043          * Fire before select file
29044          * @param {Roo.bootstrap.DocumentManager} this
29045          */
29046         "beforeselectfile" : true,
29047         /**
29048          * @event process
29049          * Fire before process file
29050          * @param {Roo.bootstrap.DocumentManager} this
29051          * @param {Object} file
29052          */
29053         "process" : true,
29054         /**
29055          * @event previewrendered
29056          * Fire when preview rendered
29057          * @param {Roo.bootstrap.DocumentManager} this
29058          * @param {Object} file
29059          */
29060         "previewrendered" : true,
29061         /**
29062          */
29063         "previewResize" : true
29064         
29065     });
29066 };
29067
29068 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29069     
29070     boxes : 0,
29071     inputName : '',
29072     thumbSize : 300,
29073     multiple : true,
29074     files : false,
29075     method : 'POST',
29076     url : '',
29077     paramName : 'imageUpload',
29078     toolTipName : 'filename',
29079     fieldLabel : '',
29080     labelWidth : 4,
29081     labelAlign : 'left',
29082     editable : true,
29083     delegates : false,
29084     xhr : false, 
29085     
29086     labellg : 0,
29087     labelmd : 0,
29088     labelsm : 0,
29089     labelxs : 0,
29090     
29091     getAutoCreate : function()
29092     {   
29093         var managerWidget = {
29094             tag : 'div',
29095             cls : 'roo-document-manager',
29096             cn : [
29097                 {
29098                     tag : 'input',
29099                     cls : 'roo-document-manager-selector',
29100                     type : 'file'
29101                 },
29102                 {
29103                     tag : 'div',
29104                     cls : 'roo-document-manager-uploader',
29105                     cn : [
29106                         {
29107                             tag : 'div',
29108                             cls : 'roo-document-manager-upload-btn',
29109                             html : '<i class="fa fa-plus"></i>'
29110                         }
29111                     ]
29112                     
29113                 }
29114             ]
29115         };
29116         
29117         var content = [
29118             {
29119                 tag : 'div',
29120                 cls : 'column col-md-12',
29121                 cn : managerWidget
29122             }
29123         ];
29124         
29125         if(this.fieldLabel.length){
29126             
29127             content = [
29128                 {
29129                     tag : 'div',
29130                     cls : 'column col-md-12',
29131                     html : this.fieldLabel
29132                 },
29133                 {
29134                     tag : 'div',
29135                     cls : 'column col-md-12',
29136                     cn : managerWidget
29137                 }
29138             ];
29139
29140             if(this.labelAlign == 'left'){
29141                 content = [
29142                     {
29143                         tag : 'div',
29144                         cls : 'column',
29145                         html : this.fieldLabel
29146                     },
29147                     {
29148                         tag : 'div',
29149                         cls : 'column',
29150                         cn : managerWidget
29151                     }
29152                 ];
29153                 
29154                 if(this.labelWidth > 12){
29155                     content[0].style = "width: " + this.labelWidth + 'px';
29156                 }
29157
29158                 if(this.labelWidth < 13 && this.labelmd == 0){
29159                     this.labelmd = this.labelWidth;
29160                 }
29161
29162                 if(this.labellg > 0){
29163                     content[0].cls += ' col-lg-' + this.labellg;
29164                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29165                 }
29166
29167                 if(this.labelmd > 0){
29168                     content[0].cls += ' col-md-' + this.labelmd;
29169                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29170                 }
29171
29172                 if(this.labelsm > 0){
29173                     content[0].cls += ' col-sm-' + this.labelsm;
29174                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29175                 }
29176
29177                 if(this.labelxs > 0){
29178                     content[0].cls += ' col-xs-' + this.labelxs;
29179                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29180                 }
29181                 
29182             }
29183         }
29184         
29185         var cfg = {
29186             tag : 'div',
29187             cls : 'row clearfix',
29188             cn : content
29189         };
29190         
29191         return cfg;
29192         
29193     },
29194     
29195     initEvents : function()
29196     {
29197         this.managerEl = this.el.select('.roo-document-manager', true).first();
29198         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29199         
29200         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29201         this.selectorEl.hide();
29202         
29203         if(this.multiple){
29204             this.selectorEl.attr('multiple', 'multiple');
29205         }
29206         
29207         this.selectorEl.on('change', this.onFileSelected, this);
29208         
29209         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29210         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29211         
29212         this.uploader.on('click', this.onUploaderClick, this);
29213         
29214         this.renderProgressDialog();
29215         
29216         var _this = this;
29217         
29218         window.addEventListener("resize", function() { _this.refresh(); } );
29219         
29220         this.fireEvent('initial', this);
29221     },
29222     
29223     renderProgressDialog : function()
29224     {
29225         var _this = this;
29226         
29227         this.progressDialog = new Roo.bootstrap.Modal({
29228             cls : 'roo-document-manager-progress-dialog',
29229             allow_close : false,
29230             animate : false,
29231             title : '',
29232             buttons : [
29233                 {
29234                     name  :'cancel',
29235                     weight : 'danger',
29236                     html : 'Cancel'
29237                 }
29238             ], 
29239             listeners : { 
29240                 btnclick : function() {
29241                     _this.uploadCancel();
29242                     this.hide();
29243                 }
29244             }
29245         });
29246          
29247         this.progressDialog.render(Roo.get(document.body));
29248          
29249         this.progress = new Roo.bootstrap.Progress({
29250             cls : 'roo-document-manager-progress',
29251             active : true,
29252             striped : true
29253         });
29254         
29255         this.progress.render(this.progressDialog.getChildContainer());
29256         
29257         this.progressBar = new Roo.bootstrap.ProgressBar({
29258             cls : 'roo-document-manager-progress-bar',
29259             aria_valuenow : 0,
29260             aria_valuemin : 0,
29261             aria_valuemax : 12,
29262             panel : 'success'
29263         });
29264         
29265         this.progressBar.render(this.progress.getChildContainer());
29266     },
29267     
29268     onUploaderClick : function(e)
29269     {
29270         e.preventDefault();
29271      
29272         if(this.fireEvent('beforeselectfile', this) != false){
29273             this.selectorEl.dom.click();
29274         }
29275         
29276     },
29277     
29278     onFileSelected : function(e)
29279     {
29280         e.preventDefault();
29281         
29282         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29283             return;
29284         }
29285         
29286         Roo.each(this.selectorEl.dom.files, function(file){
29287             if(this.fireEvent('inspect', this, file) != false){
29288                 this.files.push(file);
29289             }
29290         }, this);
29291         
29292         this.queue();
29293         
29294     },
29295     
29296     queue : function()
29297     {
29298         this.selectorEl.dom.value = '';
29299         
29300         if(!this.files || !this.files.length){
29301             return;
29302         }
29303         
29304         if(this.boxes > 0 && this.files.length > this.boxes){
29305             this.files = this.files.slice(0, this.boxes);
29306         }
29307         
29308         this.uploader.show();
29309         
29310         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29311             this.uploader.hide();
29312         }
29313         
29314         var _this = this;
29315         
29316         var files = [];
29317         
29318         var docs = [];
29319         
29320         Roo.each(this.files, function(file){
29321             
29322             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29323                 var f = this.renderPreview(file);
29324                 files.push(f);
29325                 return;
29326             }
29327             
29328             if(file.type.indexOf('image') != -1){
29329                 this.delegates.push(
29330                     (function(){
29331                         _this.process(file);
29332                     }).createDelegate(this)
29333                 );
29334         
29335                 return;
29336             }
29337             
29338             docs.push(
29339                 (function(){
29340                     _this.process(file);
29341                 }).createDelegate(this)
29342             );
29343             
29344         }, this);
29345         
29346         this.files = files;
29347         
29348         this.delegates = this.delegates.concat(docs);
29349         
29350         if(!this.delegates.length){
29351             this.refresh();
29352             return;
29353         }
29354         
29355         this.progressBar.aria_valuemax = this.delegates.length;
29356         
29357         this.arrange();
29358         
29359         return;
29360     },
29361     
29362     arrange : function()
29363     {
29364         if(!this.delegates.length){
29365             this.progressDialog.hide();
29366             this.refresh();
29367             return;
29368         }
29369         
29370         var delegate = this.delegates.shift();
29371         
29372         this.progressDialog.show();
29373         
29374         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29375         
29376         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29377         
29378         delegate();
29379     },
29380     
29381     refresh : function()
29382     {
29383         this.uploader.show();
29384         
29385         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29386             this.uploader.hide();
29387         }
29388         
29389         Roo.isTouch ? this.closable(false) : this.closable(true);
29390         
29391         this.fireEvent('refresh', this);
29392     },
29393     
29394     onRemove : function(e, el, o)
29395     {
29396         e.preventDefault();
29397         
29398         this.fireEvent('remove', this, o);
29399         
29400     },
29401     
29402     remove : function(o)
29403     {
29404         var files = [];
29405         
29406         Roo.each(this.files, function(file){
29407             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29408                 files.push(file);
29409                 return;
29410             }
29411
29412             o.target.remove();
29413
29414         }, this);
29415         
29416         this.files = files;
29417         
29418         this.refresh();
29419     },
29420     
29421     clear : function()
29422     {
29423         Roo.each(this.files, function(file){
29424             if(!file.target){
29425                 return;
29426             }
29427             
29428             file.target.remove();
29429
29430         }, this);
29431         
29432         this.files = [];
29433         
29434         this.refresh();
29435     },
29436     
29437     onClick : function(e, el, o)
29438     {
29439         e.preventDefault();
29440         
29441         this.fireEvent('click', this, o);
29442         
29443     },
29444     
29445     closable : function(closable)
29446     {
29447         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29448             
29449             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29450             
29451             if(closable){
29452                 el.show();
29453                 return;
29454             }
29455             
29456             el.hide();
29457             
29458         }, this);
29459     },
29460     
29461     xhrOnLoad : function(xhr)
29462     {
29463         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29464             el.remove();
29465         }, this);
29466         
29467         if (xhr.readyState !== 4) {
29468             this.arrange();
29469             this.fireEvent('exception', this, xhr);
29470             return;
29471         }
29472
29473         var response = Roo.decode(xhr.responseText);
29474         
29475         if(!response.success){
29476             this.arrange();
29477             this.fireEvent('exception', this, xhr);
29478             return;
29479         }
29480         
29481         var file = this.renderPreview(response.data);
29482         
29483         this.files.push(file);
29484         
29485         this.arrange();
29486         
29487         this.fireEvent('afterupload', this, xhr);
29488         
29489     },
29490     
29491     xhrOnError : function(xhr)
29492     {
29493         Roo.log('xhr on error');
29494         
29495         var response = Roo.decode(xhr.responseText);
29496           
29497         Roo.log(response);
29498         
29499         this.arrange();
29500     },
29501     
29502     process : function(file)
29503     {
29504         if(this.fireEvent('process', this, file) !== false){
29505             if(this.editable && file.type.indexOf('image') != -1){
29506                 this.fireEvent('edit', this, file);
29507                 return;
29508             }
29509
29510             this.uploadStart(file, false);
29511
29512             return;
29513         }
29514         
29515     },
29516     
29517     uploadStart : function(file, crop)
29518     {
29519         this.xhr = new XMLHttpRequest();
29520         
29521         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29522             this.arrange();
29523             return;
29524         }
29525         
29526         file.xhr = this.xhr;
29527             
29528         this.managerEl.createChild({
29529             tag : 'div',
29530             cls : 'roo-document-manager-loading',
29531             cn : [
29532                 {
29533                     tag : 'div',
29534                     tooltip : file.name,
29535                     cls : 'roo-document-manager-thumb',
29536                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29537                 }
29538             ]
29539
29540         });
29541
29542         this.xhr.open(this.method, this.url, true);
29543         
29544         var headers = {
29545             "Accept": "application/json",
29546             "Cache-Control": "no-cache",
29547             "X-Requested-With": "XMLHttpRequest"
29548         };
29549         
29550         for (var headerName in headers) {
29551             var headerValue = headers[headerName];
29552             if (headerValue) {
29553                 this.xhr.setRequestHeader(headerName, headerValue);
29554             }
29555         }
29556         
29557         var _this = this;
29558         
29559         this.xhr.onload = function()
29560         {
29561             _this.xhrOnLoad(_this.xhr);
29562         }
29563         
29564         this.xhr.onerror = function()
29565         {
29566             _this.xhrOnError(_this.xhr);
29567         }
29568         
29569         var formData = new FormData();
29570
29571         formData.append('returnHTML', 'NO');
29572         
29573         if(crop){
29574             formData.append('crop', crop);
29575         }
29576         
29577         formData.append(this.paramName, file, file.name);
29578         
29579         var options = {
29580             file : file, 
29581             manually : false
29582         };
29583         
29584         if(this.fireEvent('prepare', this, formData, options) != false){
29585             
29586             if(options.manually){
29587                 return;
29588             }
29589             
29590             this.xhr.send(formData);
29591             return;
29592         };
29593         
29594         this.uploadCancel();
29595     },
29596     
29597     uploadCancel : function()
29598     {
29599         if (this.xhr) {
29600             this.xhr.abort();
29601         }
29602         
29603         this.delegates = [];
29604         
29605         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29606             el.remove();
29607         }, this);
29608         
29609         this.arrange();
29610     },
29611     
29612     renderPreview : function(file)
29613     {
29614         if(typeof(file.target) != 'undefined' && file.target){
29615             return file;
29616         }
29617         
29618         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29619         
29620         var previewEl = this.managerEl.createChild({
29621             tag : 'div',
29622             cls : 'roo-document-manager-preview',
29623             cn : [
29624                 {
29625                     tag : 'div',
29626                     tooltip : file[this.toolTipName],
29627                     cls : 'roo-document-manager-thumb',
29628                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29629                 },
29630                 {
29631                     tag : 'button',
29632                     cls : 'close',
29633                     html : '<i class="fa fa-times-circle"></i>'
29634                 }
29635             ]
29636         });
29637
29638         var close = previewEl.select('button.close', true).first();
29639
29640         close.on('click', this.onRemove, this, file);
29641
29642         file.target = previewEl;
29643
29644         var image = previewEl.select('img', true).first();
29645         
29646         var _this = this;
29647         
29648         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29649         
29650         image.on('click', this.onClick, this, file);
29651         
29652         this.fireEvent('previewrendered', this, file);
29653         
29654         return file;
29655         
29656     },
29657     
29658     onPreviewLoad : function(file, image)
29659     {
29660         if(typeof(file.target) == 'undefined' || !file.target){
29661             return;
29662         }
29663         
29664         var width = image.dom.naturalWidth || image.dom.width;
29665         var height = image.dom.naturalHeight || image.dom.height;
29666         
29667         if(!this.previewResize) {
29668             return;
29669         }
29670         
29671         if(width > height){
29672             file.target.addClass('wide');
29673             return;
29674         }
29675         
29676         file.target.addClass('tall');
29677         return;
29678         
29679     },
29680     
29681     uploadFromSource : function(file, crop)
29682     {
29683         this.xhr = new XMLHttpRequest();
29684         
29685         this.managerEl.createChild({
29686             tag : 'div',
29687             cls : 'roo-document-manager-loading',
29688             cn : [
29689                 {
29690                     tag : 'div',
29691                     tooltip : file.name,
29692                     cls : 'roo-document-manager-thumb',
29693                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29694                 }
29695             ]
29696
29697         });
29698
29699         this.xhr.open(this.method, this.url, true);
29700         
29701         var headers = {
29702             "Accept": "application/json",
29703             "Cache-Control": "no-cache",
29704             "X-Requested-With": "XMLHttpRequest"
29705         };
29706         
29707         for (var headerName in headers) {
29708             var headerValue = headers[headerName];
29709             if (headerValue) {
29710                 this.xhr.setRequestHeader(headerName, headerValue);
29711             }
29712         }
29713         
29714         var _this = this;
29715         
29716         this.xhr.onload = function()
29717         {
29718             _this.xhrOnLoad(_this.xhr);
29719         }
29720         
29721         this.xhr.onerror = function()
29722         {
29723             _this.xhrOnError(_this.xhr);
29724         }
29725         
29726         var formData = new FormData();
29727
29728         formData.append('returnHTML', 'NO');
29729         
29730         formData.append('crop', crop);
29731         
29732         if(typeof(file.filename) != 'undefined'){
29733             formData.append('filename', file.filename);
29734         }
29735         
29736         if(typeof(file.mimetype) != 'undefined'){
29737             formData.append('mimetype', file.mimetype);
29738         }
29739         
29740         Roo.log(formData);
29741         
29742         if(this.fireEvent('prepare', this, formData) != false){
29743             this.xhr.send(formData);
29744         };
29745     }
29746 });
29747
29748 /*
29749 * Licence: LGPL
29750 */
29751
29752 /**
29753  * @class Roo.bootstrap.DocumentViewer
29754  * @extends Roo.bootstrap.Component
29755  * Bootstrap DocumentViewer class
29756  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29757  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29758  * 
29759  * @constructor
29760  * Create a new DocumentViewer
29761  * @param {Object} config The config object
29762  */
29763
29764 Roo.bootstrap.DocumentViewer = function(config){
29765     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29766     
29767     this.addEvents({
29768         /**
29769          * @event initial
29770          * Fire after initEvent
29771          * @param {Roo.bootstrap.DocumentViewer} this
29772          */
29773         "initial" : true,
29774         /**
29775          * @event click
29776          * Fire after click
29777          * @param {Roo.bootstrap.DocumentViewer} this
29778          */
29779         "click" : true,
29780         /**
29781          * @event download
29782          * Fire after download button
29783          * @param {Roo.bootstrap.DocumentViewer} this
29784          */
29785         "download" : true,
29786         /**
29787          * @event trash
29788          * Fire after trash button
29789          * @param {Roo.bootstrap.DocumentViewer} this
29790          */
29791         "trash" : true
29792         
29793     });
29794 };
29795
29796 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29797     
29798     showDownload : true,
29799     
29800     showTrash : true,
29801     
29802     getAutoCreate : function()
29803     {
29804         var cfg = {
29805             tag : 'div',
29806             cls : 'roo-document-viewer',
29807             cn : [
29808                 {
29809                     tag : 'div',
29810                     cls : 'roo-document-viewer-body',
29811                     cn : [
29812                         {
29813                             tag : 'div',
29814                             cls : 'roo-document-viewer-thumb',
29815                             cn : [
29816                                 {
29817                                     tag : 'img',
29818                                     cls : 'roo-document-viewer-image'
29819                                 }
29820                             ]
29821                         }
29822                     ]
29823                 },
29824                 {
29825                     tag : 'div',
29826                     cls : 'roo-document-viewer-footer',
29827                     cn : {
29828                         tag : 'div',
29829                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29830                         cn : [
29831                             {
29832                                 tag : 'div',
29833                                 cls : 'btn-group roo-document-viewer-download',
29834                                 cn : [
29835                                     {
29836                                         tag : 'button',
29837                                         cls : 'btn btn-default',
29838                                         html : '<i class="fa fa-download"></i>'
29839                                     }
29840                                 ]
29841                             },
29842                             {
29843                                 tag : 'div',
29844                                 cls : 'btn-group roo-document-viewer-trash',
29845                                 cn : [
29846                                     {
29847                                         tag : 'button',
29848                                         cls : 'btn btn-default',
29849                                         html : '<i class="fa fa-trash"></i>'
29850                                     }
29851                                 ]
29852                             }
29853                         ]
29854                     }
29855                 }
29856             ]
29857         };
29858         
29859         return cfg;
29860     },
29861     
29862     initEvents : function()
29863     {
29864         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29865         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29866         
29867         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29868         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29869         
29870         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29871         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29872         
29873         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29874         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29875         
29876         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29877         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29878         
29879         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29880         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29881         
29882         this.bodyEl.on('click', this.onClick, this);
29883         this.downloadBtn.on('click', this.onDownload, this);
29884         this.trashBtn.on('click', this.onTrash, this);
29885         
29886         this.downloadBtn.hide();
29887         this.trashBtn.hide();
29888         
29889         if(this.showDownload){
29890             this.downloadBtn.show();
29891         }
29892         
29893         if(this.showTrash){
29894             this.trashBtn.show();
29895         }
29896         
29897         if(!this.showDownload && !this.showTrash) {
29898             this.footerEl.hide();
29899         }
29900         
29901     },
29902     
29903     initial : function()
29904     {
29905         this.fireEvent('initial', this);
29906         
29907     },
29908     
29909     onClick : function(e)
29910     {
29911         e.preventDefault();
29912         
29913         this.fireEvent('click', this);
29914     },
29915     
29916     onDownload : function(e)
29917     {
29918         e.preventDefault();
29919         
29920         this.fireEvent('download', this);
29921     },
29922     
29923     onTrash : function(e)
29924     {
29925         e.preventDefault();
29926         
29927         this.fireEvent('trash', this);
29928     }
29929     
29930 });
29931 /*
29932  * - LGPL
29933  *
29934  * nav progress bar
29935  * 
29936  */
29937
29938 /**
29939  * @class Roo.bootstrap.NavProgressBar
29940  * @extends Roo.bootstrap.Component
29941  * Bootstrap NavProgressBar class
29942  * 
29943  * @constructor
29944  * Create a new nav progress bar
29945  * @param {Object} config The config object
29946  */
29947
29948 Roo.bootstrap.NavProgressBar = function(config){
29949     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29950
29951     this.bullets = this.bullets || [];
29952    
29953 //    Roo.bootstrap.NavProgressBar.register(this);
29954      this.addEvents({
29955         /**
29956              * @event changed
29957              * Fires when the active item changes
29958              * @param {Roo.bootstrap.NavProgressBar} this
29959              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29960              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29961          */
29962         'changed': true
29963      });
29964     
29965 };
29966
29967 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29968     
29969     bullets : [],
29970     barItems : [],
29971     
29972     getAutoCreate : function()
29973     {
29974         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29975         
29976         cfg = {
29977             tag : 'div',
29978             cls : 'roo-navigation-bar-group',
29979             cn : [
29980                 {
29981                     tag : 'div',
29982                     cls : 'roo-navigation-top-bar'
29983                 },
29984                 {
29985                     tag : 'div',
29986                     cls : 'roo-navigation-bullets-bar',
29987                     cn : [
29988                         {
29989                             tag : 'ul',
29990                             cls : 'roo-navigation-bar'
29991                         }
29992                     ]
29993                 },
29994                 
29995                 {
29996                     tag : 'div',
29997                     cls : 'roo-navigation-bottom-bar'
29998                 }
29999             ]
30000             
30001         };
30002         
30003         return cfg;
30004         
30005     },
30006     
30007     initEvents: function() 
30008     {
30009         
30010     },
30011     
30012     onRender : function(ct, position) 
30013     {
30014         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30015         
30016         if(this.bullets.length){
30017             Roo.each(this.bullets, function(b){
30018                this.addItem(b);
30019             }, this);
30020         }
30021         
30022         this.format();
30023         
30024     },
30025     
30026     addItem : function(cfg)
30027     {
30028         var item = new Roo.bootstrap.NavProgressItem(cfg);
30029         
30030         item.parentId = this.id;
30031         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30032         
30033         if(cfg.html){
30034             var top = new Roo.bootstrap.Element({
30035                 tag : 'div',
30036                 cls : 'roo-navigation-bar-text'
30037             });
30038             
30039             var bottom = new Roo.bootstrap.Element({
30040                 tag : 'div',
30041                 cls : 'roo-navigation-bar-text'
30042             });
30043             
30044             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30045             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30046             
30047             var topText = new Roo.bootstrap.Element({
30048                 tag : 'span',
30049                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30050             });
30051             
30052             var bottomText = new Roo.bootstrap.Element({
30053                 tag : 'span',
30054                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30055             });
30056             
30057             topText.onRender(top.el, null);
30058             bottomText.onRender(bottom.el, null);
30059             
30060             item.topEl = top;
30061             item.bottomEl = bottom;
30062         }
30063         
30064         this.barItems.push(item);
30065         
30066         return item;
30067     },
30068     
30069     getActive : function()
30070     {
30071         var active = false;
30072         
30073         Roo.each(this.barItems, function(v){
30074             
30075             if (!v.isActive()) {
30076                 return;
30077             }
30078             
30079             active = v;
30080             return false;
30081             
30082         });
30083         
30084         return active;
30085     },
30086     
30087     setActiveItem : function(item)
30088     {
30089         var prev = false;
30090         
30091         Roo.each(this.barItems, function(v){
30092             if (v.rid == item.rid) {
30093                 return ;
30094             }
30095             
30096             if (v.isActive()) {
30097                 v.setActive(false);
30098                 prev = v;
30099             }
30100         });
30101
30102         item.setActive(true);
30103         
30104         this.fireEvent('changed', this, item, prev);
30105     },
30106     
30107     getBarItem: function(rid)
30108     {
30109         var ret = false;
30110         
30111         Roo.each(this.barItems, function(e) {
30112             if (e.rid != rid) {
30113                 return;
30114             }
30115             
30116             ret =  e;
30117             return false;
30118         });
30119         
30120         return ret;
30121     },
30122     
30123     indexOfItem : function(item)
30124     {
30125         var index = false;
30126         
30127         Roo.each(this.barItems, function(v, i){
30128             
30129             if (v.rid != item.rid) {
30130                 return;
30131             }
30132             
30133             index = i;
30134             return false
30135         });
30136         
30137         return index;
30138     },
30139     
30140     setActiveNext : function()
30141     {
30142         var i = this.indexOfItem(this.getActive());
30143         
30144         if (i > this.barItems.length) {
30145             return;
30146         }
30147         
30148         this.setActiveItem(this.barItems[i+1]);
30149     },
30150     
30151     setActivePrev : function()
30152     {
30153         var i = this.indexOfItem(this.getActive());
30154         
30155         if (i  < 1) {
30156             return;
30157         }
30158         
30159         this.setActiveItem(this.barItems[i-1]);
30160     },
30161     
30162     format : function()
30163     {
30164         if(!this.barItems.length){
30165             return;
30166         }
30167      
30168         var width = 100 / this.barItems.length;
30169         
30170         Roo.each(this.barItems, function(i){
30171             i.el.setStyle('width', width + '%');
30172             i.topEl.el.setStyle('width', width + '%');
30173             i.bottomEl.el.setStyle('width', width + '%');
30174         }, this);
30175         
30176     }
30177     
30178 });
30179 /*
30180  * - LGPL
30181  *
30182  * Nav Progress Item
30183  * 
30184  */
30185
30186 /**
30187  * @class Roo.bootstrap.NavProgressItem
30188  * @extends Roo.bootstrap.Component
30189  * Bootstrap NavProgressItem class
30190  * @cfg {String} rid the reference id
30191  * @cfg {Boolean} active (true|false) Is item active default false
30192  * @cfg {Boolean} disabled (true|false) Is item active default false
30193  * @cfg {String} html
30194  * @cfg {String} position (top|bottom) text position default bottom
30195  * @cfg {String} icon show icon instead of number
30196  * 
30197  * @constructor
30198  * Create a new NavProgressItem
30199  * @param {Object} config The config object
30200  */
30201 Roo.bootstrap.NavProgressItem = function(config){
30202     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30203     this.addEvents({
30204         // raw events
30205         /**
30206          * @event click
30207          * The raw click event for the entire grid.
30208          * @param {Roo.bootstrap.NavProgressItem} this
30209          * @param {Roo.EventObject} e
30210          */
30211         "click" : true
30212     });
30213    
30214 };
30215
30216 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30217     
30218     rid : '',
30219     active : false,
30220     disabled : false,
30221     html : '',
30222     position : 'bottom',
30223     icon : false,
30224     
30225     getAutoCreate : function()
30226     {
30227         var iconCls = 'roo-navigation-bar-item-icon';
30228         
30229         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30230         
30231         var cfg = {
30232             tag: 'li',
30233             cls: 'roo-navigation-bar-item',
30234             cn : [
30235                 {
30236                     tag : 'i',
30237                     cls : iconCls
30238                 }
30239             ]
30240         };
30241         
30242         if(this.active){
30243             cfg.cls += ' active';
30244         }
30245         if(this.disabled){
30246             cfg.cls += ' disabled';
30247         }
30248         
30249         return cfg;
30250     },
30251     
30252     disable : function()
30253     {
30254         this.setDisabled(true);
30255     },
30256     
30257     enable : function()
30258     {
30259         this.setDisabled(false);
30260     },
30261     
30262     initEvents: function() 
30263     {
30264         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30265         
30266         this.iconEl.on('click', this.onClick, this);
30267     },
30268     
30269     onClick : function(e)
30270     {
30271         e.preventDefault();
30272         
30273         if(this.disabled){
30274             return;
30275         }
30276         
30277         if(this.fireEvent('click', this, e) === false){
30278             return;
30279         };
30280         
30281         this.parent().setActiveItem(this);
30282     },
30283     
30284     isActive: function () 
30285     {
30286         return this.active;
30287     },
30288     
30289     setActive : function(state)
30290     {
30291         if(this.active == state){
30292             return;
30293         }
30294         
30295         this.active = state;
30296         
30297         if (state) {
30298             this.el.addClass('active');
30299             return;
30300         }
30301         
30302         this.el.removeClass('active');
30303         
30304         return;
30305     },
30306     
30307     setDisabled : function(state)
30308     {
30309         if(this.disabled == state){
30310             return;
30311         }
30312         
30313         this.disabled = state;
30314         
30315         if (state) {
30316             this.el.addClass('disabled');
30317             return;
30318         }
30319         
30320         this.el.removeClass('disabled');
30321     },
30322     
30323     tooltipEl : function()
30324     {
30325         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30326     }
30327 });
30328  
30329
30330  /*
30331  * - LGPL
30332  *
30333  * FieldLabel
30334  * 
30335  */
30336
30337 /**
30338  * @class Roo.bootstrap.FieldLabel
30339  * @extends Roo.bootstrap.Component
30340  * Bootstrap FieldLabel class
30341  * @cfg {String} html contents of the element
30342  * @cfg {String} tag tag of the element default label
30343  * @cfg {String} cls class of the element
30344  * @cfg {String} target label target 
30345  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30346  * @cfg {String} invalidClass default "text-warning"
30347  * @cfg {String} validClass default "text-success"
30348  * @cfg {String} iconTooltip default "This field is required"
30349  * @cfg {String} indicatorpos (left|right) default left
30350  * 
30351  * @constructor
30352  * Create a new FieldLabel
30353  * @param {Object} config The config object
30354  */
30355
30356 Roo.bootstrap.FieldLabel = function(config){
30357     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30358     
30359     this.addEvents({
30360             /**
30361              * @event invalid
30362              * Fires after the field has been marked as invalid.
30363              * @param {Roo.form.FieldLabel} this
30364              * @param {String} msg The validation message
30365              */
30366             invalid : true,
30367             /**
30368              * @event valid
30369              * Fires after the field has been validated with no errors.
30370              * @param {Roo.form.FieldLabel} this
30371              */
30372             valid : true
30373         });
30374 };
30375
30376 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30377     
30378     tag: 'label',
30379     cls: '',
30380     html: '',
30381     target: '',
30382     allowBlank : true,
30383     invalidClass : 'has-warning',
30384     validClass : 'has-success',
30385     iconTooltip : 'This field is required',
30386     indicatorpos : 'left',
30387     
30388     getAutoCreate : function(){
30389         
30390         var cls = "";
30391         if (!this.allowBlank) {
30392             cls  = "visible";
30393         }
30394         
30395         var cfg = {
30396             tag : this.tag,
30397             cls : 'roo-bootstrap-field-label ' + this.cls,
30398             for : this.target,
30399             cn : [
30400                 {
30401                     tag : 'i',
30402                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30403                     tooltip : this.iconTooltip
30404                 },
30405                 {
30406                     tag : 'span',
30407                     html : this.html
30408                 }
30409             ] 
30410         };
30411         
30412         if(this.indicatorpos == 'right'){
30413             var cfg = {
30414                 tag : this.tag,
30415                 cls : 'roo-bootstrap-field-label ' + this.cls,
30416                 for : this.target,
30417                 cn : [
30418                     {
30419                         tag : 'span',
30420                         html : this.html
30421                     },
30422                     {
30423                         tag : 'i',
30424                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30425                         tooltip : this.iconTooltip
30426                     }
30427                 ] 
30428             };
30429         }
30430         
30431         return cfg;
30432     },
30433     
30434     initEvents: function() 
30435     {
30436         Roo.bootstrap.Element.superclass.initEvents.call(this);
30437         
30438         this.indicator = this.indicatorEl();
30439         
30440         if(this.indicator){
30441             this.indicator.removeClass('visible');
30442             this.indicator.addClass('invisible');
30443         }
30444         
30445         Roo.bootstrap.FieldLabel.register(this);
30446     },
30447     
30448     indicatorEl : function()
30449     {
30450         var indicator = this.el.select('i.roo-required-indicator',true).first();
30451         
30452         if(!indicator){
30453             return false;
30454         }
30455         
30456         return indicator;
30457         
30458     },
30459     
30460     /**
30461      * Mark this field as valid
30462      */
30463     markValid : function()
30464     {
30465         if(this.indicator){
30466             this.indicator.removeClass('visible');
30467             this.indicator.addClass('invisible');
30468         }
30469         
30470         this.el.removeClass(this.invalidClass);
30471         
30472         this.el.addClass(this.validClass);
30473         
30474         this.fireEvent('valid', this);
30475     },
30476     
30477     /**
30478      * Mark this field as invalid
30479      * @param {String} msg The validation message
30480      */
30481     markInvalid : function(msg)
30482     {
30483         if(this.indicator){
30484             this.indicator.removeClass('invisible');
30485             this.indicator.addClass('visible');
30486         }
30487         
30488         this.el.removeClass(this.validClass);
30489         
30490         this.el.addClass(this.invalidClass);
30491         
30492         this.fireEvent('invalid', this, msg);
30493     }
30494     
30495    
30496 });
30497
30498 Roo.apply(Roo.bootstrap.FieldLabel, {
30499     
30500     groups: {},
30501     
30502      /**
30503     * register a FieldLabel Group
30504     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30505     */
30506     register : function(label)
30507     {
30508         if(this.groups.hasOwnProperty(label.target)){
30509             return;
30510         }
30511      
30512         this.groups[label.target] = label;
30513         
30514     },
30515     /**
30516     * fetch a FieldLabel Group based on the target
30517     * @param {string} target
30518     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30519     */
30520     get: function(target) {
30521         if (typeof(this.groups[target]) == 'undefined') {
30522             return false;
30523         }
30524         
30525         return this.groups[target] ;
30526     }
30527 });
30528
30529  
30530
30531  /*
30532  * - LGPL
30533  *
30534  * page DateSplitField.
30535  * 
30536  */
30537
30538
30539 /**
30540  * @class Roo.bootstrap.DateSplitField
30541  * @extends Roo.bootstrap.Component
30542  * Bootstrap DateSplitField class
30543  * @cfg {string} fieldLabel - the label associated
30544  * @cfg {Number} labelWidth set the width of label (0-12)
30545  * @cfg {String} labelAlign (top|left)
30546  * @cfg {Boolean} dayAllowBlank (true|false) default false
30547  * @cfg {Boolean} monthAllowBlank (true|false) default false
30548  * @cfg {Boolean} yearAllowBlank (true|false) default false
30549  * @cfg {string} dayPlaceholder 
30550  * @cfg {string} monthPlaceholder
30551  * @cfg {string} yearPlaceholder
30552  * @cfg {string} dayFormat default 'd'
30553  * @cfg {string} monthFormat default 'm'
30554  * @cfg {string} yearFormat default 'Y'
30555  * @cfg {Number} labellg set the width of label (1-12)
30556  * @cfg {Number} labelmd set the width of label (1-12)
30557  * @cfg {Number} labelsm set the width of label (1-12)
30558  * @cfg {Number} labelxs set the width of label (1-12)
30559
30560  *     
30561  * @constructor
30562  * Create a new DateSplitField
30563  * @param {Object} config The config object
30564  */
30565
30566 Roo.bootstrap.DateSplitField = function(config){
30567     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30568     
30569     this.addEvents({
30570         // raw events
30571          /**
30572          * @event years
30573          * getting the data of years
30574          * @param {Roo.bootstrap.DateSplitField} this
30575          * @param {Object} years
30576          */
30577         "years" : true,
30578         /**
30579          * @event days
30580          * getting the data of days
30581          * @param {Roo.bootstrap.DateSplitField} this
30582          * @param {Object} days
30583          */
30584         "days" : true,
30585         /**
30586          * @event invalid
30587          * Fires after the field has been marked as invalid.
30588          * @param {Roo.form.Field} this
30589          * @param {String} msg The validation message
30590          */
30591         invalid : true,
30592        /**
30593          * @event valid
30594          * Fires after the field has been validated with no errors.
30595          * @param {Roo.form.Field} this
30596          */
30597         valid : true
30598     });
30599 };
30600
30601 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30602     
30603     fieldLabel : '',
30604     labelAlign : 'top',
30605     labelWidth : 3,
30606     dayAllowBlank : false,
30607     monthAllowBlank : false,
30608     yearAllowBlank : false,
30609     dayPlaceholder : '',
30610     monthPlaceholder : '',
30611     yearPlaceholder : '',
30612     dayFormat : 'd',
30613     monthFormat : 'm',
30614     yearFormat : 'Y',
30615     isFormField : true,
30616     labellg : 0,
30617     labelmd : 0,
30618     labelsm : 0,
30619     labelxs : 0,
30620     
30621     getAutoCreate : function()
30622     {
30623         var cfg = {
30624             tag : 'div',
30625             cls : 'row roo-date-split-field-group',
30626             cn : [
30627                 {
30628                     tag : 'input',
30629                     type : 'hidden',
30630                     cls : 'form-hidden-field roo-date-split-field-group-value',
30631                     name : this.name
30632                 }
30633             ]
30634         };
30635         
30636         var labelCls = 'col-md-12';
30637         var contentCls = 'col-md-4';
30638         
30639         if(this.fieldLabel){
30640             
30641             var label = {
30642                 tag : 'div',
30643                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30644                 cn : [
30645                     {
30646                         tag : 'label',
30647                         html : this.fieldLabel
30648                     }
30649                 ]
30650             };
30651             
30652             if(this.labelAlign == 'left'){
30653             
30654                 if(this.labelWidth > 12){
30655                     label.style = "width: " + this.labelWidth + 'px';
30656                 }
30657
30658                 if(this.labelWidth < 13 && this.labelmd == 0){
30659                     this.labelmd = this.labelWidth;
30660                 }
30661
30662                 if(this.labellg > 0){
30663                     labelCls = ' col-lg-' + this.labellg;
30664                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30665                 }
30666
30667                 if(this.labelmd > 0){
30668                     labelCls = ' col-md-' + this.labelmd;
30669                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30670                 }
30671
30672                 if(this.labelsm > 0){
30673                     labelCls = ' col-sm-' + this.labelsm;
30674                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30675                 }
30676
30677                 if(this.labelxs > 0){
30678                     labelCls = ' col-xs-' + this.labelxs;
30679                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30680                 }
30681             }
30682             
30683             label.cls += ' ' + labelCls;
30684             
30685             cfg.cn.push(label);
30686         }
30687         
30688         Roo.each(['day', 'month', 'year'], function(t){
30689             cfg.cn.push({
30690                 tag : 'div',
30691                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30692             });
30693         }, this);
30694         
30695         return cfg;
30696     },
30697     
30698     inputEl: function ()
30699     {
30700         return this.el.select('.roo-date-split-field-group-value', true).first();
30701     },
30702     
30703     onRender : function(ct, position) 
30704     {
30705         var _this = this;
30706         
30707         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30708         
30709         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30710         
30711         this.dayField = new Roo.bootstrap.ComboBox({
30712             allowBlank : this.dayAllowBlank,
30713             alwaysQuery : true,
30714             displayField : 'value',
30715             editable : false,
30716             fieldLabel : '',
30717             forceSelection : true,
30718             mode : 'local',
30719             placeholder : this.dayPlaceholder,
30720             selectOnFocus : true,
30721             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30722             triggerAction : 'all',
30723             typeAhead : true,
30724             valueField : 'value',
30725             store : new Roo.data.SimpleStore({
30726                 data : (function() {    
30727                     var days = [];
30728                     _this.fireEvent('days', _this, days);
30729                     return days;
30730                 })(),
30731                 fields : [ 'value' ]
30732             }),
30733             listeners : {
30734                 select : function (_self, record, index)
30735                 {
30736                     _this.setValue(_this.getValue());
30737                 }
30738             }
30739         });
30740
30741         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30742         
30743         this.monthField = new Roo.bootstrap.MonthField({
30744             after : '<i class=\"fa fa-calendar\"></i>',
30745             allowBlank : this.monthAllowBlank,
30746             placeholder : this.monthPlaceholder,
30747             readOnly : true,
30748             listeners : {
30749                 render : function (_self)
30750                 {
30751                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30752                         e.preventDefault();
30753                         _self.focus();
30754                     });
30755                 },
30756                 select : function (_self, oldvalue, newvalue)
30757                 {
30758                     _this.setValue(_this.getValue());
30759                 }
30760             }
30761         });
30762         
30763         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30764         
30765         this.yearField = new Roo.bootstrap.ComboBox({
30766             allowBlank : this.yearAllowBlank,
30767             alwaysQuery : true,
30768             displayField : 'value',
30769             editable : false,
30770             fieldLabel : '',
30771             forceSelection : true,
30772             mode : 'local',
30773             placeholder : this.yearPlaceholder,
30774             selectOnFocus : true,
30775             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30776             triggerAction : 'all',
30777             typeAhead : true,
30778             valueField : 'value',
30779             store : new Roo.data.SimpleStore({
30780                 data : (function() {
30781                     var years = [];
30782                     _this.fireEvent('years', _this, years);
30783                     return years;
30784                 })(),
30785                 fields : [ 'value' ]
30786             }),
30787             listeners : {
30788                 select : function (_self, record, index)
30789                 {
30790                     _this.setValue(_this.getValue());
30791                 }
30792             }
30793         });
30794
30795         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30796     },
30797     
30798     setValue : function(v, format)
30799     {
30800         this.inputEl.dom.value = v;
30801         
30802         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30803         
30804         var d = Date.parseDate(v, f);
30805         
30806         if(!d){
30807             this.validate();
30808             return;
30809         }
30810         
30811         this.setDay(d.format(this.dayFormat));
30812         this.setMonth(d.format(this.monthFormat));
30813         this.setYear(d.format(this.yearFormat));
30814         
30815         this.validate();
30816         
30817         return;
30818     },
30819     
30820     setDay : function(v)
30821     {
30822         this.dayField.setValue(v);
30823         this.inputEl.dom.value = this.getValue();
30824         this.validate();
30825         return;
30826     },
30827     
30828     setMonth : function(v)
30829     {
30830         this.monthField.setValue(v, true);
30831         this.inputEl.dom.value = this.getValue();
30832         this.validate();
30833         return;
30834     },
30835     
30836     setYear : function(v)
30837     {
30838         this.yearField.setValue(v);
30839         this.inputEl.dom.value = this.getValue();
30840         this.validate();
30841         return;
30842     },
30843     
30844     getDay : function()
30845     {
30846         return this.dayField.getValue();
30847     },
30848     
30849     getMonth : function()
30850     {
30851         return this.monthField.getValue();
30852     },
30853     
30854     getYear : function()
30855     {
30856         return this.yearField.getValue();
30857     },
30858     
30859     getValue : function()
30860     {
30861         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30862         
30863         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30864         
30865         return date;
30866     },
30867     
30868     reset : function()
30869     {
30870         this.setDay('');
30871         this.setMonth('');
30872         this.setYear('');
30873         this.inputEl.dom.value = '';
30874         this.validate();
30875         return;
30876     },
30877     
30878     validate : function()
30879     {
30880         var d = this.dayField.validate();
30881         var m = this.monthField.validate();
30882         var y = this.yearField.validate();
30883         
30884         var valid = true;
30885         
30886         if(
30887                 (!this.dayAllowBlank && !d) ||
30888                 (!this.monthAllowBlank && !m) ||
30889                 (!this.yearAllowBlank && !y)
30890         ){
30891             valid = false;
30892         }
30893         
30894         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30895             return valid;
30896         }
30897         
30898         if(valid){
30899             this.markValid();
30900             return valid;
30901         }
30902         
30903         this.markInvalid();
30904         
30905         return valid;
30906     },
30907     
30908     markValid : function()
30909     {
30910         
30911         var label = this.el.select('label', true).first();
30912         var icon = this.el.select('i.fa-star', true).first();
30913
30914         if(label && icon){
30915             icon.remove();
30916         }
30917         
30918         this.fireEvent('valid', this);
30919     },
30920     
30921      /**
30922      * Mark this field as invalid
30923      * @param {String} msg The validation message
30924      */
30925     markInvalid : function(msg)
30926     {
30927         
30928         var label = this.el.select('label', true).first();
30929         var icon = this.el.select('i.fa-star', true).first();
30930
30931         if(label && !icon){
30932             this.el.select('.roo-date-split-field-label', true).createChild({
30933                 tag : 'i',
30934                 cls : 'text-danger fa fa-lg fa-star',
30935                 tooltip : 'This field is required',
30936                 style : 'margin-right:5px;'
30937             }, label, true);
30938         }
30939         
30940         this.fireEvent('invalid', this, msg);
30941     },
30942     
30943     clearInvalid : function()
30944     {
30945         var label = this.el.select('label', true).first();
30946         var icon = this.el.select('i.fa-star', true).first();
30947
30948         if(label && icon){
30949             icon.remove();
30950         }
30951         
30952         this.fireEvent('valid', this);
30953     },
30954     
30955     getName: function()
30956     {
30957         return this.name;
30958     }
30959     
30960 });
30961
30962  /**
30963  *
30964  * This is based on 
30965  * http://masonry.desandro.com
30966  *
30967  * The idea is to render all the bricks based on vertical width...
30968  *
30969  * The original code extends 'outlayer' - we might need to use that....
30970  * 
30971  */
30972
30973
30974 /**
30975  * @class Roo.bootstrap.LayoutMasonry
30976  * @extends Roo.bootstrap.Component
30977  * Bootstrap Layout Masonry class
30978  * 
30979  * @constructor
30980  * Create a new Element
30981  * @param {Object} config The config object
30982  */
30983
30984 Roo.bootstrap.LayoutMasonry = function(config){
30985     
30986     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30987     
30988     this.bricks = [];
30989     
30990     Roo.bootstrap.LayoutMasonry.register(this);
30991     
30992     this.addEvents({
30993         // raw events
30994         /**
30995          * @event layout
30996          * Fire after layout the items
30997          * @param {Roo.bootstrap.LayoutMasonry} this
30998          * @param {Roo.EventObject} e
30999          */
31000         "layout" : true
31001     });
31002     
31003 };
31004
31005 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31006     
31007     /**
31008      * @cfg {Boolean} isLayoutInstant = no animation?
31009      */   
31010     isLayoutInstant : false, // needed?
31011    
31012     /**
31013      * @cfg {Number} boxWidth  width of the columns
31014      */   
31015     boxWidth : 450,
31016     
31017       /**
31018      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31019      */   
31020     boxHeight : 0,
31021     
31022     /**
31023      * @cfg {Number} padWidth padding below box..
31024      */   
31025     padWidth : 10, 
31026     
31027     /**
31028      * @cfg {Number} gutter gutter width..
31029      */   
31030     gutter : 10,
31031     
31032      /**
31033      * @cfg {Number} maxCols maximum number of columns
31034      */   
31035     
31036     maxCols: 0,
31037     
31038     /**
31039      * @cfg {Boolean} isAutoInitial defalut true
31040      */   
31041     isAutoInitial : true, 
31042     
31043     containerWidth: 0,
31044     
31045     /**
31046      * @cfg {Boolean} isHorizontal defalut false
31047      */   
31048     isHorizontal : false, 
31049
31050     currentSize : null,
31051     
31052     tag: 'div',
31053     
31054     cls: '',
31055     
31056     bricks: null, //CompositeElement
31057     
31058     cols : 1,
31059     
31060     _isLayoutInited : false,
31061     
31062 //    isAlternative : false, // only use for vertical layout...
31063     
31064     /**
31065      * @cfg {Number} alternativePadWidth padding below box..
31066      */   
31067     alternativePadWidth : 50,
31068     
31069     selectedBrick : [],
31070     
31071     getAutoCreate : function(){
31072         
31073         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31074         
31075         var cfg = {
31076             tag: this.tag,
31077             cls: 'blog-masonary-wrapper ' + this.cls,
31078             cn : {
31079                 cls : 'mas-boxes masonary'
31080             }
31081         };
31082         
31083         return cfg;
31084     },
31085     
31086     getChildContainer: function( )
31087     {
31088         if (this.boxesEl) {
31089             return this.boxesEl;
31090         }
31091         
31092         this.boxesEl = this.el.select('.mas-boxes').first();
31093         
31094         return this.boxesEl;
31095     },
31096     
31097     
31098     initEvents : function()
31099     {
31100         var _this = this;
31101         
31102         if(this.isAutoInitial){
31103             Roo.log('hook children rendered');
31104             this.on('childrenrendered', function() {
31105                 Roo.log('children rendered');
31106                 _this.initial();
31107             } ,this);
31108         }
31109     },
31110     
31111     initial : function()
31112     {
31113         this.selectedBrick = [];
31114         
31115         this.currentSize = this.el.getBox(true);
31116         
31117         Roo.EventManager.onWindowResize(this.resize, this); 
31118
31119         if(!this.isAutoInitial){
31120             this.layout();
31121             return;
31122         }
31123         
31124         this.layout();
31125         
31126         return;
31127         //this.layout.defer(500,this);
31128         
31129     },
31130     
31131     resize : function()
31132     {
31133         var cs = this.el.getBox(true);
31134         
31135         if (
31136                 this.currentSize.width == cs.width && 
31137                 this.currentSize.x == cs.x && 
31138                 this.currentSize.height == cs.height && 
31139                 this.currentSize.y == cs.y 
31140         ) {
31141             Roo.log("no change in with or X or Y");
31142             return;
31143         }
31144         
31145         this.currentSize = cs;
31146         
31147         this.layout();
31148         
31149     },
31150     
31151     layout : function()
31152     {   
31153         this._resetLayout();
31154         
31155         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31156         
31157         this.layoutItems( isInstant );
31158       
31159         this._isLayoutInited = true;
31160         
31161         this.fireEvent('layout', this);
31162         
31163     },
31164     
31165     _resetLayout : function()
31166     {
31167         if(this.isHorizontal){
31168             this.horizontalMeasureColumns();
31169             return;
31170         }
31171         
31172         this.verticalMeasureColumns();
31173         
31174     },
31175     
31176     verticalMeasureColumns : function()
31177     {
31178         this.getContainerWidth();
31179         
31180 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31181 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31182 //            return;
31183 //        }
31184         
31185         var boxWidth = this.boxWidth + this.padWidth;
31186         
31187         if(this.containerWidth < this.boxWidth){
31188             boxWidth = this.containerWidth
31189         }
31190         
31191         var containerWidth = this.containerWidth;
31192         
31193         var cols = Math.floor(containerWidth / boxWidth);
31194         
31195         this.cols = Math.max( cols, 1 );
31196         
31197         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31198         
31199         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31200         
31201         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31202         
31203         this.colWidth = boxWidth + avail - this.padWidth;
31204         
31205         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31206         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31207     },
31208     
31209     horizontalMeasureColumns : function()
31210     {
31211         this.getContainerWidth();
31212         
31213         var boxWidth = this.boxWidth;
31214         
31215         if(this.containerWidth < boxWidth){
31216             boxWidth = this.containerWidth;
31217         }
31218         
31219         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31220         
31221         this.el.setHeight(boxWidth);
31222         
31223     },
31224     
31225     getContainerWidth : function()
31226     {
31227         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31228     },
31229     
31230     layoutItems : function( isInstant )
31231     {
31232         Roo.log(this.bricks);
31233         
31234         var items = Roo.apply([], this.bricks);
31235         
31236         if(this.isHorizontal){
31237             this._horizontalLayoutItems( items , isInstant );
31238             return;
31239         }
31240         
31241 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31242 //            this._verticalAlternativeLayoutItems( items , isInstant );
31243 //            return;
31244 //        }
31245         
31246         this._verticalLayoutItems( items , isInstant );
31247         
31248     },
31249     
31250     _verticalLayoutItems : function ( items , isInstant)
31251     {
31252         if ( !items || !items.length ) {
31253             return;
31254         }
31255         
31256         var standard = [
31257             ['xs', 'xs', 'xs', 'tall'],
31258             ['xs', 'xs', 'tall'],
31259             ['xs', 'xs', 'sm'],
31260             ['xs', 'xs', 'xs'],
31261             ['xs', 'tall'],
31262             ['xs', 'sm'],
31263             ['xs', 'xs'],
31264             ['xs'],
31265             
31266             ['sm', 'xs', 'xs'],
31267             ['sm', 'xs'],
31268             ['sm'],
31269             
31270             ['tall', 'xs', 'xs', 'xs'],
31271             ['tall', 'xs', 'xs'],
31272             ['tall', 'xs'],
31273             ['tall']
31274             
31275         ];
31276         
31277         var queue = [];
31278         
31279         var boxes = [];
31280         
31281         var box = [];
31282         
31283         Roo.each(items, function(item, k){
31284             
31285             switch (item.size) {
31286                 // these layouts take up a full box,
31287                 case 'md' :
31288                 case 'md-left' :
31289                 case 'md-right' :
31290                 case 'wide' :
31291                     
31292                     if(box.length){
31293                         boxes.push(box);
31294                         box = [];
31295                     }
31296                     
31297                     boxes.push([item]);
31298                     
31299                     break;
31300                     
31301                 case 'xs' :
31302                 case 'sm' :
31303                 case 'tall' :
31304                     
31305                     box.push(item);
31306                     
31307                     break;
31308                 default :
31309                     break;
31310                     
31311             }
31312             
31313         }, this);
31314         
31315         if(box.length){
31316             boxes.push(box);
31317             box = [];
31318         }
31319         
31320         var filterPattern = function(box, length)
31321         {
31322             if(!box.length){
31323                 return;
31324             }
31325             
31326             var match = false;
31327             
31328             var pattern = box.slice(0, length);
31329             
31330             var format = [];
31331             
31332             Roo.each(pattern, function(i){
31333                 format.push(i.size);
31334             }, this);
31335             
31336             Roo.each(standard, function(s){
31337                 
31338                 if(String(s) != String(format)){
31339                     return;
31340                 }
31341                 
31342                 match = true;
31343                 return false;
31344                 
31345             }, this);
31346             
31347             if(!match && length == 1){
31348                 return;
31349             }
31350             
31351             if(!match){
31352                 filterPattern(box, length - 1);
31353                 return;
31354             }
31355                 
31356             queue.push(pattern);
31357
31358             box = box.slice(length, box.length);
31359
31360             filterPattern(box, 4);
31361
31362             return;
31363             
31364         }
31365         
31366         Roo.each(boxes, function(box, k){
31367             
31368             if(!box.length){
31369                 return;
31370             }
31371             
31372             if(box.length == 1){
31373                 queue.push(box);
31374                 return;
31375             }
31376             
31377             filterPattern(box, 4);
31378             
31379         }, this);
31380         
31381         this._processVerticalLayoutQueue( queue, isInstant );
31382         
31383     },
31384     
31385 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31386 //    {
31387 //        if ( !items || !items.length ) {
31388 //            return;
31389 //        }
31390 //
31391 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31392 //        
31393 //    },
31394     
31395     _horizontalLayoutItems : function ( items , isInstant)
31396     {
31397         if ( !items || !items.length || items.length < 3) {
31398             return;
31399         }
31400         
31401         items.reverse();
31402         
31403         var eItems = items.slice(0, 3);
31404         
31405         items = items.slice(3, items.length);
31406         
31407         var standard = [
31408             ['xs', 'xs', 'xs', 'wide'],
31409             ['xs', 'xs', 'wide'],
31410             ['xs', 'xs', 'sm'],
31411             ['xs', 'xs', 'xs'],
31412             ['xs', 'wide'],
31413             ['xs', 'sm'],
31414             ['xs', 'xs'],
31415             ['xs'],
31416             
31417             ['sm', 'xs', 'xs'],
31418             ['sm', 'xs'],
31419             ['sm'],
31420             
31421             ['wide', 'xs', 'xs', 'xs'],
31422             ['wide', 'xs', 'xs'],
31423             ['wide', 'xs'],
31424             ['wide'],
31425             
31426             ['wide-thin']
31427         ];
31428         
31429         var queue = [];
31430         
31431         var boxes = [];
31432         
31433         var box = [];
31434         
31435         Roo.each(items, function(item, k){
31436             
31437             switch (item.size) {
31438                 case 'md' :
31439                 case 'md-left' :
31440                 case 'md-right' :
31441                 case 'tall' :
31442                     
31443                     if(box.length){
31444                         boxes.push(box);
31445                         box = [];
31446                     }
31447                     
31448                     boxes.push([item]);
31449                     
31450                     break;
31451                     
31452                 case 'xs' :
31453                 case 'sm' :
31454                 case 'wide' :
31455                 case 'wide-thin' :
31456                     
31457                     box.push(item);
31458                     
31459                     break;
31460                 default :
31461                     break;
31462                     
31463             }
31464             
31465         }, this);
31466         
31467         if(box.length){
31468             boxes.push(box);
31469             box = [];
31470         }
31471         
31472         var filterPattern = function(box, length)
31473         {
31474             if(!box.length){
31475                 return;
31476             }
31477             
31478             var match = false;
31479             
31480             var pattern = box.slice(0, length);
31481             
31482             var format = [];
31483             
31484             Roo.each(pattern, function(i){
31485                 format.push(i.size);
31486             }, this);
31487             
31488             Roo.each(standard, function(s){
31489                 
31490                 if(String(s) != String(format)){
31491                     return;
31492                 }
31493                 
31494                 match = true;
31495                 return false;
31496                 
31497             }, this);
31498             
31499             if(!match && length == 1){
31500                 return;
31501             }
31502             
31503             if(!match){
31504                 filterPattern(box, length - 1);
31505                 return;
31506             }
31507                 
31508             queue.push(pattern);
31509
31510             box = box.slice(length, box.length);
31511
31512             filterPattern(box, 4);
31513
31514             return;
31515             
31516         }
31517         
31518         Roo.each(boxes, function(box, k){
31519             
31520             if(!box.length){
31521                 return;
31522             }
31523             
31524             if(box.length == 1){
31525                 queue.push(box);
31526                 return;
31527             }
31528             
31529             filterPattern(box, 4);
31530             
31531         }, this);
31532         
31533         
31534         var prune = [];
31535         
31536         var pos = this.el.getBox(true);
31537         
31538         var minX = pos.x;
31539         
31540         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31541         
31542         var hit_end = false;
31543         
31544         Roo.each(queue, function(box){
31545             
31546             if(hit_end){
31547                 
31548                 Roo.each(box, function(b){
31549                 
31550                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31551                     b.el.hide();
31552
31553                 }, this);
31554
31555                 return;
31556             }
31557             
31558             var mx = 0;
31559             
31560             Roo.each(box, function(b){
31561                 
31562                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31563                 b.el.show();
31564
31565                 mx = Math.max(mx, b.x);
31566                 
31567             }, this);
31568             
31569             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31570             
31571             if(maxX < minX){
31572                 
31573                 Roo.each(box, function(b){
31574                 
31575                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31576                     b.el.hide();
31577                     
31578                 }, this);
31579                 
31580                 hit_end = true;
31581                 
31582                 return;
31583             }
31584             
31585             prune.push(box);
31586             
31587         }, this);
31588         
31589         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31590     },
31591     
31592     /** Sets position of item in DOM
31593     * @param {Element} item
31594     * @param {Number} x - horizontal position
31595     * @param {Number} y - vertical position
31596     * @param {Boolean} isInstant - disables transitions
31597     */
31598     _processVerticalLayoutQueue : function( queue, isInstant )
31599     {
31600         var pos = this.el.getBox(true);
31601         var x = pos.x;
31602         var y = pos.y;
31603         var maxY = [];
31604         
31605         for (var i = 0; i < this.cols; i++){
31606             maxY[i] = pos.y;
31607         }
31608         
31609         Roo.each(queue, function(box, k){
31610             
31611             var col = k % this.cols;
31612             
31613             Roo.each(box, function(b,kk){
31614                 
31615                 b.el.position('absolute');
31616                 
31617                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31618                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31619                 
31620                 if(b.size == 'md-left' || b.size == 'md-right'){
31621                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31622                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31623                 }
31624                 
31625                 b.el.setWidth(width);
31626                 b.el.setHeight(height);
31627                 // iframe?
31628                 b.el.select('iframe',true).setSize(width,height);
31629                 
31630             }, this);
31631             
31632             for (var i = 0; i < this.cols; i++){
31633                 
31634                 if(maxY[i] < maxY[col]){
31635                     col = i;
31636                     continue;
31637                 }
31638                 
31639                 col = Math.min(col, i);
31640                 
31641             }
31642             
31643             x = pos.x + col * (this.colWidth + this.padWidth);
31644             
31645             y = maxY[col];
31646             
31647             var positions = [];
31648             
31649             switch (box.length){
31650                 case 1 :
31651                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31652                     break;
31653                 case 2 :
31654                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31655                     break;
31656                 case 3 :
31657                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31658                     break;
31659                 case 4 :
31660                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31661                     break;
31662                 default :
31663                     break;
31664             }
31665             
31666             Roo.each(box, function(b,kk){
31667                 
31668                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31669                 
31670                 var sz = b.el.getSize();
31671                 
31672                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31673                 
31674             }, this);
31675             
31676         }, this);
31677         
31678         var mY = 0;
31679         
31680         for (var i = 0; i < this.cols; i++){
31681             mY = Math.max(mY, maxY[i]);
31682         }
31683         
31684         this.el.setHeight(mY - pos.y);
31685         
31686     },
31687     
31688 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31689 //    {
31690 //        var pos = this.el.getBox(true);
31691 //        var x = pos.x;
31692 //        var y = pos.y;
31693 //        var maxX = pos.right;
31694 //        
31695 //        var maxHeight = 0;
31696 //        
31697 //        Roo.each(items, function(item, k){
31698 //            
31699 //            var c = k % 2;
31700 //            
31701 //            item.el.position('absolute');
31702 //                
31703 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31704 //
31705 //            item.el.setWidth(width);
31706 //
31707 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31708 //
31709 //            item.el.setHeight(height);
31710 //            
31711 //            if(c == 0){
31712 //                item.el.setXY([x, y], isInstant ? false : true);
31713 //            } else {
31714 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31715 //            }
31716 //            
31717 //            y = y + height + this.alternativePadWidth;
31718 //            
31719 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31720 //            
31721 //        }, this);
31722 //        
31723 //        this.el.setHeight(maxHeight);
31724 //        
31725 //    },
31726     
31727     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31728     {
31729         var pos = this.el.getBox(true);
31730         
31731         var minX = pos.x;
31732         var minY = pos.y;
31733         
31734         var maxX = pos.right;
31735         
31736         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31737         
31738         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31739         
31740         Roo.each(queue, function(box, k){
31741             
31742             Roo.each(box, function(b, kk){
31743                 
31744                 b.el.position('absolute');
31745                 
31746                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31747                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31748                 
31749                 if(b.size == 'md-left' || b.size == 'md-right'){
31750                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31751                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31752                 }
31753                 
31754                 b.el.setWidth(width);
31755                 b.el.setHeight(height);
31756                 
31757             }, this);
31758             
31759             if(!box.length){
31760                 return;
31761             }
31762             
31763             var positions = [];
31764             
31765             switch (box.length){
31766                 case 1 :
31767                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31768                     break;
31769                 case 2 :
31770                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31771                     break;
31772                 case 3 :
31773                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31774                     break;
31775                 case 4 :
31776                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31777                     break;
31778                 default :
31779                     break;
31780             }
31781             
31782             Roo.each(box, function(b,kk){
31783                 
31784                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31785                 
31786                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31787                 
31788             }, this);
31789             
31790         }, this);
31791         
31792     },
31793     
31794     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31795     {
31796         Roo.each(eItems, function(b,k){
31797             
31798             b.size = (k == 0) ? 'sm' : 'xs';
31799             b.x = (k == 0) ? 2 : 1;
31800             b.y = (k == 0) ? 2 : 1;
31801             
31802             b.el.position('absolute');
31803             
31804             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31805                 
31806             b.el.setWidth(width);
31807             
31808             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31809             
31810             b.el.setHeight(height);
31811             
31812         }, this);
31813
31814         var positions = [];
31815         
31816         positions.push({
31817             x : maxX - this.unitWidth * 2 - this.gutter,
31818             y : minY
31819         });
31820         
31821         positions.push({
31822             x : maxX - this.unitWidth,
31823             y : minY + (this.unitWidth + this.gutter) * 2
31824         });
31825         
31826         positions.push({
31827             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31828             y : minY
31829         });
31830         
31831         Roo.each(eItems, function(b,k){
31832             
31833             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31834
31835         }, this);
31836         
31837     },
31838     
31839     getVerticalOneBoxColPositions : function(x, y, box)
31840     {
31841         var pos = [];
31842         
31843         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31844         
31845         if(box[0].size == 'md-left'){
31846             rand = 0;
31847         }
31848         
31849         if(box[0].size == 'md-right'){
31850             rand = 1;
31851         }
31852         
31853         pos.push({
31854             x : x + (this.unitWidth + this.gutter) * rand,
31855             y : y
31856         });
31857         
31858         return pos;
31859     },
31860     
31861     getVerticalTwoBoxColPositions : function(x, y, box)
31862     {
31863         var pos = [];
31864         
31865         if(box[0].size == 'xs'){
31866             
31867             pos.push({
31868                 x : x,
31869                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31870             });
31871
31872             pos.push({
31873                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31874                 y : y
31875             });
31876             
31877             return pos;
31878             
31879         }
31880         
31881         pos.push({
31882             x : x,
31883             y : y
31884         });
31885
31886         pos.push({
31887             x : x + (this.unitWidth + this.gutter) * 2,
31888             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31889         });
31890         
31891         return pos;
31892         
31893     },
31894     
31895     getVerticalThreeBoxColPositions : function(x, y, box)
31896     {
31897         var pos = [];
31898         
31899         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31900             
31901             pos.push({
31902                 x : x,
31903                 y : y
31904             });
31905
31906             pos.push({
31907                 x : x + (this.unitWidth + this.gutter) * 1,
31908                 y : y
31909             });
31910             
31911             pos.push({
31912                 x : x + (this.unitWidth + this.gutter) * 2,
31913                 y : y
31914             });
31915             
31916             return pos;
31917             
31918         }
31919         
31920         if(box[0].size == 'xs' && box[1].size == 'xs'){
31921             
31922             pos.push({
31923                 x : x,
31924                 y : y
31925             });
31926
31927             pos.push({
31928                 x : x,
31929                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31930             });
31931             
31932             pos.push({
31933                 x : x + (this.unitWidth + this.gutter) * 1,
31934                 y : y
31935             });
31936             
31937             return pos;
31938             
31939         }
31940         
31941         pos.push({
31942             x : x,
31943             y : y
31944         });
31945
31946         pos.push({
31947             x : x + (this.unitWidth + this.gutter) * 2,
31948             y : y
31949         });
31950
31951         pos.push({
31952             x : x + (this.unitWidth + this.gutter) * 2,
31953             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31954         });
31955             
31956         return pos;
31957         
31958     },
31959     
31960     getVerticalFourBoxColPositions : function(x, y, box)
31961     {
31962         var pos = [];
31963         
31964         if(box[0].size == 'xs'){
31965             
31966             pos.push({
31967                 x : x,
31968                 y : y
31969             });
31970
31971             pos.push({
31972                 x : x,
31973                 y : y + (this.unitHeight + this.gutter) * 1
31974             });
31975             
31976             pos.push({
31977                 x : x,
31978                 y : y + (this.unitHeight + this.gutter) * 2
31979             });
31980             
31981             pos.push({
31982                 x : x + (this.unitWidth + this.gutter) * 1,
31983                 y : y
31984             });
31985             
31986             return pos;
31987             
31988         }
31989         
31990         pos.push({
31991             x : x,
31992             y : y
31993         });
31994
31995         pos.push({
31996             x : x + (this.unitWidth + this.gutter) * 2,
31997             y : y
31998         });
31999
32000         pos.push({
32001             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32002             y : y + (this.unitHeight + this.gutter) * 1
32003         });
32004
32005         pos.push({
32006             x : x + (this.unitWidth + this.gutter) * 2,
32007             y : y + (this.unitWidth + this.gutter) * 2
32008         });
32009
32010         return pos;
32011         
32012     },
32013     
32014     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32015     {
32016         var pos = [];
32017         
32018         if(box[0].size == 'md-left'){
32019             pos.push({
32020                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32021                 y : minY
32022             });
32023             
32024             return pos;
32025         }
32026         
32027         if(box[0].size == 'md-right'){
32028             pos.push({
32029                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32030                 y : minY + (this.unitWidth + this.gutter) * 1
32031             });
32032             
32033             return pos;
32034         }
32035         
32036         var rand = Math.floor(Math.random() * (4 - box[0].y));
32037         
32038         pos.push({
32039             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32040             y : minY + (this.unitWidth + this.gutter) * rand
32041         });
32042         
32043         return pos;
32044         
32045     },
32046     
32047     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32048     {
32049         var pos = [];
32050         
32051         if(box[0].size == 'xs'){
32052             
32053             pos.push({
32054                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32055                 y : minY
32056             });
32057
32058             pos.push({
32059                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32060                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32061             });
32062             
32063             return pos;
32064             
32065         }
32066         
32067         pos.push({
32068             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32069             y : minY
32070         });
32071
32072         pos.push({
32073             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32074             y : minY + (this.unitWidth + this.gutter) * 2
32075         });
32076         
32077         return pos;
32078         
32079     },
32080     
32081     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32082     {
32083         var pos = [];
32084         
32085         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32086             
32087             pos.push({
32088                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32089                 y : minY
32090             });
32091
32092             pos.push({
32093                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32094                 y : minY + (this.unitWidth + this.gutter) * 1
32095             });
32096             
32097             pos.push({
32098                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32099                 y : minY + (this.unitWidth + this.gutter) * 2
32100             });
32101             
32102             return pos;
32103             
32104         }
32105         
32106         if(box[0].size == 'xs' && box[1].size == 'xs'){
32107             
32108             pos.push({
32109                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32110                 y : minY
32111             });
32112
32113             pos.push({
32114                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32115                 y : minY
32116             });
32117             
32118             pos.push({
32119                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32120                 y : minY + (this.unitWidth + this.gutter) * 1
32121             });
32122             
32123             return pos;
32124             
32125         }
32126         
32127         pos.push({
32128             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32129             y : minY
32130         });
32131
32132         pos.push({
32133             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32134             y : minY + (this.unitWidth + this.gutter) * 2
32135         });
32136
32137         pos.push({
32138             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32139             y : minY + (this.unitWidth + this.gutter) * 2
32140         });
32141             
32142         return pos;
32143         
32144     },
32145     
32146     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32147     {
32148         var pos = [];
32149         
32150         if(box[0].size == 'xs'){
32151             
32152             pos.push({
32153                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32154                 y : minY
32155             });
32156
32157             pos.push({
32158                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32159                 y : minY
32160             });
32161             
32162             pos.push({
32163                 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),
32164                 y : minY
32165             });
32166             
32167             pos.push({
32168                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32169                 y : minY + (this.unitWidth + this.gutter) * 1
32170             });
32171             
32172             return pos;
32173             
32174         }
32175         
32176         pos.push({
32177             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32178             y : minY
32179         });
32180         
32181         pos.push({
32182             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32183             y : minY + (this.unitWidth + this.gutter) * 2
32184         });
32185         
32186         pos.push({
32187             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32188             y : minY + (this.unitWidth + this.gutter) * 2
32189         });
32190         
32191         pos.push({
32192             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),
32193             y : minY + (this.unitWidth + this.gutter) * 2
32194         });
32195
32196         return pos;
32197         
32198     },
32199     
32200     /**
32201     * remove a Masonry Brick
32202     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32203     */
32204     removeBrick : function(brick_id)
32205     {
32206         if (!brick_id) {
32207             return;
32208         }
32209         
32210         for (var i = 0; i<this.bricks.length; i++) {
32211             if (this.bricks[i].id == brick_id) {
32212                 this.bricks.splice(i,1);
32213                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32214                 this.initial();
32215             }
32216         }
32217     },
32218     
32219     /**
32220     * adds a Masonry Brick
32221     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32222     */
32223     addBrick : function(cfg)
32224     {
32225         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32226         //this.register(cn);
32227         cn.parentId = this.id;
32228         cn.render(this.el);
32229         return cn;
32230     },
32231     
32232     /**
32233     * register a Masonry Brick
32234     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32235     */
32236     
32237     register : function(brick)
32238     {
32239         this.bricks.push(brick);
32240         brick.masonryId = this.id;
32241     },
32242     
32243     /**
32244     * clear all the Masonry Brick
32245     */
32246     clearAll : function()
32247     {
32248         this.bricks = [];
32249         //this.getChildContainer().dom.innerHTML = "";
32250         this.el.dom.innerHTML = '';
32251     },
32252     
32253     getSelected : function()
32254     {
32255         if (!this.selectedBrick) {
32256             return false;
32257         }
32258         
32259         return this.selectedBrick;
32260     }
32261 });
32262
32263 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32264     
32265     groups: {},
32266      /**
32267     * register a Masonry Layout
32268     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32269     */
32270     
32271     register : function(layout)
32272     {
32273         this.groups[layout.id] = layout;
32274     },
32275     /**
32276     * fetch a  Masonry Layout based on the masonry layout ID
32277     * @param {string} the masonry layout to add
32278     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32279     */
32280     
32281     get: function(layout_id) {
32282         if (typeof(this.groups[layout_id]) == 'undefined') {
32283             return false;
32284         }
32285         return this.groups[layout_id] ;
32286     }
32287     
32288     
32289     
32290 });
32291
32292  
32293
32294  /**
32295  *
32296  * This is based on 
32297  * http://masonry.desandro.com
32298  *
32299  * The idea is to render all the bricks based on vertical width...
32300  *
32301  * The original code extends 'outlayer' - we might need to use that....
32302  * 
32303  */
32304
32305
32306 /**
32307  * @class Roo.bootstrap.LayoutMasonryAuto
32308  * @extends Roo.bootstrap.Component
32309  * Bootstrap Layout Masonry class
32310  * 
32311  * @constructor
32312  * Create a new Element
32313  * @param {Object} config The config object
32314  */
32315
32316 Roo.bootstrap.LayoutMasonryAuto = function(config){
32317     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32318 };
32319
32320 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32321     
32322       /**
32323      * @cfg {Boolean} isFitWidth  - resize the width..
32324      */   
32325     isFitWidth : false,  // options..
32326     /**
32327      * @cfg {Boolean} isOriginLeft = left align?
32328      */   
32329     isOriginLeft : true,
32330     /**
32331      * @cfg {Boolean} isOriginTop = top align?
32332      */   
32333     isOriginTop : false,
32334     /**
32335      * @cfg {Boolean} isLayoutInstant = no animation?
32336      */   
32337     isLayoutInstant : false, // needed?
32338     /**
32339      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32340      */   
32341     isResizingContainer : true,
32342     /**
32343      * @cfg {Number} columnWidth  width of the columns 
32344      */   
32345     
32346     columnWidth : 0,
32347     
32348     /**
32349      * @cfg {Number} maxCols maximum number of columns
32350      */   
32351     
32352     maxCols: 0,
32353     /**
32354      * @cfg {Number} padHeight padding below box..
32355      */   
32356     
32357     padHeight : 10, 
32358     
32359     /**
32360      * @cfg {Boolean} isAutoInitial defalut true
32361      */   
32362     
32363     isAutoInitial : true, 
32364     
32365     // private?
32366     gutter : 0,
32367     
32368     containerWidth: 0,
32369     initialColumnWidth : 0,
32370     currentSize : null,
32371     
32372     colYs : null, // array.
32373     maxY : 0,
32374     padWidth: 10,
32375     
32376     
32377     tag: 'div',
32378     cls: '',
32379     bricks: null, //CompositeElement
32380     cols : 0, // array?
32381     // element : null, // wrapped now this.el
32382     _isLayoutInited : null, 
32383     
32384     
32385     getAutoCreate : function(){
32386         
32387         var cfg = {
32388             tag: this.tag,
32389             cls: 'blog-masonary-wrapper ' + this.cls,
32390             cn : {
32391                 cls : 'mas-boxes masonary'
32392             }
32393         };
32394         
32395         return cfg;
32396     },
32397     
32398     getChildContainer: function( )
32399     {
32400         if (this.boxesEl) {
32401             return this.boxesEl;
32402         }
32403         
32404         this.boxesEl = this.el.select('.mas-boxes').first();
32405         
32406         return this.boxesEl;
32407     },
32408     
32409     
32410     initEvents : function()
32411     {
32412         var _this = this;
32413         
32414         if(this.isAutoInitial){
32415             Roo.log('hook children rendered');
32416             this.on('childrenrendered', function() {
32417                 Roo.log('children rendered');
32418                 _this.initial();
32419             } ,this);
32420         }
32421         
32422     },
32423     
32424     initial : function()
32425     {
32426         this.reloadItems();
32427
32428         this.currentSize = this.el.getBox(true);
32429
32430         /// was window resize... - let's see if this works..
32431         Roo.EventManager.onWindowResize(this.resize, this); 
32432
32433         if(!this.isAutoInitial){
32434             this.layout();
32435             return;
32436         }
32437         
32438         this.layout.defer(500,this);
32439     },
32440     
32441     reloadItems: function()
32442     {
32443         this.bricks = this.el.select('.masonry-brick', true);
32444         
32445         this.bricks.each(function(b) {
32446             //Roo.log(b.getSize());
32447             if (!b.attr('originalwidth')) {
32448                 b.attr('originalwidth',  b.getSize().width);
32449             }
32450             
32451         });
32452         
32453         Roo.log(this.bricks.elements.length);
32454     },
32455     
32456     resize : function()
32457     {
32458         Roo.log('resize');
32459         var cs = this.el.getBox(true);
32460         
32461         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32462             Roo.log("no change in with or X");
32463             return;
32464         }
32465         this.currentSize = cs;
32466         this.layout();
32467     },
32468     
32469     layout : function()
32470     {
32471          Roo.log('layout');
32472         this._resetLayout();
32473         //this._manageStamps();
32474       
32475         // don't animate first layout
32476         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32477         this.layoutItems( isInstant );
32478       
32479         // flag for initalized
32480         this._isLayoutInited = true;
32481     },
32482     
32483     layoutItems : function( isInstant )
32484     {
32485         //var items = this._getItemsForLayout( this.items );
32486         // original code supports filtering layout items.. we just ignore it..
32487         
32488         this._layoutItems( this.bricks , isInstant );
32489       
32490         this._postLayout();
32491     },
32492     _layoutItems : function ( items , isInstant)
32493     {
32494        //this.fireEvent( 'layout', this, items );
32495     
32496
32497         if ( !items || !items.elements.length ) {
32498           // no items, emit event with empty array
32499             return;
32500         }
32501
32502         var queue = [];
32503         items.each(function(item) {
32504             Roo.log("layout item");
32505             Roo.log(item);
32506             // get x/y object from method
32507             var position = this._getItemLayoutPosition( item );
32508             // enqueue
32509             position.item = item;
32510             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32511             queue.push( position );
32512         }, this);
32513       
32514         this._processLayoutQueue( queue );
32515     },
32516     /** Sets position of item in DOM
32517     * @param {Element} item
32518     * @param {Number} x - horizontal position
32519     * @param {Number} y - vertical position
32520     * @param {Boolean} isInstant - disables transitions
32521     */
32522     _processLayoutQueue : function( queue )
32523     {
32524         for ( var i=0, len = queue.length; i < len; i++ ) {
32525             var obj = queue[i];
32526             obj.item.position('absolute');
32527             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32528         }
32529     },
32530       
32531     
32532     /**
32533     * Any logic you want to do after each layout,
32534     * i.e. size the container
32535     */
32536     _postLayout : function()
32537     {
32538         this.resizeContainer();
32539     },
32540     
32541     resizeContainer : function()
32542     {
32543         if ( !this.isResizingContainer ) {
32544             return;
32545         }
32546         var size = this._getContainerSize();
32547         if ( size ) {
32548             this.el.setSize(size.width,size.height);
32549             this.boxesEl.setSize(size.width,size.height);
32550         }
32551     },
32552     
32553     
32554     
32555     _resetLayout : function()
32556     {
32557         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32558         this.colWidth = this.el.getWidth();
32559         //this.gutter = this.el.getWidth(); 
32560         
32561         this.measureColumns();
32562
32563         // reset column Y
32564         var i = this.cols;
32565         this.colYs = [];
32566         while (i--) {
32567             this.colYs.push( 0 );
32568         }
32569     
32570         this.maxY = 0;
32571     },
32572
32573     measureColumns : function()
32574     {
32575         this.getContainerWidth();
32576       // if columnWidth is 0, default to outerWidth of first item
32577         if ( !this.columnWidth ) {
32578             var firstItem = this.bricks.first();
32579             Roo.log(firstItem);
32580             this.columnWidth  = this.containerWidth;
32581             if (firstItem && firstItem.attr('originalwidth') ) {
32582                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32583             }
32584             // columnWidth fall back to item of first element
32585             Roo.log("set column width?");
32586                         this.initialColumnWidth = this.columnWidth  ;
32587
32588             // if first elem has no width, default to size of container
32589             
32590         }
32591         
32592         
32593         if (this.initialColumnWidth) {
32594             this.columnWidth = this.initialColumnWidth;
32595         }
32596         
32597         
32598             
32599         // column width is fixed at the top - however if container width get's smaller we should
32600         // reduce it...
32601         
32602         // this bit calcs how man columns..
32603             
32604         var columnWidth = this.columnWidth += this.gutter;
32605       
32606         // calculate columns
32607         var containerWidth = this.containerWidth + this.gutter;
32608         
32609         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32610         // fix rounding errors, typically with gutters
32611         var excess = columnWidth - containerWidth % columnWidth;
32612         
32613         
32614         // if overshoot is less than a pixel, round up, otherwise floor it
32615         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32616         cols = Math[ mathMethod ]( cols );
32617         this.cols = Math.max( cols, 1 );
32618         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32619         
32620          // padding positioning..
32621         var totalColWidth = this.cols * this.columnWidth;
32622         var padavail = this.containerWidth - totalColWidth;
32623         // so for 2 columns - we need 3 'pads'
32624         
32625         var padNeeded = (1+this.cols) * this.padWidth;
32626         
32627         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32628         
32629         this.columnWidth += padExtra
32630         //this.padWidth = Math.floor(padavail /  ( this.cols));
32631         
32632         // adjust colum width so that padding is fixed??
32633         
32634         // we have 3 columns ... total = width * 3
32635         // we have X left over... that should be used by 
32636         
32637         //if (this.expandC) {
32638             
32639         //}
32640         
32641         
32642         
32643     },
32644     
32645     getContainerWidth : function()
32646     {
32647        /* // container is parent if fit width
32648         var container = this.isFitWidth ? this.element.parentNode : this.element;
32649         // check that this.size and size are there
32650         // IE8 triggers resize on body size change, so they might not be
32651         
32652         var size = getSize( container );  //FIXME
32653         this.containerWidth = size && size.innerWidth; //FIXME
32654         */
32655          
32656         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32657         
32658     },
32659     
32660     _getItemLayoutPosition : function( item )  // what is item?
32661     {
32662         // we resize the item to our columnWidth..
32663       
32664         item.setWidth(this.columnWidth);
32665         item.autoBoxAdjust  = false;
32666         
32667         var sz = item.getSize();
32668  
32669         // how many columns does this brick span
32670         var remainder = this.containerWidth % this.columnWidth;
32671         
32672         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32673         // round if off by 1 pixel, otherwise use ceil
32674         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32675         colSpan = Math.min( colSpan, this.cols );
32676         
32677         // normally this should be '1' as we dont' currently allow multi width columns..
32678         
32679         var colGroup = this._getColGroup( colSpan );
32680         // get the minimum Y value from the columns
32681         var minimumY = Math.min.apply( Math, colGroup );
32682         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32683         
32684         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32685          
32686         // position the brick
32687         var position = {
32688             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32689             y: this.currentSize.y + minimumY + this.padHeight
32690         };
32691         
32692         Roo.log(position);
32693         // apply setHeight to necessary columns
32694         var setHeight = minimumY + sz.height + this.padHeight;
32695         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32696         
32697         var setSpan = this.cols + 1 - colGroup.length;
32698         for ( var i = 0; i < setSpan; i++ ) {
32699           this.colYs[ shortColIndex + i ] = setHeight ;
32700         }
32701       
32702         return position;
32703     },
32704     
32705     /**
32706      * @param {Number} colSpan - number of columns the element spans
32707      * @returns {Array} colGroup
32708      */
32709     _getColGroup : function( colSpan )
32710     {
32711         if ( colSpan < 2 ) {
32712           // if brick spans only one column, use all the column Ys
32713           return this.colYs;
32714         }
32715       
32716         var colGroup = [];
32717         // how many different places could this brick fit horizontally
32718         var groupCount = this.cols + 1 - colSpan;
32719         // for each group potential horizontal position
32720         for ( var i = 0; i < groupCount; i++ ) {
32721           // make an array of colY values for that one group
32722           var groupColYs = this.colYs.slice( i, i + colSpan );
32723           // and get the max value of the array
32724           colGroup[i] = Math.max.apply( Math, groupColYs );
32725         }
32726         return colGroup;
32727     },
32728     /*
32729     _manageStamp : function( stamp )
32730     {
32731         var stampSize =  stamp.getSize();
32732         var offset = stamp.getBox();
32733         // get the columns that this stamp affects
32734         var firstX = this.isOriginLeft ? offset.x : offset.right;
32735         var lastX = firstX + stampSize.width;
32736         var firstCol = Math.floor( firstX / this.columnWidth );
32737         firstCol = Math.max( 0, firstCol );
32738         
32739         var lastCol = Math.floor( lastX / this.columnWidth );
32740         // lastCol should not go over if multiple of columnWidth #425
32741         lastCol -= lastX % this.columnWidth ? 0 : 1;
32742         lastCol = Math.min( this.cols - 1, lastCol );
32743         
32744         // set colYs to bottom of the stamp
32745         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32746             stampSize.height;
32747             
32748         for ( var i = firstCol; i <= lastCol; i++ ) {
32749           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32750         }
32751     },
32752     */
32753     
32754     _getContainerSize : function()
32755     {
32756         this.maxY = Math.max.apply( Math, this.colYs );
32757         var size = {
32758             height: this.maxY
32759         };
32760       
32761         if ( this.isFitWidth ) {
32762             size.width = this._getContainerFitWidth();
32763         }
32764       
32765         return size;
32766     },
32767     
32768     _getContainerFitWidth : function()
32769     {
32770         var unusedCols = 0;
32771         // count unused columns
32772         var i = this.cols;
32773         while ( --i ) {
32774           if ( this.colYs[i] !== 0 ) {
32775             break;
32776           }
32777           unusedCols++;
32778         }
32779         // fit container to columns that have been used
32780         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32781     },
32782     
32783     needsResizeLayout : function()
32784     {
32785         var previousWidth = this.containerWidth;
32786         this.getContainerWidth();
32787         return previousWidth !== this.containerWidth;
32788     }
32789  
32790 });
32791
32792  
32793
32794  /*
32795  * - LGPL
32796  *
32797  * element
32798  * 
32799  */
32800
32801 /**
32802  * @class Roo.bootstrap.MasonryBrick
32803  * @extends Roo.bootstrap.Component
32804  * Bootstrap MasonryBrick class
32805  * 
32806  * @constructor
32807  * Create a new MasonryBrick
32808  * @param {Object} config The config object
32809  */
32810
32811 Roo.bootstrap.MasonryBrick = function(config){
32812     
32813     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32814     
32815     Roo.bootstrap.MasonryBrick.register(this);
32816     
32817     this.addEvents({
32818         // raw events
32819         /**
32820          * @event click
32821          * When a MasonryBrick is clcik
32822          * @param {Roo.bootstrap.MasonryBrick} this
32823          * @param {Roo.EventObject} e
32824          */
32825         "click" : true
32826     });
32827 };
32828
32829 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32830     
32831     /**
32832      * @cfg {String} title
32833      */   
32834     title : '',
32835     /**
32836      * @cfg {String} html
32837      */   
32838     html : '',
32839     /**
32840      * @cfg {String} bgimage
32841      */   
32842     bgimage : '',
32843     /**
32844      * @cfg {String} videourl
32845      */   
32846     videourl : '',
32847     /**
32848      * @cfg {String} cls
32849      */   
32850     cls : '',
32851     /**
32852      * @cfg {String} href
32853      */   
32854     href : '',
32855     /**
32856      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32857      */   
32858     size : 'xs',
32859     
32860     /**
32861      * @cfg {String} placetitle (center|bottom)
32862      */   
32863     placetitle : '',
32864     
32865     /**
32866      * @cfg {Boolean} isFitContainer defalut true
32867      */   
32868     isFitContainer : true, 
32869     
32870     /**
32871      * @cfg {Boolean} preventDefault defalut false
32872      */   
32873     preventDefault : false, 
32874     
32875     /**
32876      * @cfg {Boolean} inverse defalut false
32877      */   
32878     maskInverse : false, 
32879     
32880     getAutoCreate : function()
32881     {
32882         if(!this.isFitContainer){
32883             return this.getSplitAutoCreate();
32884         }
32885         
32886         var cls = 'masonry-brick masonry-brick-full';
32887         
32888         if(this.href.length){
32889             cls += ' masonry-brick-link';
32890         }
32891         
32892         if(this.bgimage.length){
32893             cls += ' masonry-brick-image';
32894         }
32895         
32896         if(this.maskInverse){
32897             cls += ' mask-inverse';
32898         }
32899         
32900         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32901             cls += ' enable-mask';
32902         }
32903         
32904         if(this.size){
32905             cls += ' masonry-' + this.size + '-brick';
32906         }
32907         
32908         if(this.placetitle.length){
32909             
32910             switch (this.placetitle) {
32911                 case 'center' :
32912                     cls += ' masonry-center-title';
32913                     break;
32914                 case 'bottom' :
32915                     cls += ' masonry-bottom-title';
32916                     break;
32917                 default:
32918                     break;
32919             }
32920             
32921         } else {
32922             if(!this.html.length && !this.bgimage.length){
32923                 cls += ' masonry-center-title';
32924             }
32925
32926             if(!this.html.length && this.bgimage.length){
32927                 cls += ' masonry-bottom-title';
32928             }
32929         }
32930         
32931         if(this.cls){
32932             cls += ' ' + this.cls;
32933         }
32934         
32935         var cfg = {
32936             tag: (this.href.length) ? 'a' : 'div',
32937             cls: cls,
32938             cn: [
32939                 {
32940                     tag: 'div',
32941                     cls: 'masonry-brick-mask'
32942                 },
32943                 {
32944                     tag: 'div',
32945                     cls: 'masonry-brick-paragraph',
32946                     cn: []
32947                 }
32948             ]
32949         };
32950         
32951         if(this.href.length){
32952             cfg.href = this.href;
32953         }
32954         
32955         var cn = cfg.cn[1].cn;
32956         
32957         if(this.title.length){
32958             cn.push({
32959                 tag: 'h4',
32960                 cls: 'masonry-brick-title',
32961                 html: this.title
32962             });
32963         }
32964         
32965         if(this.html.length){
32966             cn.push({
32967                 tag: 'p',
32968                 cls: 'masonry-brick-text',
32969                 html: this.html
32970             });
32971         }
32972         
32973         if (!this.title.length && !this.html.length) {
32974             cfg.cn[1].cls += ' hide';
32975         }
32976         
32977         if(this.bgimage.length){
32978             cfg.cn.push({
32979                 tag: 'img',
32980                 cls: 'masonry-brick-image-view',
32981                 src: this.bgimage
32982             });
32983         }
32984         
32985         if(this.videourl.length){
32986             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32987             // youtube support only?
32988             cfg.cn.push({
32989                 tag: 'iframe',
32990                 cls: 'masonry-brick-image-view',
32991                 src: vurl,
32992                 frameborder : 0,
32993                 allowfullscreen : true
32994             });
32995         }
32996         
32997         return cfg;
32998         
32999     },
33000     
33001     getSplitAutoCreate : function()
33002     {
33003         var cls = 'masonry-brick masonry-brick-split';
33004         
33005         if(this.href.length){
33006             cls += ' masonry-brick-link';
33007         }
33008         
33009         if(this.bgimage.length){
33010             cls += ' masonry-brick-image';
33011         }
33012         
33013         if(this.size){
33014             cls += ' masonry-' + this.size + '-brick';
33015         }
33016         
33017         switch (this.placetitle) {
33018             case 'center' :
33019                 cls += ' masonry-center-title';
33020                 break;
33021             case 'bottom' :
33022                 cls += ' masonry-bottom-title';
33023                 break;
33024             default:
33025                 if(!this.bgimage.length){
33026                     cls += ' masonry-center-title';
33027                 }
33028
33029                 if(this.bgimage.length){
33030                     cls += ' masonry-bottom-title';
33031                 }
33032                 break;
33033         }
33034         
33035         if(this.cls){
33036             cls += ' ' + this.cls;
33037         }
33038         
33039         var cfg = {
33040             tag: (this.href.length) ? 'a' : 'div',
33041             cls: cls,
33042             cn: [
33043                 {
33044                     tag: 'div',
33045                     cls: 'masonry-brick-split-head',
33046                     cn: [
33047                         {
33048                             tag: 'div',
33049                             cls: 'masonry-brick-paragraph',
33050                             cn: []
33051                         }
33052                     ]
33053                 },
33054                 {
33055                     tag: 'div',
33056                     cls: 'masonry-brick-split-body',
33057                     cn: []
33058                 }
33059             ]
33060         };
33061         
33062         if(this.href.length){
33063             cfg.href = this.href;
33064         }
33065         
33066         if(this.title.length){
33067             cfg.cn[0].cn[0].cn.push({
33068                 tag: 'h4',
33069                 cls: 'masonry-brick-title',
33070                 html: this.title
33071             });
33072         }
33073         
33074         if(this.html.length){
33075             cfg.cn[1].cn.push({
33076                 tag: 'p',
33077                 cls: 'masonry-brick-text',
33078                 html: this.html
33079             });
33080         }
33081
33082         if(this.bgimage.length){
33083             cfg.cn[0].cn.push({
33084                 tag: 'img',
33085                 cls: 'masonry-brick-image-view',
33086                 src: this.bgimage
33087             });
33088         }
33089         
33090         if(this.videourl.length){
33091             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33092             // youtube support only?
33093             cfg.cn[0].cn.cn.push({
33094                 tag: 'iframe',
33095                 cls: 'masonry-brick-image-view',
33096                 src: vurl,
33097                 frameborder : 0,
33098                 allowfullscreen : true
33099             });
33100         }
33101         
33102         return cfg;
33103     },
33104     
33105     initEvents: function() 
33106     {
33107         switch (this.size) {
33108             case 'xs' :
33109                 this.x = 1;
33110                 this.y = 1;
33111                 break;
33112             case 'sm' :
33113                 this.x = 2;
33114                 this.y = 2;
33115                 break;
33116             case 'md' :
33117             case 'md-left' :
33118             case 'md-right' :
33119                 this.x = 3;
33120                 this.y = 3;
33121                 break;
33122             case 'tall' :
33123                 this.x = 2;
33124                 this.y = 3;
33125                 break;
33126             case 'wide' :
33127                 this.x = 3;
33128                 this.y = 2;
33129                 break;
33130             case 'wide-thin' :
33131                 this.x = 3;
33132                 this.y = 1;
33133                 break;
33134                         
33135             default :
33136                 break;
33137         }
33138         
33139         if(Roo.isTouch){
33140             this.el.on('touchstart', this.onTouchStart, this);
33141             this.el.on('touchmove', this.onTouchMove, this);
33142             this.el.on('touchend', this.onTouchEnd, this);
33143             this.el.on('contextmenu', this.onContextMenu, this);
33144         } else {
33145             this.el.on('mouseenter'  ,this.enter, this);
33146             this.el.on('mouseleave', this.leave, this);
33147             this.el.on('click', this.onClick, this);
33148         }
33149         
33150         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33151             this.parent().bricks.push(this);   
33152         }
33153         
33154     },
33155     
33156     onClick: function(e, el)
33157     {
33158         var time = this.endTimer - this.startTimer;
33159         // Roo.log(e.preventDefault());
33160         if(Roo.isTouch){
33161             if(time > 1000){
33162                 e.preventDefault();
33163                 return;
33164             }
33165         }
33166         
33167         if(!this.preventDefault){
33168             return;
33169         }
33170         
33171         e.preventDefault();
33172         
33173         if (this.activeClass != '') {
33174             this.selectBrick();
33175         }
33176         
33177         this.fireEvent('click', this, e);
33178     },
33179     
33180     enter: function(e, el)
33181     {
33182         e.preventDefault();
33183         
33184         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33185             return;
33186         }
33187         
33188         if(this.bgimage.length && this.html.length){
33189             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33190         }
33191     },
33192     
33193     leave: function(e, el)
33194     {
33195         e.preventDefault();
33196         
33197         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33198             return;
33199         }
33200         
33201         if(this.bgimage.length && this.html.length){
33202             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33203         }
33204     },
33205     
33206     onTouchStart: function(e, el)
33207     {
33208 //        e.preventDefault();
33209         
33210         this.touchmoved = false;
33211         
33212         if(!this.isFitContainer){
33213             return;
33214         }
33215         
33216         if(!this.bgimage.length || !this.html.length){
33217             return;
33218         }
33219         
33220         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33221         
33222         this.timer = new Date().getTime();
33223         
33224     },
33225     
33226     onTouchMove: function(e, el)
33227     {
33228         this.touchmoved = true;
33229     },
33230     
33231     onContextMenu : function(e,el)
33232     {
33233         e.preventDefault();
33234         e.stopPropagation();
33235         return false;
33236     },
33237     
33238     onTouchEnd: function(e, el)
33239     {
33240 //        e.preventDefault();
33241         
33242         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33243         
33244             this.leave(e,el);
33245             
33246             return;
33247         }
33248         
33249         if(!this.bgimage.length || !this.html.length){
33250             
33251             if(this.href.length){
33252                 window.location.href = this.href;
33253             }
33254             
33255             return;
33256         }
33257         
33258         if(!this.isFitContainer){
33259             return;
33260         }
33261         
33262         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33263         
33264         window.location.href = this.href;
33265     },
33266     
33267     //selection on single brick only
33268     selectBrick : function() {
33269         
33270         if (!this.parentId) {
33271             return;
33272         }
33273         
33274         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33275         var index = m.selectedBrick.indexOf(this.id);
33276         
33277         if ( index > -1) {
33278             m.selectedBrick.splice(index,1);
33279             this.el.removeClass(this.activeClass);
33280             return;
33281         }
33282         
33283         for(var i = 0; i < m.selectedBrick.length; i++) {
33284             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33285             b.el.removeClass(b.activeClass);
33286         }
33287         
33288         m.selectedBrick = [];
33289         
33290         m.selectedBrick.push(this.id);
33291         this.el.addClass(this.activeClass);
33292         return;
33293     },
33294     
33295     isSelected : function(){
33296         return this.el.hasClass(this.activeClass);
33297         
33298     }
33299 });
33300
33301 Roo.apply(Roo.bootstrap.MasonryBrick, {
33302     
33303     //groups: {},
33304     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33305      /**
33306     * register a Masonry Brick
33307     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33308     */
33309     
33310     register : function(brick)
33311     {
33312         //this.groups[brick.id] = brick;
33313         this.groups.add(brick.id, brick);
33314     },
33315     /**
33316     * fetch a  masonry brick based on the masonry brick ID
33317     * @param {string} the masonry brick to add
33318     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33319     */
33320     
33321     get: function(brick_id) 
33322     {
33323         // if (typeof(this.groups[brick_id]) == 'undefined') {
33324         //     return false;
33325         // }
33326         // return this.groups[brick_id] ;
33327         
33328         if(this.groups.key(brick_id)) {
33329             return this.groups.key(brick_id);
33330         }
33331         
33332         return false;
33333     }
33334     
33335     
33336     
33337 });
33338
33339  /*
33340  * - LGPL
33341  *
33342  * element
33343  * 
33344  */
33345
33346 /**
33347  * @class Roo.bootstrap.Brick
33348  * @extends Roo.bootstrap.Component
33349  * Bootstrap Brick class
33350  * 
33351  * @constructor
33352  * Create a new Brick
33353  * @param {Object} config The config object
33354  */
33355
33356 Roo.bootstrap.Brick = function(config){
33357     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33358     
33359     this.addEvents({
33360         // raw events
33361         /**
33362          * @event click
33363          * When a Brick is click
33364          * @param {Roo.bootstrap.Brick} this
33365          * @param {Roo.EventObject} e
33366          */
33367         "click" : true
33368     });
33369 };
33370
33371 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33372     
33373     /**
33374      * @cfg {String} title
33375      */   
33376     title : '',
33377     /**
33378      * @cfg {String} html
33379      */   
33380     html : '',
33381     /**
33382      * @cfg {String} bgimage
33383      */   
33384     bgimage : '',
33385     /**
33386      * @cfg {String} cls
33387      */   
33388     cls : '',
33389     /**
33390      * @cfg {String} href
33391      */   
33392     href : '',
33393     /**
33394      * @cfg {String} video
33395      */   
33396     video : '',
33397     /**
33398      * @cfg {Boolean} square
33399      */   
33400     square : true,
33401     
33402     getAutoCreate : function()
33403     {
33404         var cls = 'roo-brick';
33405         
33406         if(this.href.length){
33407             cls += ' roo-brick-link';
33408         }
33409         
33410         if(this.bgimage.length){
33411             cls += ' roo-brick-image';
33412         }
33413         
33414         if(!this.html.length && !this.bgimage.length){
33415             cls += ' roo-brick-center-title';
33416         }
33417         
33418         if(!this.html.length && this.bgimage.length){
33419             cls += ' roo-brick-bottom-title';
33420         }
33421         
33422         if(this.cls){
33423             cls += ' ' + this.cls;
33424         }
33425         
33426         var cfg = {
33427             tag: (this.href.length) ? 'a' : 'div',
33428             cls: cls,
33429             cn: [
33430                 {
33431                     tag: 'div',
33432                     cls: 'roo-brick-paragraph',
33433                     cn: []
33434                 }
33435             ]
33436         };
33437         
33438         if(this.href.length){
33439             cfg.href = this.href;
33440         }
33441         
33442         var cn = cfg.cn[0].cn;
33443         
33444         if(this.title.length){
33445             cn.push({
33446                 tag: 'h4',
33447                 cls: 'roo-brick-title',
33448                 html: this.title
33449             });
33450         }
33451         
33452         if(this.html.length){
33453             cn.push({
33454                 tag: 'p',
33455                 cls: 'roo-brick-text',
33456                 html: this.html
33457             });
33458         } else {
33459             cn.cls += ' hide';
33460         }
33461         
33462         if(this.bgimage.length){
33463             cfg.cn.push({
33464                 tag: 'img',
33465                 cls: 'roo-brick-image-view',
33466                 src: this.bgimage
33467             });
33468         }
33469         
33470         return cfg;
33471     },
33472     
33473     initEvents: function() 
33474     {
33475         if(this.title.length || this.html.length){
33476             this.el.on('mouseenter'  ,this.enter, this);
33477             this.el.on('mouseleave', this.leave, this);
33478         }
33479         
33480         Roo.EventManager.onWindowResize(this.resize, this); 
33481         
33482         if(this.bgimage.length){
33483             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33484             this.imageEl.on('load', this.onImageLoad, this);
33485             return;
33486         }
33487         
33488         this.resize();
33489     },
33490     
33491     onImageLoad : function()
33492     {
33493         this.resize();
33494     },
33495     
33496     resize : function()
33497     {
33498         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33499         
33500         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33501         
33502         if(this.bgimage.length){
33503             var image = this.el.select('.roo-brick-image-view', true).first();
33504             
33505             image.setWidth(paragraph.getWidth());
33506             
33507             if(this.square){
33508                 image.setHeight(paragraph.getWidth());
33509             }
33510             
33511             this.el.setHeight(image.getHeight());
33512             paragraph.setHeight(image.getHeight());
33513             
33514         }
33515         
33516     },
33517     
33518     enter: function(e, el)
33519     {
33520         e.preventDefault();
33521         
33522         if(this.bgimage.length){
33523             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33524             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33525         }
33526     },
33527     
33528     leave: function(e, el)
33529     {
33530         e.preventDefault();
33531         
33532         if(this.bgimage.length){
33533             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33534             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33535         }
33536     }
33537     
33538 });
33539
33540  
33541
33542  /*
33543  * - LGPL
33544  *
33545  * Number field 
33546  */
33547
33548 /**
33549  * @class Roo.bootstrap.NumberField
33550  * @extends Roo.bootstrap.Input
33551  * Bootstrap NumberField class
33552  * 
33553  * 
33554  * 
33555  * 
33556  * @constructor
33557  * Create a new NumberField
33558  * @param {Object} config The config object
33559  */
33560
33561 Roo.bootstrap.NumberField = function(config){
33562     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33563 };
33564
33565 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33566     
33567     /**
33568      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33569      */
33570     allowDecimals : true,
33571     /**
33572      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33573      */
33574     decimalSeparator : ".",
33575     /**
33576      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33577      */
33578     decimalPrecision : 2,
33579     /**
33580      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33581      */
33582     allowNegative : true,
33583     
33584     /**
33585      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33586      */
33587     allowZero: true,
33588     /**
33589      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33590      */
33591     minValue : Number.NEGATIVE_INFINITY,
33592     /**
33593      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33594      */
33595     maxValue : Number.MAX_VALUE,
33596     /**
33597      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33598      */
33599     minText : "The minimum value for this field is {0}",
33600     /**
33601      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33602      */
33603     maxText : "The maximum value for this field is {0}",
33604     /**
33605      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33606      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33607      */
33608     nanText : "{0} is not a valid number",
33609     /**
33610      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33611      */
33612     thousandsDelimiter : false,
33613     /**
33614      * @cfg {String} valueAlign alignment of value
33615      */
33616     valueAlign : "left",
33617
33618     getAutoCreate : function()
33619     {
33620         var hiddenInput = {
33621             tag: 'input',
33622             type: 'hidden',
33623             id: Roo.id(),
33624             cls: 'hidden-number-input'
33625         };
33626         
33627         if (this.name) {
33628             hiddenInput.name = this.name;
33629         }
33630         
33631         this.name = '';
33632         
33633         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33634         
33635         this.name = hiddenInput.name;
33636         
33637         if(cfg.cn.length > 0) {
33638             cfg.cn.push(hiddenInput);
33639         }
33640         
33641         return cfg;
33642     },
33643
33644     // private
33645     initEvents : function()
33646     {   
33647         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33648         
33649         var allowed = "0123456789";
33650         
33651         if(this.allowDecimals){
33652             allowed += this.decimalSeparator;
33653         }
33654         
33655         if(this.allowNegative){
33656             allowed += "-";
33657         }
33658         
33659         if(this.thousandsDelimiter) {
33660             allowed += ",";
33661         }
33662         
33663         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33664         
33665         var keyPress = function(e){
33666             
33667             var k = e.getKey();
33668             
33669             var c = e.getCharCode();
33670             
33671             if(
33672                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33673                     allowed.indexOf(String.fromCharCode(c)) === -1
33674             ){
33675                 e.stopEvent();
33676                 return;
33677             }
33678             
33679             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33680                 return;
33681             }
33682             
33683             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33684                 e.stopEvent();
33685             }
33686         };
33687         
33688         this.el.on("keypress", keyPress, this);
33689     },
33690     
33691     validateValue : function(value)
33692     {
33693         
33694         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33695             return false;
33696         }
33697         
33698         var num = this.parseValue(value);
33699         
33700         if(isNaN(num)){
33701             this.markInvalid(String.format(this.nanText, value));
33702             return false;
33703         }
33704         
33705         if(num < this.minValue){
33706             this.markInvalid(String.format(this.minText, this.minValue));
33707             return false;
33708         }
33709         
33710         if(num > this.maxValue){
33711             this.markInvalid(String.format(this.maxText, this.maxValue));
33712             return false;
33713         }
33714         
33715         return true;
33716     },
33717
33718     getValue : function()
33719     {
33720         var v = this.hiddenEl().getValue();
33721         
33722         return this.fixPrecision(this.parseValue(v));
33723     },
33724
33725     parseValue : function(value)
33726     {
33727         if(this.thousandsDelimiter) {
33728             value += "";
33729             r = new RegExp(",", "g");
33730             value = value.replace(r, "");
33731         }
33732         
33733         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33734         return isNaN(value) ? '' : value;
33735     },
33736
33737     fixPrecision : function(value)
33738     {
33739         if(this.thousandsDelimiter) {
33740             value += "";
33741             r = new RegExp(",", "g");
33742             value = value.replace(r, "");
33743         }
33744         
33745         var nan = isNaN(value);
33746         
33747         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33748             return nan ? '' : value;
33749         }
33750         return parseFloat(value).toFixed(this.decimalPrecision);
33751     },
33752
33753     setValue : function(v)
33754     {
33755         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33756         
33757         this.value = v;
33758         
33759         if(this.rendered){
33760             
33761             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33762             
33763             this.inputEl().dom.value = (v == '') ? '' :
33764                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33765             
33766             if(!this.allowZero && v === '0') {
33767                 this.hiddenEl().dom.value = '';
33768                 this.inputEl().dom.value = '';
33769             }
33770             
33771             this.validate();
33772         }
33773     },
33774
33775     decimalPrecisionFcn : function(v)
33776     {
33777         return Math.floor(v);
33778     },
33779
33780     beforeBlur : function()
33781     {
33782         var v = this.parseValue(this.getRawValue());
33783         
33784         if(v || v === 0 || v === ''){
33785             this.setValue(v);
33786         }
33787     },
33788     
33789     hiddenEl : function()
33790     {
33791         return this.el.select('input.hidden-number-input',true).first();
33792     }
33793     
33794 });
33795
33796  
33797
33798 /*
33799 * Licence: LGPL
33800 */
33801
33802 /**
33803  * @class Roo.bootstrap.DocumentSlider
33804  * @extends Roo.bootstrap.Component
33805  * Bootstrap DocumentSlider class
33806  * 
33807  * @constructor
33808  * Create a new DocumentViewer
33809  * @param {Object} config The config object
33810  */
33811
33812 Roo.bootstrap.DocumentSlider = function(config){
33813     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33814     
33815     this.files = [];
33816     
33817     this.addEvents({
33818         /**
33819          * @event initial
33820          * Fire after initEvent
33821          * @param {Roo.bootstrap.DocumentSlider} this
33822          */
33823         "initial" : true,
33824         /**
33825          * @event update
33826          * Fire after update
33827          * @param {Roo.bootstrap.DocumentSlider} this
33828          */
33829         "update" : true,
33830         /**
33831          * @event click
33832          * Fire after click
33833          * @param {Roo.bootstrap.DocumentSlider} this
33834          */
33835         "click" : true
33836     });
33837 };
33838
33839 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33840     
33841     files : false,
33842     
33843     indicator : 0,
33844     
33845     getAutoCreate : function()
33846     {
33847         var cfg = {
33848             tag : 'div',
33849             cls : 'roo-document-slider',
33850             cn : [
33851                 {
33852                     tag : 'div',
33853                     cls : 'roo-document-slider-header',
33854                     cn : [
33855                         {
33856                             tag : 'div',
33857                             cls : 'roo-document-slider-header-title'
33858                         }
33859                     ]
33860                 },
33861                 {
33862                     tag : 'div',
33863                     cls : 'roo-document-slider-body',
33864                     cn : [
33865                         {
33866                             tag : 'div',
33867                             cls : 'roo-document-slider-prev',
33868                             cn : [
33869                                 {
33870                                     tag : 'i',
33871                                     cls : 'fa fa-chevron-left'
33872                                 }
33873                             ]
33874                         },
33875                         {
33876                             tag : 'div',
33877                             cls : 'roo-document-slider-thumb',
33878                             cn : [
33879                                 {
33880                                     tag : 'img',
33881                                     cls : 'roo-document-slider-image'
33882                                 }
33883                             ]
33884                         },
33885                         {
33886                             tag : 'div',
33887                             cls : 'roo-document-slider-next',
33888                             cn : [
33889                                 {
33890                                     tag : 'i',
33891                                     cls : 'fa fa-chevron-right'
33892                                 }
33893                             ]
33894                         }
33895                     ]
33896                 }
33897             ]
33898         };
33899         
33900         return cfg;
33901     },
33902     
33903     initEvents : function()
33904     {
33905         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33906         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33907         
33908         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33909         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33910         
33911         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33912         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33913         
33914         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33915         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33916         
33917         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33918         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33919         
33920         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33921         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33922         
33923         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33924         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33925         
33926         this.thumbEl.on('click', this.onClick, this);
33927         
33928         this.prevIndicator.on('click', this.prev, this);
33929         
33930         this.nextIndicator.on('click', this.next, this);
33931         
33932     },
33933     
33934     initial : function()
33935     {
33936         if(this.files.length){
33937             this.indicator = 1;
33938             this.update()
33939         }
33940         
33941         this.fireEvent('initial', this);
33942     },
33943     
33944     update : function()
33945     {
33946         this.imageEl.attr('src', this.files[this.indicator - 1]);
33947         
33948         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33949         
33950         this.prevIndicator.show();
33951         
33952         if(this.indicator == 1){
33953             this.prevIndicator.hide();
33954         }
33955         
33956         this.nextIndicator.show();
33957         
33958         if(this.indicator == this.files.length){
33959             this.nextIndicator.hide();
33960         }
33961         
33962         this.thumbEl.scrollTo('top');
33963         
33964         this.fireEvent('update', this);
33965     },
33966     
33967     onClick : function(e)
33968     {
33969         e.preventDefault();
33970         
33971         this.fireEvent('click', this);
33972     },
33973     
33974     prev : function(e)
33975     {
33976         e.preventDefault();
33977         
33978         this.indicator = Math.max(1, this.indicator - 1);
33979         
33980         this.update();
33981     },
33982     
33983     next : function(e)
33984     {
33985         e.preventDefault();
33986         
33987         this.indicator = Math.min(this.files.length, this.indicator + 1);
33988         
33989         this.update();
33990     }
33991 });
33992 /*
33993  * - LGPL
33994  *
33995  * RadioSet
33996  *
33997  *
33998  */
33999
34000 /**
34001  * @class Roo.bootstrap.RadioSet
34002  * @extends Roo.bootstrap.Input
34003  * Bootstrap RadioSet class
34004  * @cfg {String} indicatorpos (left|right) default left
34005  * @cfg {Boolean} inline (true|false) inline the element (default true)
34006  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34007  * @constructor
34008  * Create a new RadioSet
34009  * @param {Object} config The config object
34010  */
34011
34012 Roo.bootstrap.RadioSet = function(config){
34013     
34014     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34015     
34016     this.radioes = [];
34017     
34018     Roo.bootstrap.RadioSet.register(this);
34019     
34020     this.addEvents({
34021         /**
34022         * @event check
34023         * Fires when the element is checked or unchecked.
34024         * @param {Roo.bootstrap.RadioSet} this This radio
34025         * @param {Roo.bootstrap.Radio} item The checked item
34026         */
34027        check : true,
34028        /**
34029         * @event click
34030         * Fires when the element is click.
34031         * @param {Roo.bootstrap.RadioSet} this This radio set
34032         * @param {Roo.bootstrap.Radio} item The checked item
34033         * @param {Roo.EventObject} e The event object
34034         */
34035        click : true
34036     });
34037     
34038 };
34039
34040 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34041
34042     radioes : false,
34043     
34044     inline : true,
34045     
34046     weight : '',
34047     
34048     indicatorpos : 'left',
34049     
34050     getAutoCreate : function()
34051     {
34052         var label = {
34053             tag : 'label',
34054             cls : 'roo-radio-set-label',
34055             cn : [
34056                 {
34057                     tag : 'span',
34058                     html : this.fieldLabel
34059                 }
34060             ]
34061         };
34062         if (Roo.bootstrap.version == 3) {
34063             
34064             
34065             if(this.indicatorpos == 'left'){
34066                 label.cn.unshift({
34067                     tag : 'i',
34068                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34069                     tooltip : 'This field is required'
34070                 });
34071             } else {
34072                 label.cn.push({
34073                     tag : 'i',
34074                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34075                     tooltip : 'This field is required'
34076                 });
34077             }
34078         }
34079         var items = {
34080             tag : 'div',
34081             cls : 'roo-radio-set-items'
34082         };
34083         
34084         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34085         
34086         if (align === 'left' && this.fieldLabel.length) {
34087             
34088             items = {
34089                 cls : "roo-radio-set-right", 
34090                 cn: [
34091                     items
34092                 ]
34093             };
34094             
34095             if(this.labelWidth > 12){
34096                 label.style = "width: " + this.labelWidth + 'px';
34097             }
34098             
34099             if(this.labelWidth < 13 && this.labelmd == 0){
34100                 this.labelmd = this.labelWidth;
34101             }
34102             
34103             if(this.labellg > 0){
34104                 label.cls += ' col-lg-' + this.labellg;
34105                 items.cls += ' col-lg-' + (12 - this.labellg);
34106             }
34107             
34108             if(this.labelmd > 0){
34109                 label.cls += ' col-md-' + this.labelmd;
34110                 items.cls += ' col-md-' + (12 - this.labelmd);
34111             }
34112             
34113             if(this.labelsm > 0){
34114                 label.cls += ' col-sm-' + this.labelsm;
34115                 items.cls += ' col-sm-' + (12 - this.labelsm);
34116             }
34117             
34118             if(this.labelxs > 0){
34119                 label.cls += ' col-xs-' + this.labelxs;
34120                 items.cls += ' col-xs-' + (12 - this.labelxs);
34121             }
34122         }
34123         
34124         var cfg = {
34125             tag : 'div',
34126             cls : 'roo-radio-set',
34127             cn : [
34128                 {
34129                     tag : 'input',
34130                     cls : 'roo-radio-set-input',
34131                     type : 'hidden',
34132                     name : this.name,
34133                     value : this.value ? this.value :  ''
34134                 },
34135                 label,
34136                 items
34137             ]
34138         };
34139         
34140         if(this.weight.length){
34141             cfg.cls += ' roo-radio-' + this.weight;
34142         }
34143         
34144         if(this.inline) {
34145             cfg.cls += ' roo-radio-set-inline';
34146         }
34147         
34148         var settings=this;
34149         ['xs','sm','md','lg'].map(function(size){
34150             if (settings[size]) {
34151                 cfg.cls += ' col-' + size + '-' + settings[size];
34152             }
34153         });
34154         
34155         return cfg;
34156         
34157     },
34158
34159     initEvents : function()
34160     {
34161         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34162         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34163         
34164         if(!this.fieldLabel.length){
34165             this.labelEl.hide();
34166         }
34167         
34168         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34169         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34170         
34171         this.indicator = this.indicatorEl();
34172         
34173         if(this.indicator){
34174             this.indicator.addClass('invisible');
34175         }
34176         
34177         this.originalValue = this.getValue();
34178         
34179     },
34180     
34181     inputEl: function ()
34182     {
34183         return this.el.select('.roo-radio-set-input', true).first();
34184     },
34185     
34186     getChildContainer : function()
34187     {
34188         return this.itemsEl;
34189     },
34190     
34191     register : function(item)
34192     {
34193         this.radioes.push(item);
34194         
34195     },
34196     
34197     validate : function()
34198     {   
34199         if(this.getVisibilityEl().hasClass('hidden')){
34200             return true;
34201         }
34202         
34203         var valid = false;
34204         
34205         Roo.each(this.radioes, function(i){
34206             if(!i.checked){
34207                 return;
34208             }
34209             
34210             valid = true;
34211             return false;
34212         });
34213         
34214         if(this.allowBlank) {
34215             return true;
34216         }
34217         
34218         if(this.disabled || valid){
34219             this.markValid();
34220             return true;
34221         }
34222         
34223         this.markInvalid();
34224         return false;
34225         
34226     },
34227     
34228     markValid : function()
34229     {
34230         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34231             this.indicatorEl().removeClass('visible');
34232             this.indicatorEl().addClass('invisible');
34233         }
34234         
34235         this.el.removeClass([this.invalidClass, this.validClass]);
34236         this.el.addClass(this.validClass);
34237         if (Roo.bootstrap.version == 3) {
34238             this.el.removeClass(['is-invalid','is-valid']);
34239             this.el.addClass(['is-valid']);
34240         }
34241         this.fireEvent('valid', this);
34242     },
34243     
34244     markInvalid : function(msg)
34245     {
34246         if(this.allowBlank || this.disabled){
34247             return;
34248         }
34249         
34250         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34251             this.indicatorEl().removeClass('invisible');
34252             this.indicatorEl().addClass('visible');
34253         }
34254         if (Roo.bootstrap.version == 3) {
34255             this.el.removeClass([this.invalidClass, this.validClass]);
34256             this.el.addClass(this.invalidClass);
34257         }
34258         this.el.removeClass(['is-invalid','is-valid']);
34259         this.el.addClass(['is-invalid']);
34260         
34261         this.fireEvent('invalid', this, msg);
34262         
34263     },
34264     
34265     setValue : function(v, suppressEvent)
34266     {   
34267         if(this.value === v){
34268             return;
34269         }
34270         
34271         this.value = v;
34272         
34273         if(this.rendered){
34274             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34275         }
34276         
34277         Roo.each(this.radioes, function(i){
34278             i.checked = false;
34279             i.el.removeClass('checked');
34280         });
34281         
34282         Roo.each(this.radioes, function(i){
34283             
34284             if(i.value === v || i.value.toString() === v.toString()){
34285                 i.checked = true;
34286                 i.el.addClass('checked');
34287                 
34288                 if(suppressEvent !== true){
34289                     this.fireEvent('check', this, i);
34290                 }
34291                 
34292                 return false;
34293             }
34294             
34295         }, this);
34296         
34297         this.validate();
34298     },
34299     
34300     clearInvalid : function(){
34301         
34302         if(!this.el || this.preventMark){
34303             return;
34304         }
34305         
34306         this.el.removeClass([this.invalidClass]);
34307         
34308         this.fireEvent('valid', this);
34309     }
34310     
34311 });
34312
34313 Roo.apply(Roo.bootstrap.RadioSet, {
34314     
34315     groups: {},
34316     
34317     register : function(set)
34318     {
34319         this.groups[set.name] = set;
34320     },
34321     
34322     get: function(name) 
34323     {
34324         if (typeof(this.groups[name]) == 'undefined') {
34325             return false;
34326         }
34327         
34328         return this.groups[name] ;
34329     }
34330     
34331 });
34332 /*
34333  * Based on:
34334  * Ext JS Library 1.1.1
34335  * Copyright(c) 2006-2007, Ext JS, LLC.
34336  *
34337  * Originally Released Under LGPL - original licence link has changed is not relivant.
34338  *
34339  * Fork - LGPL
34340  * <script type="text/javascript">
34341  */
34342
34343
34344 /**
34345  * @class Roo.bootstrap.SplitBar
34346  * @extends Roo.util.Observable
34347  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34348  * <br><br>
34349  * Usage:
34350  * <pre><code>
34351 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34352                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34353 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34354 split.minSize = 100;
34355 split.maxSize = 600;
34356 split.animate = true;
34357 split.on('moved', splitterMoved);
34358 </code></pre>
34359  * @constructor
34360  * Create a new SplitBar
34361  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34362  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34363  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34364  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34365                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34366                         position of the SplitBar).
34367  */
34368 Roo.bootstrap.SplitBar = function(cfg){
34369     
34370     /** @private */
34371     
34372     //{
34373     //  dragElement : elm
34374     //  resizingElement: el,
34375         // optional..
34376     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34377     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34378         // existingProxy ???
34379     //}
34380     
34381     this.el = Roo.get(cfg.dragElement, true);
34382     this.el.dom.unselectable = "on";
34383     /** @private */
34384     this.resizingEl = Roo.get(cfg.resizingElement, true);
34385
34386     /**
34387      * @private
34388      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34389      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34390      * @type Number
34391      */
34392     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34393     
34394     /**
34395      * The minimum size of the resizing element. (Defaults to 0)
34396      * @type Number
34397      */
34398     this.minSize = 0;
34399     
34400     /**
34401      * The maximum size of the resizing element. (Defaults to 2000)
34402      * @type Number
34403      */
34404     this.maxSize = 2000;
34405     
34406     /**
34407      * Whether to animate the transition to the new size
34408      * @type Boolean
34409      */
34410     this.animate = false;
34411     
34412     /**
34413      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34414      * @type Boolean
34415      */
34416     this.useShim = false;
34417     
34418     /** @private */
34419     this.shim = null;
34420     
34421     if(!cfg.existingProxy){
34422         /** @private */
34423         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34424     }else{
34425         this.proxy = Roo.get(cfg.existingProxy).dom;
34426     }
34427     /** @private */
34428     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34429     
34430     /** @private */
34431     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34432     
34433     /** @private */
34434     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34435     
34436     /** @private */
34437     this.dragSpecs = {};
34438     
34439     /**
34440      * @private The adapter to use to positon and resize elements
34441      */
34442     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34443     this.adapter.init(this);
34444     
34445     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34446         /** @private */
34447         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34448         this.el.addClass("roo-splitbar-h");
34449     }else{
34450         /** @private */
34451         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34452         this.el.addClass("roo-splitbar-v");
34453     }
34454     
34455     this.addEvents({
34456         /**
34457          * @event resize
34458          * Fires when the splitter is moved (alias for {@link #event-moved})
34459          * @param {Roo.bootstrap.SplitBar} this
34460          * @param {Number} newSize the new width or height
34461          */
34462         "resize" : true,
34463         /**
34464          * @event moved
34465          * Fires when the splitter is moved
34466          * @param {Roo.bootstrap.SplitBar} this
34467          * @param {Number} newSize the new width or height
34468          */
34469         "moved" : true,
34470         /**
34471          * @event beforeresize
34472          * Fires before the splitter is dragged
34473          * @param {Roo.bootstrap.SplitBar} this
34474          */
34475         "beforeresize" : true,
34476
34477         "beforeapply" : true
34478     });
34479
34480     Roo.util.Observable.call(this);
34481 };
34482
34483 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34484     onStartProxyDrag : function(x, y){
34485         this.fireEvent("beforeresize", this);
34486         if(!this.overlay){
34487             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34488             o.unselectable();
34489             o.enableDisplayMode("block");
34490             // all splitbars share the same overlay
34491             Roo.bootstrap.SplitBar.prototype.overlay = o;
34492         }
34493         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34494         this.overlay.show();
34495         Roo.get(this.proxy).setDisplayed("block");
34496         var size = this.adapter.getElementSize(this);
34497         this.activeMinSize = this.getMinimumSize();;
34498         this.activeMaxSize = this.getMaximumSize();;
34499         var c1 = size - this.activeMinSize;
34500         var c2 = Math.max(this.activeMaxSize - size, 0);
34501         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34502             this.dd.resetConstraints();
34503             this.dd.setXConstraint(
34504                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34505                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34506             );
34507             this.dd.setYConstraint(0, 0);
34508         }else{
34509             this.dd.resetConstraints();
34510             this.dd.setXConstraint(0, 0);
34511             this.dd.setYConstraint(
34512                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34513                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34514             );
34515          }
34516         this.dragSpecs.startSize = size;
34517         this.dragSpecs.startPoint = [x, y];
34518         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34519     },
34520     
34521     /** 
34522      * @private Called after the drag operation by the DDProxy
34523      */
34524     onEndProxyDrag : function(e){
34525         Roo.get(this.proxy).setDisplayed(false);
34526         var endPoint = Roo.lib.Event.getXY(e);
34527         if(this.overlay){
34528             this.overlay.hide();
34529         }
34530         var newSize;
34531         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34532             newSize = this.dragSpecs.startSize + 
34533                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34534                     endPoint[0] - this.dragSpecs.startPoint[0] :
34535                     this.dragSpecs.startPoint[0] - endPoint[0]
34536                 );
34537         }else{
34538             newSize = this.dragSpecs.startSize + 
34539                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34540                     endPoint[1] - this.dragSpecs.startPoint[1] :
34541                     this.dragSpecs.startPoint[1] - endPoint[1]
34542                 );
34543         }
34544         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34545         if(newSize != this.dragSpecs.startSize){
34546             if(this.fireEvent('beforeapply', this, newSize) !== false){
34547                 this.adapter.setElementSize(this, newSize);
34548                 this.fireEvent("moved", this, newSize);
34549                 this.fireEvent("resize", this, newSize);
34550             }
34551         }
34552     },
34553     
34554     /**
34555      * Get the adapter this SplitBar uses
34556      * @return The adapter object
34557      */
34558     getAdapter : function(){
34559         return this.adapter;
34560     },
34561     
34562     /**
34563      * Set the adapter this SplitBar uses
34564      * @param {Object} adapter A SplitBar adapter object
34565      */
34566     setAdapter : function(adapter){
34567         this.adapter = adapter;
34568         this.adapter.init(this);
34569     },
34570     
34571     /**
34572      * Gets the minimum size for the resizing element
34573      * @return {Number} The minimum size
34574      */
34575     getMinimumSize : function(){
34576         return this.minSize;
34577     },
34578     
34579     /**
34580      * Sets the minimum size for the resizing element
34581      * @param {Number} minSize The minimum size
34582      */
34583     setMinimumSize : function(minSize){
34584         this.minSize = minSize;
34585     },
34586     
34587     /**
34588      * Gets the maximum size for the resizing element
34589      * @return {Number} The maximum size
34590      */
34591     getMaximumSize : function(){
34592         return this.maxSize;
34593     },
34594     
34595     /**
34596      * Sets the maximum size for the resizing element
34597      * @param {Number} maxSize The maximum size
34598      */
34599     setMaximumSize : function(maxSize){
34600         this.maxSize = maxSize;
34601     },
34602     
34603     /**
34604      * Sets the initialize size for the resizing element
34605      * @param {Number} size The initial size
34606      */
34607     setCurrentSize : function(size){
34608         var oldAnimate = this.animate;
34609         this.animate = false;
34610         this.adapter.setElementSize(this, size);
34611         this.animate = oldAnimate;
34612     },
34613     
34614     /**
34615      * Destroy this splitbar. 
34616      * @param {Boolean} removeEl True to remove the element
34617      */
34618     destroy : function(removeEl){
34619         if(this.shim){
34620             this.shim.remove();
34621         }
34622         this.dd.unreg();
34623         this.proxy.parentNode.removeChild(this.proxy);
34624         if(removeEl){
34625             this.el.remove();
34626         }
34627     }
34628 });
34629
34630 /**
34631  * @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.
34632  */
34633 Roo.bootstrap.SplitBar.createProxy = function(dir){
34634     var proxy = new Roo.Element(document.createElement("div"));
34635     proxy.unselectable();
34636     var cls = 'roo-splitbar-proxy';
34637     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34638     document.body.appendChild(proxy.dom);
34639     return proxy.dom;
34640 };
34641
34642 /** 
34643  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34644  * Default Adapter. It assumes the splitter and resizing element are not positioned
34645  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34646  */
34647 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34648 };
34649
34650 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34651     // do nothing for now
34652     init : function(s){
34653     
34654     },
34655     /**
34656      * Called before drag operations to get the current size of the resizing element. 
34657      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34658      */
34659      getElementSize : function(s){
34660         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34661             return s.resizingEl.getWidth();
34662         }else{
34663             return s.resizingEl.getHeight();
34664         }
34665     },
34666     
34667     /**
34668      * Called after drag operations to set the size of the resizing element.
34669      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34670      * @param {Number} newSize The new size to set
34671      * @param {Function} onComplete A function to be invoked when resizing is complete
34672      */
34673     setElementSize : function(s, newSize, onComplete){
34674         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34675             if(!s.animate){
34676                 s.resizingEl.setWidth(newSize);
34677                 if(onComplete){
34678                     onComplete(s, newSize);
34679                 }
34680             }else{
34681                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34682             }
34683         }else{
34684             
34685             if(!s.animate){
34686                 s.resizingEl.setHeight(newSize);
34687                 if(onComplete){
34688                     onComplete(s, newSize);
34689                 }
34690             }else{
34691                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34692             }
34693         }
34694     }
34695 };
34696
34697 /** 
34698  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34699  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34700  * Adapter that  moves the splitter element to align with the resized sizing element. 
34701  * Used with an absolute positioned SplitBar.
34702  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34703  * document.body, make sure you assign an id to the body element.
34704  */
34705 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34706     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34707     this.container = Roo.get(container);
34708 };
34709
34710 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34711     init : function(s){
34712         this.basic.init(s);
34713     },
34714     
34715     getElementSize : function(s){
34716         return this.basic.getElementSize(s);
34717     },
34718     
34719     setElementSize : function(s, newSize, onComplete){
34720         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34721     },
34722     
34723     moveSplitter : function(s){
34724         var yes = Roo.bootstrap.SplitBar;
34725         switch(s.placement){
34726             case yes.LEFT:
34727                 s.el.setX(s.resizingEl.getRight());
34728                 break;
34729             case yes.RIGHT:
34730                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34731                 break;
34732             case yes.TOP:
34733                 s.el.setY(s.resizingEl.getBottom());
34734                 break;
34735             case yes.BOTTOM:
34736                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34737                 break;
34738         }
34739     }
34740 };
34741
34742 /**
34743  * Orientation constant - Create a vertical SplitBar
34744  * @static
34745  * @type Number
34746  */
34747 Roo.bootstrap.SplitBar.VERTICAL = 1;
34748
34749 /**
34750  * Orientation constant - Create a horizontal SplitBar
34751  * @static
34752  * @type Number
34753  */
34754 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34755
34756 /**
34757  * Placement constant - The resizing element is to the left of the splitter element
34758  * @static
34759  * @type Number
34760  */
34761 Roo.bootstrap.SplitBar.LEFT = 1;
34762
34763 /**
34764  * Placement constant - The resizing element is to the right of the splitter element
34765  * @static
34766  * @type Number
34767  */
34768 Roo.bootstrap.SplitBar.RIGHT = 2;
34769
34770 /**
34771  * Placement constant - The resizing element is positioned above the splitter element
34772  * @static
34773  * @type Number
34774  */
34775 Roo.bootstrap.SplitBar.TOP = 3;
34776
34777 /**
34778  * Placement constant - The resizing element is positioned under splitter element
34779  * @static
34780  * @type Number
34781  */
34782 Roo.bootstrap.SplitBar.BOTTOM = 4;
34783 Roo.namespace("Roo.bootstrap.layout");/*
34784  * Based on:
34785  * Ext JS Library 1.1.1
34786  * Copyright(c) 2006-2007, Ext JS, LLC.
34787  *
34788  * Originally Released Under LGPL - original licence link has changed is not relivant.
34789  *
34790  * Fork - LGPL
34791  * <script type="text/javascript">
34792  */
34793
34794 /**
34795  * @class Roo.bootstrap.layout.Manager
34796  * @extends Roo.bootstrap.Component
34797  * Base class for layout managers.
34798  */
34799 Roo.bootstrap.layout.Manager = function(config)
34800 {
34801     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34802
34803
34804
34805
34806
34807     /** false to disable window resize monitoring @type Boolean */
34808     this.monitorWindowResize = true;
34809     this.regions = {};
34810     this.addEvents({
34811         /**
34812          * @event layout
34813          * Fires when a layout is performed.
34814          * @param {Roo.LayoutManager} this
34815          */
34816         "layout" : true,
34817         /**
34818          * @event regionresized
34819          * Fires when the user resizes a region.
34820          * @param {Roo.LayoutRegion} region The resized region
34821          * @param {Number} newSize The new size (width for east/west, height for north/south)
34822          */
34823         "regionresized" : true,
34824         /**
34825          * @event regioncollapsed
34826          * Fires when a region is collapsed.
34827          * @param {Roo.LayoutRegion} region The collapsed region
34828          */
34829         "regioncollapsed" : true,
34830         /**
34831          * @event regionexpanded
34832          * Fires when a region is expanded.
34833          * @param {Roo.LayoutRegion} region The expanded region
34834          */
34835         "regionexpanded" : true
34836     });
34837     this.updating = false;
34838
34839     if (config.el) {
34840         this.el = Roo.get(config.el);
34841         this.initEvents();
34842     }
34843
34844 };
34845
34846 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34847
34848
34849     regions : null,
34850
34851     monitorWindowResize : true,
34852
34853
34854     updating : false,
34855
34856
34857     onRender : function(ct, position)
34858     {
34859         if(!this.el){
34860             this.el = Roo.get(ct);
34861             this.initEvents();
34862         }
34863         //this.fireEvent('render',this);
34864     },
34865
34866
34867     initEvents: function()
34868     {
34869
34870
34871         // ie scrollbar fix
34872         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34873             document.body.scroll = "no";
34874         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34875             this.el.position('relative');
34876         }
34877         this.id = this.el.id;
34878         this.el.addClass("roo-layout-container");
34879         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34880         if(this.el.dom != document.body ) {
34881             this.el.on('resize', this.layout,this);
34882             this.el.on('show', this.layout,this);
34883         }
34884
34885     },
34886
34887     /**
34888      * Returns true if this layout is currently being updated
34889      * @return {Boolean}
34890      */
34891     isUpdating : function(){
34892         return this.updating;
34893     },
34894
34895     /**
34896      * Suspend the LayoutManager from doing auto-layouts while
34897      * making multiple add or remove calls
34898      */
34899     beginUpdate : function(){
34900         this.updating = true;
34901     },
34902
34903     /**
34904      * Restore auto-layouts and optionally disable the manager from performing a layout
34905      * @param {Boolean} noLayout true to disable a layout update
34906      */
34907     endUpdate : function(noLayout){
34908         this.updating = false;
34909         if(!noLayout){
34910             this.layout();
34911         }
34912     },
34913
34914     layout: function(){
34915         // abstract...
34916     },
34917
34918     onRegionResized : function(region, newSize){
34919         this.fireEvent("regionresized", region, newSize);
34920         this.layout();
34921     },
34922
34923     onRegionCollapsed : function(region){
34924         this.fireEvent("regioncollapsed", region);
34925     },
34926
34927     onRegionExpanded : function(region){
34928         this.fireEvent("regionexpanded", region);
34929     },
34930
34931     /**
34932      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34933      * performs box-model adjustments.
34934      * @return {Object} The size as an object {width: (the width), height: (the height)}
34935      */
34936     getViewSize : function()
34937     {
34938         var size;
34939         if(this.el.dom != document.body){
34940             size = this.el.getSize();
34941         }else{
34942             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34943         }
34944         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34945         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34946         return size;
34947     },
34948
34949     /**
34950      * Returns the Element this layout is bound to.
34951      * @return {Roo.Element}
34952      */
34953     getEl : function(){
34954         return this.el;
34955     },
34956
34957     /**
34958      * Returns the specified region.
34959      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34960      * @return {Roo.LayoutRegion}
34961      */
34962     getRegion : function(target){
34963         return this.regions[target.toLowerCase()];
34964     },
34965
34966     onWindowResize : function(){
34967         if(this.monitorWindowResize){
34968             this.layout();
34969         }
34970     }
34971 });
34972 /*
34973  * Based on:
34974  * Ext JS Library 1.1.1
34975  * Copyright(c) 2006-2007, Ext JS, LLC.
34976  *
34977  * Originally Released Under LGPL - original licence link has changed is not relivant.
34978  *
34979  * Fork - LGPL
34980  * <script type="text/javascript">
34981  */
34982 /**
34983  * @class Roo.bootstrap.layout.Border
34984  * @extends Roo.bootstrap.layout.Manager
34985  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34986  * please see: examples/bootstrap/nested.html<br><br>
34987  
34988 <b>The container the layout is rendered into can be either the body element or any other element.
34989 If it is not the body element, the container needs to either be an absolute positioned element,
34990 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34991 the container size if it is not the body element.</b>
34992
34993 * @constructor
34994 * Create a new Border
34995 * @param {Object} config Configuration options
34996  */
34997 Roo.bootstrap.layout.Border = function(config){
34998     config = config || {};
34999     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35000     
35001     
35002     
35003     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35004         if(config[region]){
35005             config[region].region = region;
35006             this.addRegion(config[region]);
35007         }
35008     },this);
35009     
35010 };
35011
35012 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35013
35014 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35015     /**
35016      * Creates and adds a new region if it doesn't already exist.
35017      * @param {String} target The target region key (north, south, east, west or center).
35018      * @param {Object} config The regions config object
35019      * @return {BorderLayoutRegion} The new region
35020      */
35021     addRegion : function(config)
35022     {
35023         if(!this.regions[config.region]){
35024             var r = this.factory(config);
35025             this.bindRegion(r);
35026         }
35027         return this.regions[config.region];
35028     },
35029
35030     // private (kinda)
35031     bindRegion : function(r){
35032         this.regions[r.config.region] = r;
35033         
35034         r.on("visibilitychange",    this.layout, this);
35035         r.on("paneladded",          this.layout, this);
35036         r.on("panelremoved",        this.layout, this);
35037         r.on("invalidated",         this.layout, this);
35038         r.on("resized",             this.onRegionResized, this);
35039         r.on("collapsed",           this.onRegionCollapsed, this);
35040         r.on("expanded",            this.onRegionExpanded, this);
35041     },
35042
35043     /**
35044      * Performs a layout update.
35045      */
35046     layout : function()
35047     {
35048         if(this.updating) {
35049             return;
35050         }
35051         
35052         // render all the rebions if they have not been done alreayd?
35053         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35054             if(this.regions[region] && !this.regions[region].bodyEl){
35055                 this.regions[region].onRender(this.el)
35056             }
35057         },this);
35058         
35059         var size = this.getViewSize();
35060         var w = size.width;
35061         var h = size.height;
35062         var centerW = w;
35063         var centerH = h;
35064         var centerY = 0;
35065         var centerX = 0;
35066         //var x = 0, y = 0;
35067
35068         var rs = this.regions;
35069         var north = rs["north"];
35070         var south = rs["south"]; 
35071         var west = rs["west"];
35072         var east = rs["east"];
35073         var center = rs["center"];
35074         //if(this.hideOnLayout){ // not supported anymore
35075             //c.el.setStyle("display", "none");
35076         //}
35077         if(north && north.isVisible()){
35078             var b = north.getBox();
35079             var m = north.getMargins();
35080             b.width = w - (m.left+m.right);
35081             b.x = m.left;
35082             b.y = m.top;
35083             centerY = b.height + b.y + m.bottom;
35084             centerH -= centerY;
35085             north.updateBox(this.safeBox(b));
35086         }
35087         if(south && south.isVisible()){
35088             var b = south.getBox();
35089             var m = south.getMargins();
35090             b.width = w - (m.left+m.right);
35091             b.x = m.left;
35092             var totalHeight = (b.height + m.top + m.bottom);
35093             b.y = h - totalHeight + m.top;
35094             centerH -= totalHeight;
35095             south.updateBox(this.safeBox(b));
35096         }
35097         if(west && west.isVisible()){
35098             var b = west.getBox();
35099             var m = west.getMargins();
35100             b.height = centerH - (m.top+m.bottom);
35101             b.x = m.left;
35102             b.y = centerY + m.top;
35103             var totalWidth = (b.width + m.left + m.right);
35104             centerX += totalWidth;
35105             centerW -= totalWidth;
35106             west.updateBox(this.safeBox(b));
35107         }
35108         if(east && east.isVisible()){
35109             var b = east.getBox();
35110             var m = east.getMargins();
35111             b.height = centerH - (m.top+m.bottom);
35112             var totalWidth = (b.width + m.left + m.right);
35113             b.x = w - totalWidth + m.left;
35114             b.y = centerY + m.top;
35115             centerW -= totalWidth;
35116             east.updateBox(this.safeBox(b));
35117         }
35118         if(center){
35119             var m = center.getMargins();
35120             var centerBox = {
35121                 x: centerX + m.left,
35122                 y: centerY + m.top,
35123                 width: centerW - (m.left+m.right),
35124                 height: centerH - (m.top+m.bottom)
35125             };
35126             //if(this.hideOnLayout){
35127                 //center.el.setStyle("display", "block");
35128             //}
35129             center.updateBox(this.safeBox(centerBox));
35130         }
35131         this.el.repaint();
35132         this.fireEvent("layout", this);
35133     },
35134
35135     // private
35136     safeBox : function(box){
35137         box.width = Math.max(0, box.width);
35138         box.height = Math.max(0, box.height);
35139         return box;
35140     },
35141
35142     /**
35143      * Adds a ContentPanel (or subclass) to this layout.
35144      * @param {String} target The target region key (north, south, east, west or center).
35145      * @param {Roo.ContentPanel} panel The panel to add
35146      * @return {Roo.ContentPanel} The added panel
35147      */
35148     add : function(target, panel){
35149          
35150         target = target.toLowerCase();
35151         return this.regions[target].add(panel);
35152     },
35153
35154     /**
35155      * Remove a ContentPanel (or subclass) to this layout.
35156      * @param {String} target The target region key (north, south, east, west or center).
35157      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35158      * @return {Roo.ContentPanel} The removed panel
35159      */
35160     remove : function(target, panel){
35161         target = target.toLowerCase();
35162         return this.regions[target].remove(panel);
35163     },
35164
35165     /**
35166      * Searches all regions for a panel with the specified id
35167      * @param {String} panelId
35168      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35169      */
35170     findPanel : function(panelId){
35171         var rs = this.regions;
35172         for(var target in rs){
35173             if(typeof rs[target] != "function"){
35174                 var p = rs[target].getPanel(panelId);
35175                 if(p){
35176                     return p;
35177                 }
35178             }
35179         }
35180         return null;
35181     },
35182
35183     /**
35184      * Searches all regions for a panel with the specified id and activates (shows) it.
35185      * @param {String/ContentPanel} panelId The panels id or the panel itself
35186      * @return {Roo.ContentPanel} The shown panel or null
35187      */
35188     showPanel : function(panelId) {
35189       var rs = this.regions;
35190       for(var target in rs){
35191          var r = rs[target];
35192          if(typeof r != "function"){
35193             if(r.hasPanel(panelId)){
35194                return r.showPanel(panelId);
35195             }
35196          }
35197       }
35198       return null;
35199    },
35200
35201    /**
35202      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35203      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35204      */
35205    /*
35206     restoreState : function(provider){
35207         if(!provider){
35208             provider = Roo.state.Manager;
35209         }
35210         var sm = new Roo.LayoutStateManager();
35211         sm.init(this, provider);
35212     },
35213 */
35214  
35215  
35216     /**
35217      * Adds a xtype elements to the layout.
35218      * <pre><code>
35219
35220 layout.addxtype({
35221        xtype : 'ContentPanel',
35222        region: 'west',
35223        items: [ .... ]
35224    }
35225 );
35226
35227 layout.addxtype({
35228         xtype : 'NestedLayoutPanel',
35229         region: 'west',
35230         layout: {
35231            center: { },
35232            west: { }   
35233         },
35234         items : [ ... list of content panels or nested layout panels.. ]
35235    }
35236 );
35237 </code></pre>
35238      * @param {Object} cfg Xtype definition of item to add.
35239      */
35240     addxtype : function(cfg)
35241     {
35242         // basically accepts a pannel...
35243         // can accept a layout region..!?!?
35244         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35245         
35246         
35247         // theory?  children can only be panels??
35248         
35249         //if (!cfg.xtype.match(/Panel$/)) {
35250         //    return false;
35251         //}
35252         var ret = false;
35253         
35254         if (typeof(cfg.region) == 'undefined') {
35255             Roo.log("Failed to add Panel, region was not set");
35256             Roo.log(cfg);
35257             return false;
35258         }
35259         var region = cfg.region;
35260         delete cfg.region;
35261         
35262           
35263         var xitems = [];
35264         if (cfg.items) {
35265             xitems = cfg.items;
35266             delete cfg.items;
35267         }
35268         var nb = false;
35269         
35270         switch(cfg.xtype) 
35271         {
35272             case 'Content':  // ContentPanel (el, cfg)
35273             case 'Scroll':  // ContentPanel (el, cfg)
35274             case 'View': 
35275                 cfg.autoCreate = true;
35276                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35277                 //} else {
35278                 //    var el = this.el.createChild();
35279                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35280                 //}
35281                 
35282                 this.add(region, ret);
35283                 break;
35284             
35285             /*
35286             case 'TreePanel': // our new panel!
35287                 cfg.el = this.el.createChild();
35288                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35289                 this.add(region, ret);
35290                 break;
35291             */
35292             
35293             case 'Nest': 
35294                 // create a new Layout (which is  a Border Layout...
35295                 
35296                 var clayout = cfg.layout;
35297                 clayout.el  = this.el.createChild();
35298                 clayout.items   = clayout.items  || [];
35299                 
35300                 delete cfg.layout;
35301                 
35302                 // replace this exitems with the clayout ones..
35303                 xitems = clayout.items;
35304                  
35305                 // force background off if it's in center...
35306                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35307                     cfg.background = false;
35308                 }
35309                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35310                 
35311                 
35312                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35313                 //console.log('adding nested layout panel '  + cfg.toSource());
35314                 this.add(region, ret);
35315                 nb = {}; /// find first...
35316                 break;
35317             
35318             case 'Grid':
35319                 
35320                 // needs grid and region
35321                 
35322                 //var el = this.getRegion(region).el.createChild();
35323                 /*
35324                  *var el = this.el.createChild();
35325                 // create the grid first...
35326                 cfg.grid.container = el;
35327                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35328                 */
35329                 
35330                 if (region == 'center' && this.active ) {
35331                     cfg.background = false;
35332                 }
35333                 
35334                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35335                 
35336                 this.add(region, ret);
35337                 /*
35338                 if (cfg.background) {
35339                     // render grid on panel activation (if panel background)
35340                     ret.on('activate', function(gp) {
35341                         if (!gp.grid.rendered) {
35342                     //        gp.grid.render(el);
35343                         }
35344                     });
35345                 } else {
35346                   //  cfg.grid.render(el);
35347                 }
35348                 */
35349                 break;
35350            
35351            
35352             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35353                 // it was the old xcomponent building that caused this before.
35354                 // espeically if border is the top element in the tree.
35355                 ret = this;
35356                 break; 
35357                 
35358                     
35359                 
35360                 
35361                 
35362             default:
35363                 /*
35364                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35365                     
35366                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35367                     this.add(region, ret);
35368                 } else {
35369                 */
35370                     Roo.log(cfg);
35371                     throw "Can not add '" + cfg.xtype + "' to Border";
35372                     return null;
35373              
35374                                 
35375              
35376         }
35377         this.beginUpdate();
35378         // add children..
35379         var region = '';
35380         var abn = {};
35381         Roo.each(xitems, function(i)  {
35382             region = nb && i.region ? i.region : false;
35383             
35384             var add = ret.addxtype(i);
35385            
35386             if (region) {
35387                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35388                 if (!i.background) {
35389                     abn[region] = nb[region] ;
35390                 }
35391             }
35392             
35393         });
35394         this.endUpdate();
35395
35396         // make the last non-background panel active..
35397         //if (nb) { Roo.log(abn); }
35398         if (nb) {
35399             
35400             for(var r in abn) {
35401                 region = this.getRegion(r);
35402                 if (region) {
35403                     // tried using nb[r], but it does not work..
35404                      
35405                     region.showPanel(abn[r]);
35406                    
35407                 }
35408             }
35409         }
35410         return ret;
35411         
35412     },
35413     
35414     
35415 // private
35416     factory : function(cfg)
35417     {
35418         
35419         var validRegions = Roo.bootstrap.layout.Border.regions;
35420
35421         var target = cfg.region;
35422         cfg.mgr = this;
35423         
35424         var r = Roo.bootstrap.layout;
35425         Roo.log(target);
35426         switch(target){
35427             case "north":
35428                 return new r.North(cfg);
35429             case "south":
35430                 return new r.South(cfg);
35431             case "east":
35432                 return new r.East(cfg);
35433             case "west":
35434                 return new r.West(cfg);
35435             case "center":
35436                 return new r.Center(cfg);
35437         }
35438         throw 'Layout region "'+target+'" not supported.';
35439     }
35440     
35441     
35442 });
35443  /*
35444  * Based on:
35445  * Ext JS Library 1.1.1
35446  * Copyright(c) 2006-2007, Ext JS, LLC.
35447  *
35448  * Originally Released Under LGPL - original licence link has changed is not relivant.
35449  *
35450  * Fork - LGPL
35451  * <script type="text/javascript">
35452  */
35453  
35454 /**
35455  * @class Roo.bootstrap.layout.Basic
35456  * @extends Roo.util.Observable
35457  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35458  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35459  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35460  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35461  * @cfg {string}   region  the region that it inhabits..
35462  * @cfg {bool}   skipConfig skip config?
35463  * 
35464
35465  */
35466 Roo.bootstrap.layout.Basic = function(config){
35467     
35468     this.mgr = config.mgr;
35469     
35470     this.position = config.region;
35471     
35472     var skipConfig = config.skipConfig;
35473     
35474     this.events = {
35475         /**
35476          * @scope Roo.BasicLayoutRegion
35477          */
35478         
35479         /**
35480          * @event beforeremove
35481          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35482          * @param {Roo.LayoutRegion} this
35483          * @param {Roo.ContentPanel} panel The panel
35484          * @param {Object} e The cancel event object
35485          */
35486         "beforeremove" : true,
35487         /**
35488          * @event invalidated
35489          * Fires when the layout for this region is changed.
35490          * @param {Roo.LayoutRegion} this
35491          */
35492         "invalidated" : true,
35493         /**
35494          * @event visibilitychange
35495          * Fires when this region is shown or hidden 
35496          * @param {Roo.LayoutRegion} this
35497          * @param {Boolean} visibility true or false
35498          */
35499         "visibilitychange" : true,
35500         /**
35501          * @event paneladded
35502          * Fires when a panel is added. 
35503          * @param {Roo.LayoutRegion} this
35504          * @param {Roo.ContentPanel} panel The panel
35505          */
35506         "paneladded" : true,
35507         /**
35508          * @event panelremoved
35509          * Fires when a panel is removed. 
35510          * @param {Roo.LayoutRegion} this
35511          * @param {Roo.ContentPanel} panel The panel
35512          */
35513         "panelremoved" : true,
35514         /**
35515          * @event beforecollapse
35516          * Fires when this region before collapse.
35517          * @param {Roo.LayoutRegion} this
35518          */
35519         "beforecollapse" : true,
35520         /**
35521          * @event collapsed
35522          * Fires when this region is collapsed.
35523          * @param {Roo.LayoutRegion} this
35524          */
35525         "collapsed" : true,
35526         /**
35527          * @event expanded
35528          * Fires when this region is expanded.
35529          * @param {Roo.LayoutRegion} this
35530          */
35531         "expanded" : true,
35532         /**
35533          * @event slideshow
35534          * Fires when this region is slid into view.
35535          * @param {Roo.LayoutRegion} this
35536          */
35537         "slideshow" : true,
35538         /**
35539          * @event slidehide
35540          * Fires when this region slides out of view. 
35541          * @param {Roo.LayoutRegion} this
35542          */
35543         "slidehide" : true,
35544         /**
35545          * @event panelactivated
35546          * Fires when a panel is activated. 
35547          * @param {Roo.LayoutRegion} this
35548          * @param {Roo.ContentPanel} panel The activated panel
35549          */
35550         "panelactivated" : true,
35551         /**
35552          * @event resized
35553          * Fires when the user resizes this region. 
35554          * @param {Roo.LayoutRegion} this
35555          * @param {Number} newSize The new size (width for east/west, height for north/south)
35556          */
35557         "resized" : true
35558     };
35559     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35560     this.panels = new Roo.util.MixedCollection();
35561     this.panels.getKey = this.getPanelId.createDelegate(this);
35562     this.box = null;
35563     this.activePanel = null;
35564     // ensure listeners are added...
35565     
35566     if (config.listeners || config.events) {
35567         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35568             listeners : config.listeners || {},
35569             events : config.events || {}
35570         });
35571     }
35572     
35573     if(skipConfig !== true){
35574         this.applyConfig(config);
35575     }
35576 };
35577
35578 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35579 {
35580     getPanelId : function(p){
35581         return p.getId();
35582     },
35583     
35584     applyConfig : function(config){
35585         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35586         this.config = config;
35587         
35588     },
35589     
35590     /**
35591      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35592      * the width, for horizontal (north, south) the height.
35593      * @param {Number} newSize The new width or height
35594      */
35595     resizeTo : function(newSize){
35596         var el = this.el ? this.el :
35597                  (this.activePanel ? this.activePanel.getEl() : null);
35598         if(el){
35599             switch(this.position){
35600                 case "east":
35601                 case "west":
35602                     el.setWidth(newSize);
35603                     this.fireEvent("resized", this, newSize);
35604                 break;
35605                 case "north":
35606                 case "south":
35607                     el.setHeight(newSize);
35608                     this.fireEvent("resized", this, newSize);
35609                 break;                
35610             }
35611         }
35612     },
35613     
35614     getBox : function(){
35615         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35616     },
35617     
35618     getMargins : function(){
35619         return this.margins;
35620     },
35621     
35622     updateBox : function(box){
35623         this.box = box;
35624         var el = this.activePanel.getEl();
35625         el.dom.style.left = box.x + "px";
35626         el.dom.style.top = box.y + "px";
35627         this.activePanel.setSize(box.width, box.height);
35628     },
35629     
35630     /**
35631      * Returns the container element for this region.
35632      * @return {Roo.Element}
35633      */
35634     getEl : function(){
35635         return this.activePanel;
35636     },
35637     
35638     /**
35639      * Returns true if this region is currently visible.
35640      * @return {Boolean}
35641      */
35642     isVisible : function(){
35643         return this.activePanel ? true : false;
35644     },
35645     
35646     setActivePanel : function(panel){
35647         panel = this.getPanel(panel);
35648         if(this.activePanel && this.activePanel != panel){
35649             this.activePanel.setActiveState(false);
35650             this.activePanel.getEl().setLeftTop(-10000,-10000);
35651         }
35652         this.activePanel = panel;
35653         panel.setActiveState(true);
35654         if(this.box){
35655             panel.setSize(this.box.width, this.box.height);
35656         }
35657         this.fireEvent("panelactivated", this, panel);
35658         this.fireEvent("invalidated");
35659     },
35660     
35661     /**
35662      * Show the specified panel.
35663      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35664      * @return {Roo.ContentPanel} The shown panel or null
35665      */
35666     showPanel : function(panel){
35667         panel = this.getPanel(panel);
35668         if(panel){
35669             this.setActivePanel(panel);
35670         }
35671         return panel;
35672     },
35673     
35674     /**
35675      * Get the active panel for this region.
35676      * @return {Roo.ContentPanel} The active panel or null
35677      */
35678     getActivePanel : function(){
35679         return this.activePanel;
35680     },
35681     
35682     /**
35683      * Add the passed ContentPanel(s)
35684      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35685      * @return {Roo.ContentPanel} The panel added (if only one was added)
35686      */
35687     add : function(panel){
35688         if(arguments.length > 1){
35689             for(var i = 0, len = arguments.length; i < len; i++) {
35690                 this.add(arguments[i]);
35691             }
35692             return null;
35693         }
35694         if(this.hasPanel(panel)){
35695             this.showPanel(panel);
35696             return panel;
35697         }
35698         var el = panel.getEl();
35699         if(el.dom.parentNode != this.mgr.el.dom){
35700             this.mgr.el.dom.appendChild(el.dom);
35701         }
35702         if(panel.setRegion){
35703             panel.setRegion(this);
35704         }
35705         this.panels.add(panel);
35706         el.setStyle("position", "absolute");
35707         if(!panel.background){
35708             this.setActivePanel(panel);
35709             if(this.config.initialSize && this.panels.getCount()==1){
35710                 this.resizeTo(this.config.initialSize);
35711             }
35712         }
35713         this.fireEvent("paneladded", this, panel);
35714         return panel;
35715     },
35716     
35717     /**
35718      * Returns true if the panel is in this region.
35719      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35720      * @return {Boolean}
35721      */
35722     hasPanel : function(panel){
35723         if(typeof panel == "object"){ // must be panel obj
35724             panel = panel.getId();
35725         }
35726         return this.getPanel(panel) ? true : false;
35727     },
35728     
35729     /**
35730      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35731      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35732      * @param {Boolean} preservePanel Overrides the config preservePanel option
35733      * @return {Roo.ContentPanel} The panel that was removed
35734      */
35735     remove : function(panel, preservePanel){
35736         panel = this.getPanel(panel);
35737         if(!panel){
35738             return null;
35739         }
35740         var e = {};
35741         this.fireEvent("beforeremove", this, panel, e);
35742         if(e.cancel === true){
35743             return null;
35744         }
35745         var panelId = panel.getId();
35746         this.panels.removeKey(panelId);
35747         return panel;
35748     },
35749     
35750     /**
35751      * Returns the panel specified or null if it's not in this region.
35752      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35753      * @return {Roo.ContentPanel}
35754      */
35755     getPanel : function(id){
35756         if(typeof id == "object"){ // must be panel obj
35757             return id;
35758         }
35759         return this.panels.get(id);
35760     },
35761     
35762     /**
35763      * Returns this regions position (north/south/east/west/center).
35764      * @return {String} 
35765      */
35766     getPosition: function(){
35767         return this.position;    
35768     }
35769 });/*
35770  * Based on:
35771  * Ext JS Library 1.1.1
35772  * Copyright(c) 2006-2007, Ext JS, LLC.
35773  *
35774  * Originally Released Under LGPL - original licence link has changed is not relivant.
35775  *
35776  * Fork - LGPL
35777  * <script type="text/javascript">
35778  */
35779  
35780 /**
35781  * @class Roo.bootstrap.layout.Region
35782  * @extends Roo.bootstrap.layout.Basic
35783  * This class represents a region in a layout manager.
35784  
35785  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35786  * @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})
35787  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35788  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35789  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35790  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35791  * @cfg {String}    title           The title for the region (overrides panel titles)
35792  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35793  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35794  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35795  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35796  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35797  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35798  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35799  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35800  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35801  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35802
35803  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35804  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35805  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35806  * @cfg {Number}    width           For East/West panels
35807  * @cfg {Number}    height          For North/South panels
35808  * @cfg {Boolean}   split           To show the splitter
35809  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35810  * 
35811  * @cfg {string}   cls             Extra CSS classes to add to region
35812  * 
35813  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35814  * @cfg {string}   region  the region that it inhabits..
35815  *
35816
35817  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35818  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35819
35820  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35821  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35822  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35823  */
35824 Roo.bootstrap.layout.Region = function(config)
35825 {
35826     this.applyConfig(config);
35827
35828     var mgr = config.mgr;
35829     var pos = config.region;
35830     config.skipConfig = true;
35831     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35832     
35833     if (mgr.el) {
35834         this.onRender(mgr.el);   
35835     }
35836      
35837     this.visible = true;
35838     this.collapsed = false;
35839     this.unrendered_panels = [];
35840 };
35841
35842 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35843
35844     position: '', // set by wrapper (eg. north/south etc..)
35845     unrendered_panels : null,  // unrendered panels.
35846     createBody : function(){
35847         /** This region's body element 
35848         * @type Roo.Element */
35849         this.bodyEl = this.el.createChild({
35850                 tag: "div",
35851                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35852         });
35853     },
35854
35855     onRender: function(ctr, pos)
35856     {
35857         var dh = Roo.DomHelper;
35858         /** This region's container element 
35859         * @type Roo.Element */
35860         this.el = dh.append(ctr.dom, {
35861                 tag: "div",
35862                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35863             }, true);
35864         /** This region's title element 
35865         * @type Roo.Element */
35866     
35867         this.titleEl = dh.append(this.el.dom,
35868             {
35869                     tag: "div",
35870                     unselectable: "on",
35871                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35872                     children:[
35873                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35874                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35875                     ]}, true);
35876         
35877         this.titleEl.enableDisplayMode();
35878         /** This region's title text element 
35879         * @type HTMLElement */
35880         this.titleTextEl = this.titleEl.dom.firstChild;
35881         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35882         /*
35883         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35884         this.closeBtn.enableDisplayMode();
35885         this.closeBtn.on("click", this.closeClicked, this);
35886         this.closeBtn.hide();
35887     */
35888         this.createBody(this.config);
35889         if(this.config.hideWhenEmpty){
35890             this.hide();
35891             this.on("paneladded", this.validateVisibility, this);
35892             this.on("panelremoved", this.validateVisibility, this);
35893         }
35894         if(this.autoScroll){
35895             this.bodyEl.setStyle("overflow", "auto");
35896         }else{
35897             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35898         }
35899         //if(c.titlebar !== false){
35900             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35901                 this.titleEl.hide();
35902             }else{
35903                 this.titleEl.show();
35904                 if(this.config.title){
35905                     this.titleTextEl.innerHTML = this.config.title;
35906                 }
35907             }
35908         //}
35909         if(this.config.collapsed){
35910             this.collapse(true);
35911         }
35912         if(this.config.hidden){
35913             this.hide();
35914         }
35915         
35916         if (this.unrendered_panels && this.unrendered_panels.length) {
35917             for (var i =0;i< this.unrendered_panels.length; i++) {
35918                 this.add(this.unrendered_panels[i]);
35919             }
35920             this.unrendered_panels = null;
35921             
35922         }
35923         
35924     },
35925     
35926     applyConfig : function(c)
35927     {
35928         /*
35929          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35930             var dh = Roo.DomHelper;
35931             if(c.titlebar !== false){
35932                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35933                 this.collapseBtn.on("click", this.collapse, this);
35934                 this.collapseBtn.enableDisplayMode();
35935                 /*
35936                 if(c.showPin === true || this.showPin){
35937                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35938                     this.stickBtn.enableDisplayMode();
35939                     this.stickBtn.on("click", this.expand, this);
35940                     this.stickBtn.hide();
35941                 }
35942                 
35943             }
35944             */
35945             /** This region's collapsed element
35946             * @type Roo.Element */
35947             /*
35948              *
35949             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35950                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35951             ]}, true);
35952             
35953             if(c.floatable !== false){
35954                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35955                this.collapsedEl.on("click", this.collapseClick, this);
35956             }
35957
35958             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35959                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35960                    id: "message", unselectable: "on", style:{"float":"left"}});
35961                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35962              }
35963             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35964             this.expandBtn.on("click", this.expand, this);
35965             
35966         }
35967         
35968         if(this.collapseBtn){
35969             this.collapseBtn.setVisible(c.collapsible == true);
35970         }
35971         
35972         this.cmargins = c.cmargins || this.cmargins ||
35973                          (this.position == "west" || this.position == "east" ?
35974                              {top: 0, left: 2, right:2, bottom: 0} :
35975                              {top: 2, left: 0, right:0, bottom: 2});
35976         */
35977         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35978         
35979         
35980         this.bottomTabs = c.tabPosition != "top";
35981         
35982         this.autoScroll = c.autoScroll || false;
35983         
35984         
35985        
35986         
35987         this.duration = c.duration || .30;
35988         this.slideDuration = c.slideDuration || .45;
35989         this.config = c;
35990        
35991     },
35992     /**
35993      * Returns true if this region is currently visible.
35994      * @return {Boolean}
35995      */
35996     isVisible : function(){
35997         return this.visible;
35998     },
35999
36000     /**
36001      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36002      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36003      */
36004     //setCollapsedTitle : function(title){
36005     //    title = title || "&#160;";
36006      //   if(this.collapsedTitleTextEl){
36007       //      this.collapsedTitleTextEl.innerHTML = title;
36008        // }
36009     //},
36010
36011     getBox : function(){
36012         var b;
36013       //  if(!this.collapsed){
36014             b = this.el.getBox(false, true);
36015        // }else{
36016           //  b = this.collapsedEl.getBox(false, true);
36017         //}
36018         return b;
36019     },
36020
36021     getMargins : function(){
36022         return this.margins;
36023         //return this.collapsed ? this.cmargins : this.margins;
36024     },
36025 /*
36026     highlight : function(){
36027         this.el.addClass("x-layout-panel-dragover");
36028     },
36029
36030     unhighlight : function(){
36031         this.el.removeClass("x-layout-panel-dragover");
36032     },
36033 */
36034     updateBox : function(box)
36035     {
36036         if (!this.bodyEl) {
36037             return; // not rendered yet..
36038         }
36039         
36040         this.box = box;
36041         if(!this.collapsed){
36042             this.el.dom.style.left = box.x + "px";
36043             this.el.dom.style.top = box.y + "px";
36044             this.updateBody(box.width, box.height);
36045         }else{
36046             this.collapsedEl.dom.style.left = box.x + "px";
36047             this.collapsedEl.dom.style.top = box.y + "px";
36048             this.collapsedEl.setSize(box.width, box.height);
36049         }
36050         if(this.tabs){
36051             this.tabs.autoSizeTabs();
36052         }
36053     },
36054
36055     updateBody : function(w, h)
36056     {
36057         if(w !== null){
36058             this.el.setWidth(w);
36059             w -= this.el.getBorderWidth("rl");
36060             if(this.config.adjustments){
36061                 w += this.config.adjustments[0];
36062             }
36063         }
36064         if(h !== null && h > 0){
36065             this.el.setHeight(h);
36066             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36067             h -= this.el.getBorderWidth("tb");
36068             if(this.config.adjustments){
36069                 h += this.config.adjustments[1];
36070             }
36071             this.bodyEl.setHeight(h);
36072             if(this.tabs){
36073                 h = this.tabs.syncHeight(h);
36074             }
36075         }
36076         if(this.panelSize){
36077             w = w !== null ? w : this.panelSize.width;
36078             h = h !== null ? h : this.panelSize.height;
36079         }
36080         if(this.activePanel){
36081             var el = this.activePanel.getEl();
36082             w = w !== null ? w : el.getWidth();
36083             h = h !== null ? h : el.getHeight();
36084             this.panelSize = {width: w, height: h};
36085             this.activePanel.setSize(w, h);
36086         }
36087         if(Roo.isIE && this.tabs){
36088             this.tabs.el.repaint();
36089         }
36090     },
36091
36092     /**
36093      * Returns the container element for this region.
36094      * @return {Roo.Element}
36095      */
36096     getEl : function(){
36097         return this.el;
36098     },
36099
36100     /**
36101      * Hides this region.
36102      */
36103     hide : function(){
36104         //if(!this.collapsed){
36105             this.el.dom.style.left = "-2000px";
36106             this.el.hide();
36107         //}else{
36108          //   this.collapsedEl.dom.style.left = "-2000px";
36109          //   this.collapsedEl.hide();
36110        // }
36111         this.visible = false;
36112         this.fireEvent("visibilitychange", this, false);
36113     },
36114
36115     /**
36116      * Shows this region if it was previously hidden.
36117      */
36118     show : function(){
36119         //if(!this.collapsed){
36120             this.el.show();
36121         //}else{
36122         //    this.collapsedEl.show();
36123        // }
36124         this.visible = true;
36125         this.fireEvent("visibilitychange", this, true);
36126     },
36127 /*
36128     closeClicked : function(){
36129         if(this.activePanel){
36130             this.remove(this.activePanel);
36131         }
36132     },
36133
36134     collapseClick : function(e){
36135         if(this.isSlid){
36136            e.stopPropagation();
36137            this.slideIn();
36138         }else{
36139            e.stopPropagation();
36140            this.slideOut();
36141         }
36142     },
36143 */
36144     /**
36145      * Collapses this region.
36146      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36147      */
36148     /*
36149     collapse : function(skipAnim, skipCheck = false){
36150         if(this.collapsed) {
36151             return;
36152         }
36153         
36154         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36155             
36156             this.collapsed = true;
36157             if(this.split){
36158                 this.split.el.hide();
36159             }
36160             if(this.config.animate && skipAnim !== true){
36161                 this.fireEvent("invalidated", this);
36162                 this.animateCollapse();
36163             }else{
36164                 this.el.setLocation(-20000,-20000);
36165                 this.el.hide();
36166                 this.collapsedEl.show();
36167                 this.fireEvent("collapsed", this);
36168                 this.fireEvent("invalidated", this);
36169             }
36170         }
36171         
36172     },
36173 */
36174     animateCollapse : function(){
36175         // overridden
36176     },
36177
36178     /**
36179      * Expands this region if it was previously collapsed.
36180      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36181      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36182      */
36183     /*
36184     expand : function(e, skipAnim){
36185         if(e) {
36186             e.stopPropagation();
36187         }
36188         if(!this.collapsed || this.el.hasActiveFx()) {
36189             return;
36190         }
36191         if(this.isSlid){
36192             this.afterSlideIn();
36193             skipAnim = true;
36194         }
36195         this.collapsed = false;
36196         if(this.config.animate && skipAnim !== true){
36197             this.animateExpand();
36198         }else{
36199             this.el.show();
36200             if(this.split){
36201                 this.split.el.show();
36202             }
36203             this.collapsedEl.setLocation(-2000,-2000);
36204             this.collapsedEl.hide();
36205             this.fireEvent("invalidated", this);
36206             this.fireEvent("expanded", this);
36207         }
36208     },
36209 */
36210     animateExpand : function(){
36211         // overridden
36212     },
36213
36214     initTabs : function()
36215     {
36216         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36217         
36218         var ts = new Roo.bootstrap.panel.Tabs({
36219                 el: this.bodyEl.dom,
36220                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36221                 disableTooltips: this.config.disableTabTips,
36222                 toolbar : this.config.toolbar
36223             });
36224         
36225         if(this.config.hideTabs){
36226             ts.stripWrap.setDisplayed(false);
36227         }
36228         this.tabs = ts;
36229         ts.resizeTabs = this.config.resizeTabs === true;
36230         ts.minTabWidth = this.config.minTabWidth || 40;
36231         ts.maxTabWidth = this.config.maxTabWidth || 250;
36232         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36233         ts.monitorResize = false;
36234         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36235         ts.bodyEl.addClass('roo-layout-tabs-body');
36236         this.panels.each(this.initPanelAsTab, this);
36237     },
36238
36239     initPanelAsTab : function(panel){
36240         var ti = this.tabs.addTab(
36241             panel.getEl().id,
36242             panel.getTitle(),
36243             null,
36244             this.config.closeOnTab && panel.isClosable(),
36245             panel.tpl
36246         );
36247         if(panel.tabTip !== undefined){
36248             ti.setTooltip(panel.tabTip);
36249         }
36250         ti.on("activate", function(){
36251               this.setActivePanel(panel);
36252         }, this);
36253         
36254         if(this.config.closeOnTab){
36255             ti.on("beforeclose", function(t, e){
36256                 e.cancel = true;
36257                 this.remove(panel);
36258             }, this);
36259         }
36260         
36261         panel.tabItem = ti;
36262         
36263         return ti;
36264     },
36265
36266     updatePanelTitle : function(panel, title)
36267     {
36268         if(this.activePanel == panel){
36269             this.updateTitle(title);
36270         }
36271         if(this.tabs){
36272             var ti = this.tabs.getTab(panel.getEl().id);
36273             ti.setText(title);
36274             if(panel.tabTip !== undefined){
36275                 ti.setTooltip(panel.tabTip);
36276             }
36277         }
36278     },
36279
36280     updateTitle : function(title){
36281         if(this.titleTextEl && !this.config.title){
36282             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36283         }
36284     },
36285
36286     setActivePanel : function(panel)
36287     {
36288         panel = this.getPanel(panel);
36289         if(this.activePanel && this.activePanel != panel){
36290             if(this.activePanel.setActiveState(false) === false){
36291                 return;
36292             }
36293         }
36294         this.activePanel = panel;
36295         panel.setActiveState(true);
36296         if(this.panelSize){
36297             panel.setSize(this.panelSize.width, this.panelSize.height);
36298         }
36299         if(this.closeBtn){
36300             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36301         }
36302         this.updateTitle(panel.getTitle());
36303         if(this.tabs){
36304             this.fireEvent("invalidated", this);
36305         }
36306         this.fireEvent("panelactivated", this, panel);
36307     },
36308
36309     /**
36310      * Shows the specified panel.
36311      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36312      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36313      */
36314     showPanel : function(panel)
36315     {
36316         panel = this.getPanel(panel);
36317         if(panel){
36318             if(this.tabs){
36319                 var tab = this.tabs.getTab(panel.getEl().id);
36320                 if(tab.isHidden()){
36321                     this.tabs.unhideTab(tab.id);
36322                 }
36323                 tab.activate();
36324             }else{
36325                 this.setActivePanel(panel);
36326             }
36327         }
36328         return panel;
36329     },
36330
36331     /**
36332      * Get the active panel for this region.
36333      * @return {Roo.ContentPanel} The active panel or null
36334      */
36335     getActivePanel : function(){
36336         return this.activePanel;
36337     },
36338
36339     validateVisibility : function(){
36340         if(this.panels.getCount() < 1){
36341             this.updateTitle("&#160;");
36342             this.closeBtn.hide();
36343             this.hide();
36344         }else{
36345             if(!this.isVisible()){
36346                 this.show();
36347             }
36348         }
36349     },
36350
36351     /**
36352      * Adds the passed ContentPanel(s) to this region.
36353      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36354      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36355      */
36356     add : function(panel)
36357     {
36358         if(arguments.length > 1){
36359             for(var i = 0, len = arguments.length; i < len; i++) {
36360                 this.add(arguments[i]);
36361             }
36362             return null;
36363         }
36364         
36365         // if we have not been rendered yet, then we can not really do much of this..
36366         if (!this.bodyEl) {
36367             this.unrendered_panels.push(panel);
36368             return panel;
36369         }
36370         
36371         
36372         
36373         
36374         if(this.hasPanel(panel)){
36375             this.showPanel(panel);
36376             return panel;
36377         }
36378         panel.setRegion(this);
36379         this.panels.add(panel);
36380        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36381             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36382             // and hide them... ???
36383             this.bodyEl.dom.appendChild(panel.getEl().dom);
36384             if(panel.background !== true){
36385                 this.setActivePanel(panel);
36386             }
36387             this.fireEvent("paneladded", this, panel);
36388             return panel;
36389         }
36390         */
36391         if(!this.tabs){
36392             this.initTabs();
36393         }else{
36394             this.initPanelAsTab(panel);
36395         }
36396         
36397         
36398         if(panel.background !== true){
36399             this.tabs.activate(panel.getEl().id);
36400         }
36401         this.fireEvent("paneladded", this, panel);
36402         return panel;
36403     },
36404
36405     /**
36406      * Hides the tab for the specified panel.
36407      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36408      */
36409     hidePanel : function(panel){
36410         if(this.tabs && (panel = this.getPanel(panel))){
36411             this.tabs.hideTab(panel.getEl().id);
36412         }
36413     },
36414
36415     /**
36416      * Unhides the tab for a previously hidden panel.
36417      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36418      */
36419     unhidePanel : function(panel){
36420         if(this.tabs && (panel = this.getPanel(panel))){
36421             this.tabs.unhideTab(panel.getEl().id);
36422         }
36423     },
36424
36425     clearPanels : function(){
36426         while(this.panels.getCount() > 0){
36427              this.remove(this.panels.first());
36428         }
36429     },
36430
36431     /**
36432      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36433      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36434      * @param {Boolean} preservePanel Overrides the config preservePanel option
36435      * @return {Roo.ContentPanel} The panel that was removed
36436      */
36437     remove : function(panel, preservePanel)
36438     {
36439         panel = this.getPanel(panel);
36440         if(!panel){
36441             return null;
36442         }
36443         var e = {};
36444         this.fireEvent("beforeremove", this, panel, e);
36445         if(e.cancel === true){
36446             return null;
36447         }
36448         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36449         var panelId = panel.getId();
36450         this.panels.removeKey(panelId);
36451         if(preservePanel){
36452             document.body.appendChild(panel.getEl().dom);
36453         }
36454         if(this.tabs){
36455             this.tabs.removeTab(panel.getEl().id);
36456         }else if (!preservePanel){
36457             this.bodyEl.dom.removeChild(panel.getEl().dom);
36458         }
36459         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36460             var p = this.panels.first();
36461             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36462             tempEl.appendChild(p.getEl().dom);
36463             this.bodyEl.update("");
36464             this.bodyEl.dom.appendChild(p.getEl().dom);
36465             tempEl = null;
36466             this.updateTitle(p.getTitle());
36467             this.tabs = null;
36468             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36469             this.setActivePanel(p);
36470         }
36471         panel.setRegion(null);
36472         if(this.activePanel == panel){
36473             this.activePanel = null;
36474         }
36475         if(this.config.autoDestroy !== false && preservePanel !== true){
36476             try{panel.destroy();}catch(e){}
36477         }
36478         this.fireEvent("panelremoved", this, panel);
36479         return panel;
36480     },
36481
36482     /**
36483      * Returns the TabPanel component used by this region
36484      * @return {Roo.TabPanel}
36485      */
36486     getTabs : function(){
36487         return this.tabs;
36488     },
36489
36490     createTool : function(parentEl, className){
36491         var btn = Roo.DomHelper.append(parentEl, {
36492             tag: "div",
36493             cls: "x-layout-tools-button",
36494             children: [ {
36495                 tag: "div",
36496                 cls: "roo-layout-tools-button-inner " + className,
36497                 html: "&#160;"
36498             }]
36499         }, true);
36500         btn.addClassOnOver("roo-layout-tools-button-over");
36501         return btn;
36502     }
36503 });/*
36504  * Based on:
36505  * Ext JS Library 1.1.1
36506  * Copyright(c) 2006-2007, Ext JS, LLC.
36507  *
36508  * Originally Released Under LGPL - original licence link has changed is not relivant.
36509  *
36510  * Fork - LGPL
36511  * <script type="text/javascript">
36512  */
36513  
36514
36515
36516 /**
36517  * @class Roo.SplitLayoutRegion
36518  * @extends Roo.LayoutRegion
36519  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36520  */
36521 Roo.bootstrap.layout.Split = function(config){
36522     this.cursor = config.cursor;
36523     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36524 };
36525
36526 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36527 {
36528     splitTip : "Drag to resize.",
36529     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36530     useSplitTips : false,
36531
36532     applyConfig : function(config){
36533         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36534     },
36535     
36536     onRender : function(ctr,pos) {
36537         
36538         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36539         if(!this.config.split){
36540             return;
36541         }
36542         if(!this.split){
36543             
36544             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36545                             tag: "div",
36546                             id: this.el.id + "-split",
36547                             cls: "roo-layout-split roo-layout-split-"+this.position,
36548                             html: "&#160;"
36549             });
36550             /** The SplitBar for this region 
36551             * @type Roo.SplitBar */
36552             // does not exist yet...
36553             Roo.log([this.position, this.orientation]);
36554             
36555             this.split = new Roo.bootstrap.SplitBar({
36556                 dragElement : splitEl,
36557                 resizingElement: this.el,
36558                 orientation : this.orientation
36559             });
36560             
36561             this.split.on("moved", this.onSplitMove, this);
36562             this.split.useShim = this.config.useShim === true;
36563             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36564             if(this.useSplitTips){
36565                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36566             }
36567             //if(config.collapsible){
36568             //    this.split.el.on("dblclick", this.collapse,  this);
36569             //}
36570         }
36571         if(typeof this.config.minSize != "undefined"){
36572             this.split.minSize = this.config.minSize;
36573         }
36574         if(typeof this.config.maxSize != "undefined"){
36575             this.split.maxSize = this.config.maxSize;
36576         }
36577         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36578             this.hideSplitter();
36579         }
36580         
36581     },
36582
36583     getHMaxSize : function(){
36584          var cmax = this.config.maxSize || 10000;
36585          var center = this.mgr.getRegion("center");
36586          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36587     },
36588
36589     getVMaxSize : function(){
36590          var cmax = this.config.maxSize || 10000;
36591          var center = this.mgr.getRegion("center");
36592          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36593     },
36594
36595     onSplitMove : function(split, newSize){
36596         this.fireEvent("resized", this, newSize);
36597     },
36598     
36599     /** 
36600      * Returns the {@link Roo.SplitBar} for this region.
36601      * @return {Roo.SplitBar}
36602      */
36603     getSplitBar : function(){
36604         return this.split;
36605     },
36606     
36607     hide : function(){
36608         this.hideSplitter();
36609         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36610     },
36611
36612     hideSplitter : function(){
36613         if(this.split){
36614             this.split.el.setLocation(-2000,-2000);
36615             this.split.el.hide();
36616         }
36617     },
36618
36619     show : function(){
36620         if(this.split){
36621             this.split.el.show();
36622         }
36623         Roo.bootstrap.layout.Split.superclass.show.call(this);
36624     },
36625     
36626     beforeSlide: function(){
36627         if(Roo.isGecko){// firefox overflow auto bug workaround
36628             this.bodyEl.clip();
36629             if(this.tabs) {
36630                 this.tabs.bodyEl.clip();
36631             }
36632             if(this.activePanel){
36633                 this.activePanel.getEl().clip();
36634                 
36635                 if(this.activePanel.beforeSlide){
36636                     this.activePanel.beforeSlide();
36637                 }
36638             }
36639         }
36640     },
36641     
36642     afterSlide : function(){
36643         if(Roo.isGecko){// firefox overflow auto bug workaround
36644             this.bodyEl.unclip();
36645             if(this.tabs) {
36646                 this.tabs.bodyEl.unclip();
36647             }
36648             if(this.activePanel){
36649                 this.activePanel.getEl().unclip();
36650                 if(this.activePanel.afterSlide){
36651                     this.activePanel.afterSlide();
36652                 }
36653             }
36654         }
36655     },
36656
36657     initAutoHide : function(){
36658         if(this.autoHide !== false){
36659             if(!this.autoHideHd){
36660                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36661                 this.autoHideHd = {
36662                     "mouseout": function(e){
36663                         if(!e.within(this.el, true)){
36664                             st.delay(500);
36665                         }
36666                     },
36667                     "mouseover" : function(e){
36668                         st.cancel();
36669                     },
36670                     scope : this
36671                 };
36672             }
36673             this.el.on(this.autoHideHd);
36674         }
36675     },
36676
36677     clearAutoHide : function(){
36678         if(this.autoHide !== false){
36679             this.el.un("mouseout", this.autoHideHd.mouseout);
36680             this.el.un("mouseover", this.autoHideHd.mouseover);
36681         }
36682     },
36683
36684     clearMonitor : function(){
36685         Roo.get(document).un("click", this.slideInIf, this);
36686     },
36687
36688     // these names are backwards but not changed for compat
36689     slideOut : function(){
36690         if(this.isSlid || this.el.hasActiveFx()){
36691             return;
36692         }
36693         this.isSlid = true;
36694         if(this.collapseBtn){
36695             this.collapseBtn.hide();
36696         }
36697         this.closeBtnState = this.closeBtn.getStyle('display');
36698         this.closeBtn.hide();
36699         if(this.stickBtn){
36700             this.stickBtn.show();
36701         }
36702         this.el.show();
36703         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36704         this.beforeSlide();
36705         this.el.setStyle("z-index", 10001);
36706         this.el.slideIn(this.getSlideAnchor(), {
36707             callback: function(){
36708                 this.afterSlide();
36709                 this.initAutoHide();
36710                 Roo.get(document).on("click", this.slideInIf, this);
36711                 this.fireEvent("slideshow", this);
36712             },
36713             scope: this,
36714             block: true
36715         });
36716     },
36717
36718     afterSlideIn : function(){
36719         this.clearAutoHide();
36720         this.isSlid = false;
36721         this.clearMonitor();
36722         this.el.setStyle("z-index", "");
36723         if(this.collapseBtn){
36724             this.collapseBtn.show();
36725         }
36726         this.closeBtn.setStyle('display', this.closeBtnState);
36727         if(this.stickBtn){
36728             this.stickBtn.hide();
36729         }
36730         this.fireEvent("slidehide", this);
36731     },
36732
36733     slideIn : function(cb){
36734         if(!this.isSlid || this.el.hasActiveFx()){
36735             Roo.callback(cb);
36736             return;
36737         }
36738         this.isSlid = false;
36739         this.beforeSlide();
36740         this.el.slideOut(this.getSlideAnchor(), {
36741             callback: function(){
36742                 this.el.setLeftTop(-10000, -10000);
36743                 this.afterSlide();
36744                 this.afterSlideIn();
36745                 Roo.callback(cb);
36746             },
36747             scope: this,
36748             block: true
36749         });
36750     },
36751     
36752     slideInIf : function(e){
36753         if(!e.within(this.el)){
36754             this.slideIn();
36755         }
36756     },
36757
36758     animateCollapse : function(){
36759         this.beforeSlide();
36760         this.el.setStyle("z-index", 20000);
36761         var anchor = this.getSlideAnchor();
36762         this.el.slideOut(anchor, {
36763             callback : function(){
36764                 this.el.setStyle("z-index", "");
36765                 this.collapsedEl.slideIn(anchor, {duration:.3});
36766                 this.afterSlide();
36767                 this.el.setLocation(-10000,-10000);
36768                 this.el.hide();
36769                 this.fireEvent("collapsed", this);
36770             },
36771             scope: this,
36772             block: true
36773         });
36774     },
36775
36776     animateExpand : function(){
36777         this.beforeSlide();
36778         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36779         this.el.setStyle("z-index", 20000);
36780         this.collapsedEl.hide({
36781             duration:.1
36782         });
36783         this.el.slideIn(this.getSlideAnchor(), {
36784             callback : function(){
36785                 this.el.setStyle("z-index", "");
36786                 this.afterSlide();
36787                 if(this.split){
36788                     this.split.el.show();
36789                 }
36790                 this.fireEvent("invalidated", this);
36791                 this.fireEvent("expanded", this);
36792             },
36793             scope: this,
36794             block: true
36795         });
36796     },
36797
36798     anchors : {
36799         "west" : "left",
36800         "east" : "right",
36801         "north" : "top",
36802         "south" : "bottom"
36803     },
36804
36805     sanchors : {
36806         "west" : "l",
36807         "east" : "r",
36808         "north" : "t",
36809         "south" : "b"
36810     },
36811
36812     canchors : {
36813         "west" : "tl-tr",
36814         "east" : "tr-tl",
36815         "north" : "tl-bl",
36816         "south" : "bl-tl"
36817     },
36818
36819     getAnchor : function(){
36820         return this.anchors[this.position];
36821     },
36822
36823     getCollapseAnchor : function(){
36824         return this.canchors[this.position];
36825     },
36826
36827     getSlideAnchor : function(){
36828         return this.sanchors[this.position];
36829     },
36830
36831     getAlignAdj : function(){
36832         var cm = this.cmargins;
36833         switch(this.position){
36834             case "west":
36835                 return [0, 0];
36836             break;
36837             case "east":
36838                 return [0, 0];
36839             break;
36840             case "north":
36841                 return [0, 0];
36842             break;
36843             case "south":
36844                 return [0, 0];
36845             break;
36846         }
36847     },
36848
36849     getExpandAdj : function(){
36850         var c = this.collapsedEl, cm = this.cmargins;
36851         switch(this.position){
36852             case "west":
36853                 return [-(cm.right+c.getWidth()+cm.left), 0];
36854             break;
36855             case "east":
36856                 return [cm.right+c.getWidth()+cm.left, 0];
36857             break;
36858             case "north":
36859                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36860             break;
36861             case "south":
36862                 return [0, cm.top+cm.bottom+c.getHeight()];
36863             break;
36864         }
36865     }
36866 });/*
36867  * Based on:
36868  * Ext JS Library 1.1.1
36869  * Copyright(c) 2006-2007, Ext JS, LLC.
36870  *
36871  * Originally Released Under LGPL - original licence link has changed is not relivant.
36872  *
36873  * Fork - LGPL
36874  * <script type="text/javascript">
36875  */
36876 /*
36877  * These classes are private internal classes
36878  */
36879 Roo.bootstrap.layout.Center = function(config){
36880     config.region = "center";
36881     Roo.bootstrap.layout.Region.call(this, config);
36882     this.visible = true;
36883     this.minWidth = config.minWidth || 20;
36884     this.minHeight = config.minHeight || 20;
36885 };
36886
36887 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36888     hide : function(){
36889         // center panel can't be hidden
36890     },
36891     
36892     show : function(){
36893         // center panel can't be hidden
36894     },
36895     
36896     getMinWidth: function(){
36897         return this.minWidth;
36898     },
36899     
36900     getMinHeight: function(){
36901         return this.minHeight;
36902     }
36903 });
36904
36905
36906
36907
36908  
36909
36910
36911
36912
36913
36914 Roo.bootstrap.layout.North = function(config)
36915 {
36916     config.region = 'north';
36917     config.cursor = 'n-resize';
36918     
36919     Roo.bootstrap.layout.Split.call(this, config);
36920     
36921     
36922     if(this.split){
36923         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36924         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36925         this.split.el.addClass("roo-layout-split-v");
36926     }
36927     var size = config.initialSize || config.height;
36928     if(typeof size != "undefined"){
36929         this.el.setHeight(size);
36930     }
36931 };
36932 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36933 {
36934     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36935     
36936     
36937     
36938     getBox : function(){
36939         if(this.collapsed){
36940             return this.collapsedEl.getBox();
36941         }
36942         var box = this.el.getBox();
36943         if(this.split){
36944             box.height += this.split.el.getHeight();
36945         }
36946         return box;
36947     },
36948     
36949     updateBox : function(box){
36950         if(this.split && !this.collapsed){
36951             box.height -= this.split.el.getHeight();
36952             this.split.el.setLeft(box.x);
36953             this.split.el.setTop(box.y+box.height);
36954             this.split.el.setWidth(box.width);
36955         }
36956         if(this.collapsed){
36957             this.updateBody(box.width, null);
36958         }
36959         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36960     }
36961 });
36962
36963
36964
36965
36966
36967 Roo.bootstrap.layout.South = function(config){
36968     config.region = 'south';
36969     config.cursor = 's-resize';
36970     Roo.bootstrap.layout.Split.call(this, config);
36971     if(this.split){
36972         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36973         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36974         this.split.el.addClass("roo-layout-split-v");
36975     }
36976     var size = config.initialSize || config.height;
36977     if(typeof size != "undefined"){
36978         this.el.setHeight(size);
36979     }
36980 };
36981
36982 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36983     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36984     getBox : function(){
36985         if(this.collapsed){
36986             return this.collapsedEl.getBox();
36987         }
36988         var box = this.el.getBox();
36989         if(this.split){
36990             var sh = this.split.el.getHeight();
36991             box.height += sh;
36992             box.y -= sh;
36993         }
36994         return box;
36995     },
36996     
36997     updateBox : function(box){
36998         if(this.split && !this.collapsed){
36999             var sh = this.split.el.getHeight();
37000             box.height -= sh;
37001             box.y += sh;
37002             this.split.el.setLeft(box.x);
37003             this.split.el.setTop(box.y-sh);
37004             this.split.el.setWidth(box.width);
37005         }
37006         if(this.collapsed){
37007             this.updateBody(box.width, null);
37008         }
37009         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37010     }
37011 });
37012
37013 Roo.bootstrap.layout.East = function(config){
37014     config.region = "east";
37015     config.cursor = "e-resize";
37016     Roo.bootstrap.layout.Split.call(this, config);
37017     if(this.split){
37018         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37019         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37020         this.split.el.addClass("roo-layout-split-h");
37021     }
37022     var size = config.initialSize || config.width;
37023     if(typeof size != "undefined"){
37024         this.el.setWidth(size);
37025     }
37026 };
37027 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37028     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37029     getBox : function(){
37030         if(this.collapsed){
37031             return this.collapsedEl.getBox();
37032         }
37033         var box = this.el.getBox();
37034         if(this.split){
37035             var sw = this.split.el.getWidth();
37036             box.width += sw;
37037             box.x -= sw;
37038         }
37039         return box;
37040     },
37041
37042     updateBox : function(box){
37043         if(this.split && !this.collapsed){
37044             var sw = this.split.el.getWidth();
37045             box.width -= sw;
37046             this.split.el.setLeft(box.x);
37047             this.split.el.setTop(box.y);
37048             this.split.el.setHeight(box.height);
37049             box.x += sw;
37050         }
37051         if(this.collapsed){
37052             this.updateBody(null, box.height);
37053         }
37054         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37055     }
37056 });
37057
37058 Roo.bootstrap.layout.West = function(config){
37059     config.region = "west";
37060     config.cursor = "w-resize";
37061     
37062     Roo.bootstrap.layout.Split.call(this, config);
37063     if(this.split){
37064         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37065         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37066         this.split.el.addClass("roo-layout-split-h");
37067     }
37068     
37069 };
37070 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37071     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37072     
37073     onRender: function(ctr, pos)
37074     {
37075         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37076         var size = this.config.initialSize || this.config.width;
37077         if(typeof size != "undefined"){
37078             this.el.setWidth(size);
37079         }
37080     },
37081     
37082     getBox : function(){
37083         if(this.collapsed){
37084             return this.collapsedEl.getBox();
37085         }
37086         var box = this.el.getBox();
37087         if(this.split){
37088             box.width += this.split.el.getWidth();
37089         }
37090         return box;
37091     },
37092     
37093     updateBox : function(box){
37094         if(this.split && !this.collapsed){
37095             var sw = this.split.el.getWidth();
37096             box.width -= sw;
37097             this.split.el.setLeft(box.x+box.width);
37098             this.split.el.setTop(box.y);
37099             this.split.el.setHeight(box.height);
37100         }
37101         if(this.collapsed){
37102             this.updateBody(null, box.height);
37103         }
37104         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37105     }
37106 });
37107 Roo.namespace("Roo.bootstrap.panel");/*
37108  * Based on:
37109  * Ext JS Library 1.1.1
37110  * Copyright(c) 2006-2007, Ext JS, LLC.
37111  *
37112  * Originally Released Under LGPL - original licence link has changed is not relivant.
37113  *
37114  * Fork - LGPL
37115  * <script type="text/javascript">
37116  */
37117 /**
37118  * @class Roo.ContentPanel
37119  * @extends Roo.util.Observable
37120  * A basic ContentPanel element.
37121  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37122  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37123  * @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
37124  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37125  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37126  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37127  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37128  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37129  * @cfg {String} title          The title for this panel
37130  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37131  * @cfg {String} url            Calls {@link #setUrl} with this value
37132  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37133  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37134  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37135  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37136  * @cfg {Boolean} badges render the badges
37137
37138  * @constructor
37139  * Create a new ContentPanel.
37140  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37141  * @param {String/Object} config A string to set only the title or a config object
37142  * @param {String} content (optional) Set the HTML content for this panel
37143  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37144  */
37145 Roo.bootstrap.panel.Content = function( config){
37146     
37147     this.tpl = config.tpl || false;
37148     
37149     var el = config.el;
37150     var content = config.content;
37151
37152     if(config.autoCreate){ // xtype is available if this is called from factory
37153         el = Roo.id();
37154     }
37155     this.el = Roo.get(el);
37156     if(!this.el && config && config.autoCreate){
37157         if(typeof config.autoCreate == "object"){
37158             if(!config.autoCreate.id){
37159                 config.autoCreate.id = config.id||el;
37160             }
37161             this.el = Roo.DomHelper.append(document.body,
37162                         config.autoCreate, true);
37163         }else{
37164             var elcfg =  {   tag: "div",
37165                             cls: "roo-layout-inactive-content",
37166                             id: config.id||el
37167                             };
37168             if (config.html) {
37169                 elcfg.html = config.html;
37170                 
37171             }
37172                         
37173             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37174         }
37175     } 
37176     this.closable = false;
37177     this.loaded = false;
37178     this.active = false;
37179    
37180       
37181     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37182         
37183         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37184         
37185         this.wrapEl = this.el; //this.el.wrap();
37186         var ti = [];
37187         if (config.toolbar.items) {
37188             ti = config.toolbar.items ;
37189             delete config.toolbar.items ;
37190         }
37191         
37192         var nitems = [];
37193         this.toolbar.render(this.wrapEl, 'before');
37194         for(var i =0;i < ti.length;i++) {
37195           //  Roo.log(['add child', items[i]]);
37196             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37197         }
37198         this.toolbar.items = nitems;
37199         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37200         delete config.toolbar;
37201         
37202     }
37203     /*
37204     // xtype created footer. - not sure if will work as we normally have to render first..
37205     if (this.footer && !this.footer.el && this.footer.xtype) {
37206         if (!this.wrapEl) {
37207             this.wrapEl = this.el.wrap();
37208         }
37209     
37210         this.footer.container = this.wrapEl.createChild();
37211          
37212         this.footer = Roo.factory(this.footer, Roo);
37213         
37214     }
37215     */
37216     
37217      if(typeof config == "string"){
37218         this.title = config;
37219     }else{
37220         Roo.apply(this, config);
37221     }
37222     
37223     if(this.resizeEl){
37224         this.resizeEl = Roo.get(this.resizeEl, true);
37225     }else{
37226         this.resizeEl = this.el;
37227     }
37228     // handle view.xtype
37229     
37230  
37231     
37232     
37233     this.addEvents({
37234         /**
37235          * @event activate
37236          * Fires when this panel is activated. 
37237          * @param {Roo.ContentPanel} this
37238          */
37239         "activate" : true,
37240         /**
37241          * @event deactivate
37242          * Fires when this panel is activated. 
37243          * @param {Roo.ContentPanel} this
37244          */
37245         "deactivate" : true,
37246
37247         /**
37248          * @event resize
37249          * Fires when this panel is resized if fitToFrame is true.
37250          * @param {Roo.ContentPanel} this
37251          * @param {Number} width The width after any component adjustments
37252          * @param {Number} height The height after any component adjustments
37253          */
37254         "resize" : true,
37255         
37256          /**
37257          * @event render
37258          * Fires when this tab is created
37259          * @param {Roo.ContentPanel} this
37260          */
37261         "render" : true
37262         
37263         
37264         
37265     });
37266     
37267
37268     
37269     
37270     if(this.autoScroll){
37271         this.resizeEl.setStyle("overflow", "auto");
37272     } else {
37273         // fix randome scrolling
37274         //this.el.on('scroll', function() {
37275         //    Roo.log('fix random scolling');
37276         //    this.scrollTo('top',0); 
37277         //});
37278     }
37279     content = content || this.content;
37280     if(content){
37281         this.setContent(content);
37282     }
37283     if(config && config.url){
37284         this.setUrl(this.url, this.params, this.loadOnce);
37285     }
37286     
37287     
37288     
37289     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37290     
37291     if (this.view && typeof(this.view.xtype) != 'undefined') {
37292         this.view.el = this.el.appendChild(document.createElement("div"));
37293         this.view = Roo.factory(this.view); 
37294         this.view.render  &&  this.view.render(false, '');  
37295     }
37296     
37297     
37298     this.fireEvent('render', this);
37299 };
37300
37301 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37302     
37303     tabTip : '',
37304     
37305     setRegion : function(region){
37306         this.region = region;
37307         this.setActiveClass(region && !this.background);
37308     },
37309     
37310     
37311     setActiveClass: function(state)
37312     {
37313         if(state){
37314            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37315            this.el.setStyle('position','relative');
37316         }else{
37317            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37318            this.el.setStyle('position', 'absolute');
37319         } 
37320     },
37321     
37322     /**
37323      * Returns the toolbar for this Panel if one was configured. 
37324      * @return {Roo.Toolbar} 
37325      */
37326     getToolbar : function(){
37327         return this.toolbar;
37328     },
37329     
37330     setActiveState : function(active)
37331     {
37332         this.active = active;
37333         this.setActiveClass(active);
37334         if(!active){
37335             if(this.fireEvent("deactivate", this) === false){
37336                 return false;
37337             }
37338             return true;
37339         }
37340         this.fireEvent("activate", this);
37341         return true;
37342     },
37343     /**
37344      * Updates this panel's element
37345      * @param {String} content The new content
37346      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37347     */
37348     setContent : function(content, loadScripts){
37349         this.el.update(content, loadScripts);
37350     },
37351
37352     ignoreResize : function(w, h){
37353         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37354             return true;
37355         }else{
37356             this.lastSize = {width: w, height: h};
37357             return false;
37358         }
37359     },
37360     /**
37361      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37362      * @return {Roo.UpdateManager} The UpdateManager
37363      */
37364     getUpdateManager : function(){
37365         return this.el.getUpdateManager();
37366     },
37367      /**
37368      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37369      * @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:
37370 <pre><code>
37371 panel.load({
37372     url: "your-url.php",
37373     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37374     callback: yourFunction,
37375     scope: yourObject, //(optional scope)
37376     discardUrl: false,
37377     nocache: false,
37378     text: "Loading...",
37379     timeout: 30,
37380     scripts: false
37381 });
37382 </code></pre>
37383      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37384      * 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.
37385      * @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}
37386      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37387      * @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.
37388      * @return {Roo.ContentPanel} this
37389      */
37390     load : function(){
37391         var um = this.el.getUpdateManager();
37392         um.update.apply(um, arguments);
37393         return this;
37394     },
37395
37396
37397     /**
37398      * 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.
37399      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37400      * @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)
37401      * @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)
37402      * @return {Roo.UpdateManager} The UpdateManager
37403      */
37404     setUrl : function(url, params, loadOnce){
37405         if(this.refreshDelegate){
37406             this.removeListener("activate", this.refreshDelegate);
37407         }
37408         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37409         this.on("activate", this.refreshDelegate);
37410         return this.el.getUpdateManager();
37411     },
37412     
37413     _handleRefresh : function(url, params, loadOnce){
37414         if(!loadOnce || !this.loaded){
37415             var updater = this.el.getUpdateManager();
37416             updater.update(url, params, this._setLoaded.createDelegate(this));
37417         }
37418     },
37419     
37420     _setLoaded : function(){
37421         this.loaded = true;
37422     }, 
37423     
37424     /**
37425      * Returns this panel's id
37426      * @return {String} 
37427      */
37428     getId : function(){
37429         return this.el.id;
37430     },
37431     
37432     /** 
37433      * Returns this panel's element - used by regiosn to add.
37434      * @return {Roo.Element} 
37435      */
37436     getEl : function(){
37437         return this.wrapEl || this.el;
37438     },
37439     
37440    
37441     
37442     adjustForComponents : function(width, height)
37443     {
37444         //Roo.log('adjustForComponents ');
37445         if(this.resizeEl != this.el){
37446             width -= this.el.getFrameWidth('lr');
37447             height -= this.el.getFrameWidth('tb');
37448         }
37449         if(this.toolbar){
37450             var te = this.toolbar.getEl();
37451             te.setWidth(width);
37452             height -= te.getHeight();
37453         }
37454         if(this.footer){
37455             var te = this.footer.getEl();
37456             te.setWidth(width);
37457             height -= te.getHeight();
37458         }
37459         
37460         
37461         if(this.adjustments){
37462             width += this.adjustments[0];
37463             height += this.adjustments[1];
37464         }
37465         return {"width": width, "height": height};
37466     },
37467     
37468     setSize : function(width, height){
37469         if(this.fitToFrame && !this.ignoreResize(width, height)){
37470             if(this.fitContainer && this.resizeEl != this.el){
37471                 this.el.setSize(width, height);
37472             }
37473             var size = this.adjustForComponents(width, height);
37474             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37475             this.fireEvent('resize', this, size.width, size.height);
37476         }
37477     },
37478     
37479     /**
37480      * Returns this panel's title
37481      * @return {String} 
37482      */
37483     getTitle : function(){
37484         
37485         if (typeof(this.title) != 'object') {
37486             return this.title;
37487         }
37488         
37489         var t = '';
37490         for (var k in this.title) {
37491             if (!this.title.hasOwnProperty(k)) {
37492                 continue;
37493             }
37494             
37495             if (k.indexOf('-') >= 0) {
37496                 var s = k.split('-');
37497                 for (var i = 0; i<s.length; i++) {
37498                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37499                 }
37500             } else {
37501                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37502             }
37503         }
37504         return t;
37505     },
37506     
37507     /**
37508      * Set this panel's title
37509      * @param {String} title
37510      */
37511     setTitle : function(title){
37512         this.title = title;
37513         if(this.region){
37514             this.region.updatePanelTitle(this, title);
37515         }
37516     },
37517     
37518     /**
37519      * Returns true is this panel was configured to be closable
37520      * @return {Boolean} 
37521      */
37522     isClosable : function(){
37523         return this.closable;
37524     },
37525     
37526     beforeSlide : function(){
37527         this.el.clip();
37528         this.resizeEl.clip();
37529     },
37530     
37531     afterSlide : function(){
37532         this.el.unclip();
37533         this.resizeEl.unclip();
37534     },
37535     
37536     /**
37537      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37538      *   Will fail silently if the {@link #setUrl} method has not been called.
37539      *   This does not activate the panel, just updates its content.
37540      */
37541     refresh : function(){
37542         if(this.refreshDelegate){
37543            this.loaded = false;
37544            this.refreshDelegate();
37545         }
37546     },
37547     
37548     /**
37549      * Destroys this panel
37550      */
37551     destroy : function(){
37552         this.el.removeAllListeners();
37553         var tempEl = document.createElement("span");
37554         tempEl.appendChild(this.el.dom);
37555         tempEl.innerHTML = "";
37556         this.el.remove();
37557         this.el = null;
37558     },
37559     
37560     /**
37561      * form - if the content panel contains a form - this is a reference to it.
37562      * @type {Roo.form.Form}
37563      */
37564     form : false,
37565     /**
37566      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37567      *    This contains a reference to it.
37568      * @type {Roo.View}
37569      */
37570     view : false,
37571     
37572       /**
37573      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37574      * <pre><code>
37575
37576 layout.addxtype({
37577        xtype : 'Form',
37578        items: [ .... ]
37579    }
37580 );
37581
37582 </code></pre>
37583      * @param {Object} cfg Xtype definition of item to add.
37584      */
37585     
37586     
37587     getChildContainer: function () {
37588         return this.getEl();
37589     }
37590     
37591     
37592     /*
37593         var  ret = new Roo.factory(cfg);
37594         return ret;
37595         
37596         
37597         // add form..
37598         if (cfg.xtype.match(/^Form$/)) {
37599             
37600             var el;
37601             //if (this.footer) {
37602             //    el = this.footer.container.insertSibling(false, 'before');
37603             //} else {
37604                 el = this.el.createChild();
37605             //}
37606
37607             this.form = new  Roo.form.Form(cfg);
37608             
37609             
37610             if ( this.form.allItems.length) {
37611                 this.form.render(el.dom);
37612             }
37613             return this.form;
37614         }
37615         // should only have one of theses..
37616         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37617             // views.. should not be just added - used named prop 'view''
37618             
37619             cfg.el = this.el.appendChild(document.createElement("div"));
37620             // factory?
37621             
37622             var ret = new Roo.factory(cfg);
37623              
37624              ret.render && ret.render(false, ''); // render blank..
37625             this.view = ret;
37626             return ret;
37627         }
37628         return false;
37629     }
37630     \*/
37631 });
37632  
37633 /**
37634  * @class Roo.bootstrap.panel.Grid
37635  * @extends Roo.bootstrap.panel.Content
37636  * @constructor
37637  * Create a new GridPanel.
37638  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37639  * @param {Object} config A the config object
37640   
37641  */
37642
37643
37644
37645 Roo.bootstrap.panel.Grid = function(config)
37646 {
37647     
37648       
37649     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37650         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37651
37652     config.el = this.wrapper;
37653     //this.el = this.wrapper;
37654     
37655       if (config.container) {
37656         // ctor'ed from a Border/panel.grid
37657         
37658         
37659         this.wrapper.setStyle("overflow", "hidden");
37660         this.wrapper.addClass('roo-grid-container');
37661
37662     }
37663     
37664     
37665     if(config.toolbar){
37666         var tool_el = this.wrapper.createChild();    
37667         this.toolbar = Roo.factory(config.toolbar);
37668         var ti = [];
37669         if (config.toolbar.items) {
37670             ti = config.toolbar.items ;
37671             delete config.toolbar.items ;
37672         }
37673         
37674         var nitems = [];
37675         this.toolbar.render(tool_el);
37676         for(var i =0;i < ti.length;i++) {
37677           //  Roo.log(['add child', items[i]]);
37678             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37679         }
37680         this.toolbar.items = nitems;
37681         
37682         delete config.toolbar;
37683     }
37684     
37685     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37686     config.grid.scrollBody = true;;
37687     config.grid.monitorWindowResize = false; // turn off autosizing
37688     config.grid.autoHeight = false;
37689     config.grid.autoWidth = false;
37690     
37691     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37692     
37693     if (config.background) {
37694         // render grid on panel activation (if panel background)
37695         this.on('activate', function(gp) {
37696             if (!gp.grid.rendered) {
37697                 gp.grid.render(this.wrapper);
37698                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37699             }
37700         });
37701             
37702     } else {
37703         this.grid.render(this.wrapper);
37704         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37705
37706     }
37707     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37708     // ??? needed ??? config.el = this.wrapper;
37709     
37710     
37711     
37712   
37713     // xtype created footer. - not sure if will work as we normally have to render first..
37714     if (this.footer && !this.footer.el && this.footer.xtype) {
37715         
37716         var ctr = this.grid.getView().getFooterPanel(true);
37717         this.footer.dataSource = this.grid.dataSource;
37718         this.footer = Roo.factory(this.footer, Roo);
37719         this.footer.render(ctr);
37720         
37721     }
37722     
37723     
37724     
37725     
37726      
37727 };
37728
37729 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37730     getId : function(){
37731         return this.grid.id;
37732     },
37733     
37734     /**
37735      * Returns the grid for this panel
37736      * @return {Roo.bootstrap.Table} 
37737      */
37738     getGrid : function(){
37739         return this.grid;    
37740     },
37741     
37742     setSize : function(width, height){
37743         if(!this.ignoreResize(width, height)){
37744             var grid = this.grid;
37745             var size = this.adjustForComponents(width, height);
37746             var gridel = grid.getGridEl();
37747             gridel.setSize(size.width, size.height);
37748             /*
37749             var thd = grid.getGridEl().select('thead',true).first();
37750             var tbd = grid.getGridEl().select('tbody', true).first();
37751             if (tbd) {
37752                 tbd.setSize(width, height - thd.getHeight());
37753             }
37754             */
37755             grid.autoSize();
37756         }
37757     },
37758      
37759     
37760     
37761     beforeSlide : function(){
37762         this.grid.getView().scroller.clip();
37763     },
37764     
37765     afterSlide : function(){
37766         this.grid.getView().scroller.unclip();
37767     },
37768     
37769     destroy : function(){
37770         this.grid.destroy();
37771         delete this.grid;
37772         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37773     }
37774 });
37775
37776 /**
37777  * @class Roo.bootstrap.panel.Nest
37778  * @extends Roo.bootstrap.panel.Content
37779  * @constructor
37780  * Create a new Panel, that can contain a layout.Border.
37781  * 
37782  * 
37783  * @param {Roo.BorderLayout} layout The layout for this panel
37784  * @param {String/Object} config A string to set only the title or a config object
37785  */
37786 Roo.bootstrap.panel.Nest = function(config)
37787 {
37788     // construct with only one argument..
37789     /* FIXME - implement nicer consturctors
37790     if (layout.layout) {
37791         config = layout;
37792         layout = config.layout;
37793         delete config.layout;
37794     }
37795     if (layout.xtype && !layout.getEl) {
37796         // then layout needs constructing..
37797         layout = Roo.factory(layout, Roo);
37798     }
37799     */
37800     
37801     config.el =  config.layout.getEl();
37802     
37803     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37804     
37805     config.layout.monitorWindowResize = false; // turn off autosizing
37806     this.layout = config.layout;
37807     this.layout.getEl().addClass("roo-layout-nested-layout");
37808     
37809     
37810     
37811     
37812 };
37813
37814 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37815
37816     setSize : function(width, height){
37817         if(!this.ignoreResize(width, height)){
37818             var size = this.adjustForComponents(width, height);
37819             var el = this.layout.getEl();
37820             if (size.height < 1) {
37821                 el.setWidth(size.width);   
37822             } else {
37823                 el.setSize(size.width, size.height);
37824             }
37825             var touch = el.dom.offsetWidth;
37826             this.layout.layout();
37827             // ie requires a double layout on the first pass
37828             if(Roo.isIE && !this.initialized){
37829                 this.initialized = true;
37830                 this.layout.layout();
37831             }
37832         }
37833     },
37834     
37835     // activate all subpanels if not currently active..
37836     
37837     setActiveState : function(active){
37838         this.active = active;
37839         this.setActiveClass(active);
37840         
37841         if(!active){
37842             this.fireEvent("deactivate", this);
37843             return;
37844         }
37845         
37846         this.fireEvent("activate", this);
37847         // not sure if this should happen before or after..
37848         if (!this.layout) {
37849             return; // should not happen..
37850         }
37851         var reg = false;
37852         for (var r in this.layout.regions) {
37853             reg = this.layout.getRegion(r);
37854             if (reg.getActivePanel()) {
37855                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37856                 reg.setActivePanel(reg.getActivePanel());
37857                 continue;
37858             }
37859             if (!reg.panels.length) {
37860                 continue;
37861             }
37862             reg.showPanel(reg.getPanel(0));
37863         }
37864         
37865         
37866         
37867         
37868     },
37869     
37870     /**
37871      * Returns the nested BorderLayout for this panel
37872      * @return {Roo.BorderLayout} 
37873      */
37874     getLayout : function(){
37875         return this.layout;
37876     },
37877     
37878      /**
37879      * Adds a xtype elements to the layout of the nested panel
37880      * <pre><code>
37881
37882 panel.addxtype({
37883        xtype : 'ContentPanel',
37884        region: 'west',
37885        items: [ .... ]
37886    }
37887 );
37888
37889 panel.addxtype({
37890         xtype : 'NestedLayoutPanel',
37891         region: 'west',
37892         layout: {
37893            center: { },
37894            west: { }   
37895         },
37896         items : [ ... list of content panels or nested layout panels.. ]
37897    }
37898 );
37899 </code></pre>
37900      * @param {Object} cfg Xtype definition of item to add.
37901      */
37902     addxtype : function(cfg) {
37903         return this.layout.addxtype(cfg);
37904     
37905     }
37906 });        /*
37907  * Based on:
37908  * Ext JS Library 1.1.1
37909  * Copyright(c) 2006-2007, Ext JS, LLC.
37910  *
37911  * Originally Released Under LGPL - original licence link has changed is not relivant.
37912  *
37913  * Fork - LGPL
37914  * <script type="text/javascript">
37915  */
37916 /**
37917  * @class Roo.TabPanel
37918  * @extends Roo.util.Observable
37919  * A lightweight tab container.
37920  * <br><br>
37921  * Usage:
37922  * <pre><code>
37923 // basic tabs 1, built from existing content
37924 var tabs = new Roo.TabPanel("tabs1");
37925 tabs.addTab("script", "View Script");
37926 tabs.addTab("markup", "View Markup");
37927 tabs.activate("script");
37928
37929 // more advanced tabs, built from javascript
37930 var jtabs = new Roo.TabPanel("jtabs");
37931 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37932
37933 // set up the UpdateManager
37934 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37935 var updater = tab2.getUpdateManager();
37936 updater.setDefaultUrl("ajax1.htm");
37937 tab2.on('activate', updater.refresh, updater, true);
37938
37939 // Use setUrl for Ajax loading
37940 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37941 tab3.setUrl("ajax2.htm", null, true);
37942
37943 // Disabled tab
37944 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37945 tab4.disable();
37946
37947 jtabs.activate("jtabs-1");
37948  * </code></pre>
37949  * @constructor
37950  * Create a new TabPanel.
37951  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37952  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37953  */
37954 Roo.bootstrap.panel.Tabs = function(config){
37955     /**
37956     * The container element for this TabPanel.
37957     * @type Roo.Element
37958     */
37959     this.el = Roo.get(config.el);
37960     delete config.el;
37961     if(config){
37962         if(typeof config == "boolean"){
37963             this.tabPosition = config ? "bottom" : "top";
37964         }else{
37965             Roo.apply(this, config);
37966         }
37967     }
37968     
37969     if(this.tabPosition == "bottom"){
37970         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37971         this.el.addClass("roo-tabs-bottom");
37972     }
37973     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37974     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37975     this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
37976     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37977     if(Roo.isIE){
37978         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37979     }
37980     if(this.tabPosition != "bottom"){
37981         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37982          * @type Roo.Element
37983          */
37984         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37985         this.el.addClass("roo-tabs-top");
37986     }
37987     this.items = [];
37988
37989     this.bodyEl.setStyle("position", "relative");
37990
37991     this.active = null;
37992     this.activateDelegate = this.activate.createDelegate(this);
37993
37994     this.addEvents({
37995         /**
37996          * @event tabchange
37997          * Fires when the active tab changes
37998          * @param {Roo.TabPanel} this
37999          * @param {Roo.TabPanelItem} activePanel The new active tab
38000          */
38001         "tabchange": true,
38002         /**
38003          * @event beforetabchange
38004          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38005          * @param {Roo.TabPanel} this
38006          * @param {Object} e Set cancel to true on this object to cancel the tab change
38007          * @param {Roo.TabPanelItem} tab The tab being changed to
38008          */
38009         "beforetabchange" : true
38010     });
38011
38012     Roo.EventManager.onWindowResize(this.onResize, this);
38013     this.cpad = this.el.getPadding("lr");
38014     this.hiddenCount = 0;
38015
38016
38017     // toolbar on the tabbar support...
38018     if (this.toolbar) {
38019         alert("no toolbar support yet");
38020         this.toolbar  = false;
38021         /*
38022         var tcfg = this.toolbar;
38023         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38024         this.toolbar = new Roo.Toolbar(tcfg);
38025         if (Roo.isSafari) {
38026             var tbl = tcfg.container.child('table', true);
38027             tbl.setAttribute('width', '100%');
38028         }
38029         */
38030         
38031     }
38032    
38033
38034
38035     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38036 };
38037
38038 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38039     /*
38040      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38041      */
38042     tabPosition : "top",
38043     /*
38044      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38045      */
38046     currentTabWidth : 0,
38047     /*
38048      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38049      */
38050     minTabWidth : 40,
38051     /*
38052      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38053      */
38054     maxTabWidth : 250,
38055     /*
38056      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38057      */
38058     preferredTabWidth : 175,
38059     /*
38060      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38061      */
38062     resizeTabs : false,
38063     /*
38064      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38065      */
38066     monitorResize : true,
38067     /*
38068      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38069      */
38070     toolbar : false,
38071
38072     /**
38073      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38074      * @param {String} id The id of the div to use <b>or create</b>
38075      * @param {String} text The text for the tab
38076      * @param {String} content (optional) Content to put in the TabPanelItem body
38077      * @param {Boolean} closable (optional) True to create a close icon on the tab
38078      * @return {Roo.TabPanelItem} The created TabPanelItem
38079      */
38080     addTab : function(id, text, content, closable, tpl)
38081     {
38082         var item = new Roo.bootstrap.panel.TabItem({
38083             panel: this,
38084             id : id,
38085             text : text,
38086             closable : closable,
38087             tpl : tpl
38088         });
38089         this.addTabItem(item);
38090         if(content){
38091             item.setContent(content);
38092         }
38093         return item;
38094     },
38095
38096     /**
38097      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38098      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38099      * @return {Roo.TabPanelItem}
38100      */
38101     getTab : function(id){
38102         return this.items[id];
38103     },
38104
38105     /**
38106      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38107      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38108      */
38109     hideTab : function(id){
38110         var t = this.items[id];
38111         if(!t.isHidden()){
38112            t.setHidden(true);
38113            this.hiddenCount++;
38114            this.autoSizeTabs();
38115         }
38116     },
38117
38118     /**
38119      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38120      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38121      */
38122     unhideTab : function(id){
38123         var t = this.items[id];
38124         if(t.isHidden()){
38125            t.setHidden(false);
38126            this.hiddenCount--;
38127            this.autoSizeTabs();
38128         }
38129     },
38130
38131     /**
38132      * Adds an existing {@link Roo.TabPanelItem}.
38133      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38134      */
38135     addTabItem : function(item)
38136     {
38137         this.items[item.id] = item;
38138         this.items.push(item);
38139         this.autoSizeTabs();
38140       //  if(this.resizeTabs){
38141     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38142   //         this.autoSizeTabs();
38143 //        }else{
38144 //            item.autoSize();
38145        // }
38146     },
38147
38148     /**
38149      * Removes a {@link Roo.TabPanelItem}.
38150      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38151      */
38152     removeTab : function(id){
38153         var items = this.items;
38154         var tab = items[id];
38155         if(!tab) { return; }
38156         var index = items.indexOf(tab);
38157         if(this.active == tab && items.length > 1){
38158             var newTab = this.getNextAvailable(index);
38159             if(newTab) {
38160                 newTab.activate();
38161             }
38162         }
38163         this.stripEl.dom.removeChild(tab.pnode.dom);
38164         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38165             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38166         }
38167         items.splice(index, 1);
38168         delete this.items[tab.id];
38169         tab.fireEvent("close", tab);
38170         tab.purgeListeners();
38171         this.autoSizeTabs();
38172     },
38173
38174     getNextAvailable : function(start){
38175         var items = this.items;
38176         var index = start;
38177         // look for a next tab that will slide over to
38178         // replace the one being removed
38179         while(index < items.length){
38180             var item = items[++index];
38181             if(item && !item.isHidden()){
38182                 return item;
38183             }
38184         }
38185         // if one isn't found select the previous tab (on the left)
38186         index = start;
38187         while(index >= 0){
38188             var item = items[--index];
38189             if(item && !item.isHidden()){
38190                 return item;
38191             }
38192         }
38193         return null;
38194     },
38195
38196     /**
38197      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38198      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38199      */
38200     disableTab : function(id){
38201         var tab = this.items[id];
38202         if(tab && this.active != tab){
38203             tab.disable();
38204         }
38205     },
38206
38207     /**
38208      * Enables a {@link Roo.TabPanelItem} that is disabled.
38209      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38210      */
38211     enableTab : function(id){
38212         var tab = this.items[id];
38213         tab.enable();
38214     },
38215
38216     /**
38217      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38218      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38219      * @return {Roo.TabPanelItem} The TabPanelItem.
38220      */
38221     activate : function(id)
38222     {
38223         var tab = this.items[id];
38224         if(!tab){
38225             return null;
38226         }
38227         if(tab == this.active || tab.disabled){
38228             return tab;
38229         }
38230         var e = {};
38231         this.fireEvent("beforetabchange", this, e, tab);
38232         if(e.cancel !== true && !tab.disabled){
38233             if(this.active){
38234                 this.active.hide();
38235             }
38236             this.active = this.items[id];
38237             this.active.show();
38238             this.fireEvent("tabchange", this, this.active);
38239         }
38240         return tab;
38241     },
38242
38243     /**
38244      * Gets the active {@link Roo.TabPanelItem}.
38245      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38246      */
38247     getActiveTab : function(){
38248         return this.active;
38249     },
38250
38251     /**
38252      * Updates the tab body element to fit the height of the container element
38253      * for overflow scrolling
38254      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38255      */
38256     syncHeight : function(targetHeight){
38257         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38258         var bm = this.bodyEl.getMargins();
38259         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38260         this.bodyEl.setHeight(newHeight);
38261         return newHeight;
38262     },
38263
38264     onResize : function(){
38265         if(this.monitorResize){
38266             this.autoSizeTabs();
38267         }
38268     },
38269
38270     /**
38271      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38272      */
38273     beginUpdate : function(){
38274         this.updating = true;
38275     },
38276
38277     /**
38278      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38279      */
38280     endUpdate : function(){
38281         this.updating = false;
38282         this.autoSizeTabs();
38283     },
38284
38285     /**
38286      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38287      */
38288     autoSizeTabs : function()
38289     {
38290         var count = this.items.length;
38291         var vcount = count - this.hiddenCount;
38292         
38293         if (vcount < 2) {
38294             this.stripEl.hide();
38295         } else {
38296             this.stripEl.show();
38297         }
38298         
38299         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38300             return;
38301         }
38302         
38303         
38304         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38305         var availWidth = Math.floor(w / vcount);
38306         var b = this.stripBody;
38307         if(b.getWidth() > w){
38308             var tabs = this.items;
38309             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38310             if(availWidth < this.minTabWidth){
38311                 /*if(!this.sleft){    // incomplete scrolling code
38312                     this.createScrollButtons();
38313                 }
38314                 this.showScroll();
38315                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38316             }
38317         }else{
38318             if(this.currentTabWidth < this.preferredTabWidth){
38319                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38320             }
38321         }
38322     },
38323
38324     /**
38325      * Returns the number of tabs in this TabPanel.
38326      * @return {Number}
38327      */
38328      getCount : function(){
38329          return this.items.length;
38330      },
38331
38332     /**
38333      * Resizes all the tabs to the passed width
38334      * @param {Number} The new width
38335      */
38336     setTabWidth : function(width){
38337         this.currentTabWidth = width;
38338         for(var i = 0, len = this.items.length; i < len; i++) {
38339                 if(!this.items[i].isHidden()) {
38340                 this.items[i].setWidth(width);
38341             }
38342         }
38343     },
38344
38345     /**
38346      * Destroys this TabPanel
38347      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38348      */
38349     destroy : function(removeEl){
38350         Roo.EventManager.removeResizeListener(this.onResize, this);
38351         for(var i = 0, len = this.items.length; i < len; i++){
38352             this.items[i].purgeListeners();
38353         }
38354         if(removeEl === true){
38355             this.el.update("");
38356             this.el.remove();
38357         }
38358     },
38359     
38360     createStrip : function(container)
38361     {
38362         var strip = document.createElement("nav");
38363         strip.className = Roo.bootstrap.version == 4 ?
38364             "navbar-light bg-light" : 
38365             "navbar navbar-default"; //"x-tabs-wrap";
38366         container.appendChild(strip);
38367         return strip;
38368     },
38369     
38370     createStripList : function(strip)
38371     {
38372         // div wrapper for retard IE
38373         // returns the "tr" element.
38374         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38375         //'<div class="x-tabs-strip-wrap">'+
38376           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38377           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38378         return strip.firstChild; //.firstChild.firstChild.firstChild;
38379     },
38380     createBody : function(container)
38381     {
38382         var body = document.createElement("div");
38383         Roo.id(body, "tab-body");
38384         //Roo.fly(body).addClass("x-tabs-body");
38385         Roo.fly(body).addClass("tab-content");
38386         container.appendChild(body);
38387         return body;
38388     },
38389     createItemBody :function(bodyEl, id){
38390         var body = Roo.getDom(id);
38391         if(!body){
38392             body = document.createElement("div");
38393             body.id = id;
38394         }
38395         //Roo.fly(body).addClass("x-tabs-item-body");
38396         Roo.fly(body).addClass("tab-pane");
38397          bodyEl.insertBefore(body, bodyEl.firstChild);
38398         return body;
38399     },
38400     /** @private */
38401     createStripElements :  function(stripEl, text, closable, tpl)
38402     {
38403         var td = document.createElement("li"); // was td..
38404         td.className = 'nav-item';
38405         
38406         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38407         
38408         
38409         stripEl.appendChild(td);
38410         /*if(closable){
38411             td.className = "x-tabs-closable";
38412             if(!this.closeTpl){
38413                 this.closeTpl = new Roo.Template(
38414                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38415                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38416                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38417                 );
38418             }
38419             var el = this.closeTpl.overwrite(td, {"text": text});
38420             var close = el.getElementsByTagName("div")[0];
38421             var inner = el.getElementsByTagName("em")[0];
38422             return {"el": el, "close": close, "inner": inner};
38423         } else {
38424         */
38425         // not sure what this is..
38426 //            if(!this.tabTpl){
38427                 //this.tabTpl = new Roo.Template(
38428                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38429                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38430                 //);
38431 //                this.tabTpl = new Roo.Template(
38432 //                   '<a href="#">' +
38433 //                   '<span unselectable="on"' +
38434 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38435 //                            ' >{text}</span></a>'
38436 //                );
38437 //                
38438 //            }
38439
38440
38441             var template = tpl || this.tabTpl || false;
38442             
38443             if(!template){
38444                 template =  new Roo.Template(
38445                         Roo.bootstrap.version == 4 ? 
38446                             (
38447                                 '<a class="nav-link" href="#" unselectable="on"' +
38448                                      (this.disableTooltips ? '' : ' title="{text}"') +
38449                                      ' >{text}</a>'
38450                             ) : (
38451                                 '<a class="nav-link" href="#">' +
38452                                 '<span unselectable="on"' +
38453                                          (this.disableTooltips ? '' : ' title="{text}"') +
38454                                     ' >{text}</span></a>'
38455                             )
38456                 );
38457             }
38458             
38459             switch (typeof(template)) {
38460                 case 'object' :
38461                     break;
38462                 case 'string' :
38463                     template = new Roo.Template(template);
38464                     break;
38465                 default :
38466                     break;
38467             }
38468             
38469             var el = template.overwrite(td, {"text": text});
38470             
38471             var inner = el.getElementsByTagName("span")[0];
38472             
38473             return {"el": el, "inner": inner};
38474             
38475     }
38476         
38477     
38478 });
38479
38480 /**
38481  * @class Roo.TabPanelItem
38482  * @extends Roo.util.Observable
38483  * Represents an individual item (tab plus body) in a TabPanel.
38484  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38485  * @param {String} id The id of this TabPanelItem
38486  * @param {String} text The text for the tab of this TabPanelItem
38487  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38488  */
38489 Roo.bootstrap.panel.TabItem = function(config){
38490     /**
38491      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38492      * @type Roo.TabPanel
38493      */
38494     this.tabPanel = config.panel;
38495     /**
38496      * The id for this TabPanelItem
38497      * @type String
38498      */
38499     this.id = config.id;
38500     /** @private */
38501     this.disabled = false;
38502     /** @private */
38503     this.text = config.text;
38504     /** @private */
38505     this.loaded = false;
38506     this.closable = config.closable;
38507
38508     /**
38509      * The body element for this TabPanelItem.
38510      * @type Roo.Element
38511      */
38512     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38513     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38514     this.bodyEl.setStyle("display", "block");
38515     this.bodyEl.setStyle("zoom", "1");
38516     //this.hideAction();
38517
38518     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38519     /** @private */
38520     this.el = Roo.get(els.el);
38521     this.inner = Roo.get(els.inner, true);
38522      this.textEl = Roo.bootstrap.version == 4 ?
38523         this.el : Roo.get(this.el.dom.firstChild, true);
38524
38525     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38526     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38527
38528     
38529 //    this.el.on("mousedown", this.onTabMouseDown, this);
38530     this.el.on("click", this.onTabClick, this);
38531     /** @private */
38532     if(config.closable){
38533         var c = Roo.get(els.close, true);
38534         c.dom.title = this.closeText;
38535         c.addClassOnOver("close-over");
38536         c.on("click", this.closeClick, this);
38537      }
38538
38539     this.addEvents({
38540          /**
38541          * @event activate
38542          * Fires when this tab becomes the active tab.
38543          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38544          * @param {Roo.TabPanelItem} this
38545          */
38546         "activate": true,
38547         /**
38548          * @event beforeclose
38549          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38550          * @param {Roo.TabPanelItem} this
38551          * @param {Object} e Set cancel to true on this object to cancel the close.
38552          */
38553         "beforeclose": true,
38554         /**
38555          * @event close
38556          * Fires when this tab is closed.
38557          * @param {Roo.TabPanelItem} this
38558          */
38559          "close": true,
38560         /**
38561          * @event deactivate
38562          * Fires when this tab is no longer the active tab.
38563          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38564          * @param {Roo.TabPanelItem} this
38565          */
38566          "deactivate" : true
38567     });
38568     this.hidden = false;
38569
38570     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38571 };
38572
38573 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38574            {
38575     purgeListeners : function(){
38576        Roo.util.Observable.prototype.purgeListeners.call(this);
38577        this.el.removeAllListeners();
38578     },
38579     /**
38580      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38581      */
38582     show : function(){
38583         this.status_node.addClass("active");
38584         this.showAction();
38585         if(Roo.isOpera){
38586             this.tabPanel.stripWrap.repaint();
38587         }
38588         this.fireEvent("activate", this.tabPanel, this);
38589     },
38590
38591     /**
38592      * Returns true if this tab is the active tab.
38593      * @return {Boolean}
38594      */
38595     isActive : function(){
38596         return this.tabPanel.getActiveTab() == this;
38597     },
38598
38599     /**
38600      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38601      */
38602     hide : function(){
38603         this.status_node.removeClass("active");
38604         this.hideAction();
38605         this.fireEvent("deactivate", this.tabPanel, this);
38606     },
38607
38608     hideAction : function(){
38609         this.bodyEl.hide();
38610         this.bodyEl.setStyle("position", "absolute");
38611         this.bodyEl.setLeft("-20000px");
38612         this.bodyEl.setTop("-20000px");
38613     },
38614
38615     showAction : function(){
38616         this.bodyEl.setStyle("position", "relative");
38617         this.bodyEl.setTop("");
38618         this.bodyEl.setLeft("");
38619         this.bodyEl.show();
38620     },
38621
38622     /**
38623      * Set the tooltip for the tab.
38624      * @param {String} tooltip The tab's tooltip
38625      */
38626     setTooltip : function(text){
38627         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38628             this.textEl.dom.qtip = text;
38629             this.textEl.dom.removeAttribute('title');
38630         }else{
38631             this.textEl.dom.title = text;
38632         }
38633     },
38634
38635     onTabClick : function(e){
38636         e.preventDefault();
38637         this.tabPanel.activate(this.id);
38638     },
38639
38640     onTabMouseDown : function(e){
38641         e.preventDefault();
38642         this.tabPanel.activate(this.id);
38643     },
38644 /*
38645     getWidth : function(){
38646         return this.inner.getWidth();
38647     },
38648
38649     setWidth : function(width){
38650         var iwidth = width - this.linode.getPadding("lr");
38651         this.inner.setWidth(iwidth);
38652         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38653         this.linode.setWidth(width);
38654     },
38655 */
38656     /**
38657      * Show or hide the tab
38658      * @param {Boolean} hidden True to hide or false to show.
38659      */
38660     setHidden : function(hidden){
38661         this.hidden = hidden;
38662         this.linode.setStyle("display", hidden ? "none" : "");
38663     },
38664
38665     /**
38666      * Returns true if this tab is "hidden"
38667      * @return {Boolean}
38668      */
38669     isHidden : function(){
38670         return this.hidden;
38671     },
38672
38673     /**
38674      * Returns the text for this tab
38675      * @return {String}
38676      */
38677     getText : function(){
38678         return this.text;
38679     },
38680     /*
38681     autoSize : function(){
38682         //this.el.beginMeasure();
38683         this.textEl.setWidth(1);
38684         /*
38685          *  #2804 [new] Tabs in Roojs
38686          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38687          */
38688         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38689         //this.el.endMeasure();
38690     //},
38691
38692     /**
38693      * Sets the text for the tab (Note: this also sets the tooltip text)
38694      * @param {String} text The tab's text and tooltip
38695      */
38696     setText : function(text){
38697         this.text = text;
38698         this.textEl.update(text);
38699         this.setTooltip(text);
38700         //if(!this.tabPanel.resizeTabs){
38701         //    this.autoSize();
38702         //}
38703     },
38704     /**
38705      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38706      */
38707     activate : function(){
38708         this.tabPanel.activate(this.id);
38709     },
38710
38711     /**
38712      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38713      */
38714     disable : function(){
38715         if(this.tabPanel.active != this){
38716             this.disabled = true;
38717             this.status_node.addClass("disabled");
38718         }
38719     },
38720
38721     /**
38722      * Enables this TabPanelItem if it was previously disabled.
38723      */
38724     enable : function(){
38725         this.disabled = false;
38726         this.status_node.removeClass("disabled");
38727     },
38728
38729     /**
38730      * Sets the content for this TabPanelItem.
38731      * @param {String} content The content
38732      * @param {Boolean} loadScripts true to look for and load scripts
38733      */
38734     setContent : function(content, loadScripts){
38735         this.bodyEl.update(content, loadScripts);
38736     },
38737
38738     /**
38739      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38740      * @return {Roo.UpdateManager} The UpdateManager
38741      */
38742     getUpdateManager : function(){
38743         return this.bodyEl.getUpdateManager();
38744     },
38745
38746     /**
38747      * Set a URL to be used to load the content for this TabPanelItem.
38748      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38749      * @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)
38750      * @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)
38751      * @return {Roo.UpdateManager} The UpdateManager
38752      */
38753     setUrl : function(url, params, loadOnce){
38754         if(this.refreshDelegate){
38755             this.un('activate', this.refreshDelegate);
38756         }
38757         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38758         this.on("activate", this.refreshDelegate);
38759         return this.bodyEl.getUpdateManager();
38760     },
38761
38762     /** @private */
38763     _handleRefresh : function(url, params, loadOnce){
38764         if(!loadOnce || !this.loaded){
38765             var updater = this.bodyEl.getUpdateManager();
38766             updater.update(url, params, this._setLoaded.createDelegate(this));
38767         }
38768     },
38769
38770     /**
38771      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38772      *   Will fail silently if the setUrl method has not been called.
38773      *   This does not activate the panel, just updates its content.
38774      */
38775     refresh : function(){
38776         if(this.refreshDelegate){
38777            this.loaded = false;
38778            this.refreshDelegate();
38779         }
38780     },
38781
38782     /** @private */
38783     _setLoaded : function(){
38784         this.loaded = true;
38785     },
38786
38787     /** @private */
38788     closeClick : function(e){
38789         var o = {};
38790         e.stopEvent();
38791         this.fireEvent("beforeclose", this, o);
38792         if(o.cancel !== true){
38793             this.tabPanel.removeTab(this.id);
38794         }
38795     },
38796     /**
38797      * The text displayed in the tooltip for the close icon.
38798      * @type String
38799      */
38800     closeText : "Close this tab"
38801 });
38802 /**
38803 *    This script refer to:
38804 *    Title: International Telephone Input
38805 *    Author: Jack O'Connor
38806 *    Code version:  v12.1.12
38807 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38808 **/
38809
38810 Roo.bootstrap.PhoneInputData = function() {
38811     var d = [
38812       [
38813         "Afghanistan (‫افغانستان‬‎)",
38814         "af",
38815         "93"
38816       ],
38817       [
38818         "Albania (Shqipëri)",
38819         "al",
38820         "355"
38821       ],
38822       [
38823         "Algeria (‫الجزائر‬‎)",
38824         "dz",
38825         "213"
38826       ],
38827       [
38828         "American Samoa",
38829         "as",
38830         "1684"
38831       ],
38832       [
38833         "Andorra",
38834         "ad",
38835         "376"
38836       ],
38837       [
38838         "Angola",
38839         "ao",
38840         "244"
38841       ],
38842       [
38843         "Anguilla",
38844         "ai",
38845         "1264"
38846       ],
38847       [
38848         "Antigua and Barbuda",
38849         "ag",
38850         "1268"
38851       ],
38852       [
38853         "Argentina",
38854         "ar",
38855         "54"
38856       ],
38857       [
38858         "Armenia (Հայաստան)",
38859         "am",
38860         "374"
38861       ],
38862       [
38863         "Aruba",
38864         "aw",
38865         "297"
38866       ],
38867       [
38868         "Australia",
38869         "au",
38870         "61",
38871         0
38872       ],
38873       [
38874         "Austria (Österreich)",
38875         "at",
38876         "43"
38877       ],
38878       [
38879         "Azerbaijan (Azərbaycan)",
38880         "az",
38881         "994"
38882       ],
38883       [
38884         "Bahamas",
38885         "bs",
38886         "1242"
38887       ],
38888       [
38889         "Bahrain (‫البحرين‬‎)",
38890         "bh",
38891         "973"
38892       ],
38893       [
38894         "Bangladesh (বাংলাদেশ)",
38895         "bd",
38896         "880"
38897       ],
38898       [
38899         "Barbados",
38900         "bb",
38901         "1246"
38902       ],
38903       [
38904         "Belarus (Беларусь)",
38905         "by",
38906         "375"
38907       ],
38908       [
38909         "Belgium (België)",
38910         "be",
38911         "32"
38912       ],
38913       [
38914         "Belize",
38915         "bz",
38916         "501"
38917       ],
38918       [
38919         "Benin (Bénin)",
38920         "bj",
38921         "229"
38922       ],
38923       [
38924         "Bermuda",
38925         "bm",
38926         "1441"
38927       ],
38928       [
38929         "Bhutan (འབྲུག)",
38930         "bt",
38931         "975"
38932       ],
38933       [
38934         "Bolivia",
38935         "bo",
38936         "591"
38937       ],
38938       [
38939         "Bosnia and Herzegovina (Босна и Херцеговина)",
38940         "ba",
38941         "387"
38942       ],
38943       [
38944         "Botswana",
38945         "bw",
38946         "267"
38947       ],
38948       [
38949         "Brazil (Brasil)",
38950         "br",
38951         "55"
38952       ],
38953       [
38954         "British Indian Ocean Territory",
38955         "io",
38956         "246"
38957       ],
38958       [
38959         "British Virgin Islands",
38960         "vg",
38961         "1284"
38962       ],
38963       [
38964         "Brunei",
38965         "bn",
38966         "673"
38967       ],
38968       [
38969         "Bulgaria (България)",
38970         "bg",
38971         "359"
38972       ],
38973       [
38974         "Burkina Faso",
38975         "bf",
38976         "226"
38977       ],
38978       [
38979         "Burundi (Uburundi)",
38980         "bi",
38981         "257"
38982       ],
38983       [
38984         "Cambodia (កម្ពុជា)",
38985         "kh",
38986         "855"
38987       ],
38988       [
38989         "Cameroon (Cameroun)",
38990         "cm",
38991         "237"
38992       ],
38993       [
38994         "Canada",
38995         "ca",
38996         "1",
38997         1,
38998         ["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"]
38999       ],
39000       [
39001         "Cape Verde (Kabu Verdi)",
39002         "cv",
39003         "238"
39004       ],
39005       [
39006         "Caribbean Netherlands",
39007         "bq",
39008         "599",
39009         1
39010       ],
39011       [
39012         "Cayman Islands",
39013         "ky",
39014         "1345"
39015       ],
39016       [
39017         "Central African Republic (République centrafricaine)",
39018         "cf",
39019         "236"
39020       ],
39021       [
39022         "Chad (Tchad)",
39023         "td",
39024         "235"
39025       ],
39026       [
39027         "Chile",
39028         "cl",
39029         "56"
39030       ],
39031       [
39032         "China (中国)",
39033         "cn",
39034         "86"
39035       ],
39036       [
39037         "Christmas Island",
39038         "cx",
39039         "61",
39040         2
39041       ],
39042       [
39043         "Cocos (Keeling) Islands",
39044         "cc",
39045         "61",
39046         1
39047       ],
39048       [
39049         "Colombia",
39050         "co",
39051         "57"
39052       ],
39053       [
39054         "Comoros (‫جزر القمر‬‎)",
39055         "km",
39056         "269"
39057       ],
39058       [
39059         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39060         "cd",
39061         "243"
39062       ],
39063       [
39064         "Congo (Republic) (Congo-Brazzaville)",
39065         "cg",
39066         "242"
39067       ],
39068       [
39069         "Cook Islands",
39070         "ck",
39071         "682"
39072       ],
39073       [
39074         "Costa Rica",
39075         "cr",
39076         "506"
39077       ],
39078       [
39079         "Côte d’Ivoire",
39080         "ci",
39081         "225"
39082       ],
39083       [
39084         "Croatia (Hrvatska)",
39085         "hr",
39086         "385"
39087       ],
39088       [
39089         "Cuba",
39090         "cu",
39091         "53"
39092       ],
39093       [
39094         "Curaçao",
39095         "cw",
39096         "599",
39097         0
39098       ],
39099       [
39100         "Cyprus (Κύπρος)",
39101         "cy",
39102         "357"
39103       ],
39104       [
39105         "Czech Republic (Česká republika)",
39106         "cz",
39107         "420"
39108       ],
39109       [
39110         "Denmark (Danmark)",
39111         "dk",
39112         "45"
39113       ],
39114       [
39115         "Djibouti",
39116         "dj",
39117         "253"
39118       ],
39119       [
39120         "Dominica",
39121         "dm",
39122         "1767"
39123       ],
39124       [
39125         "Dominican Republic (República Dominicana)",
39126         "do",
39127         "1",
39128         2,
39129         ["809", "829", "849"]
39130       ],
39131       [
39132         "Ecuador",
39133         "ec",
39134         "593"
39135       ],
39136       [
39137         "Egypt (‫مصر‬‎)",
39138         "eg",
39139         "20"
39140       ],
39141       [
39142         "El Salvador",
39143         "sv",
39144         "503"
39145       ],
39146       [
39147         "Equatorial Guinea (Guinea Ecuatorial)",
39148         "gq",
39149         "240"
39150       ],
39151       [
39152         "Eritrea",
39153         "er",
39154         "291"
39155       ],
39156       [
39157         "Estonia (Eesti)",
39158         "ee",
39159         "372"
39160       ],
39161       [
39162         "Ethiopia",
39163         "et",
39164         "251"
39165       ],
39166       [
39167         "Falkland Islands (Islas Malvinas)",
39168         "fk",
39169         "500"
39170       ],
39171       [
39172         "Faroe Islands (Føroyar)",
39173         "fo",
39174         "298"
39175       ],
39176       [
39177         "Fiji",
39178         "fj",
39179         "679"
39180       ],
39181       [
39182         "Finland (Suomi)",
39183         "fi",
39184         "358",
39185         0
39186       ],
39187       [
39188         "France",
39189         "fr",
39190         "33"
39191       ],
39192       [
39193         "French Guiana (Guyane française)",
39194         "gf",
39195         "594"
39196       ],
39197       [
39198         "French Polynesia (Polynésie française)",
39199         "pf",
39200         "689"
39201       ],
39202       [
39203         "Gabon",
39204         "ga",
39205         "241"
39206       ],
39207       [
39208         "Gambia",
39209         "gm",
39210         "220"
39211       ],
39212       [
39213         "Georgia (საქართველო)",
39214         "ge",
39215         "995"
39216       ],
39217       [
39218         "Germany (Deutschland)",
39219         "de",
39220         "49"
39221       ],
39222       [
39223         "Ghana (Gaana)",
39224         "gh",
39225         "233"
39226       ],
39227       [
39228         "Gibraltar",
39229         "gi",
39230         "350"
39231       ],
39232       [
39233         "Greece (Ελλάδα)",
39234         "gr",
39235         "30"
39236       ],
39237       [
39238         "Greenland (Kalaallit Nunaat)",
39239         "gl",
39240         "299"
39241       ],
39242       [
39243         "Grenada",
39244         "gd",
39245         "1473"
39246       ],
39247       [
39248         "Guadeloupe",
39249         "gp",
39250         "590",
39251         0
39252       ],
39253       [
39254         "Guam",
39255         "gu",
39256         "1671"
39257       ],
39258       [
39259         "Guatemala",
39260         "gt",
39261         "502"
39262       ],
39263       [
39264         "Guernsey",
39265         "gg",
39266         "44",
39267         1
39268       ],
39269       [
39270         "Guinea (Guinée)",
39271         "gn",
39272         "224"
39273       ],
39274       [
39275         "Guinea-Bissau (Guiné Bissau)",
39276         "gw",
39277         "245"
39278       ],
39279       [
39280         "Guyana",
39281         "gy",
39282         "592"
39283       ],
39284       [
39285         "Haiti",
39286         "ht",
39287         "509"
39288       ],
39289       [
39290         "Honduras",
39291         "hn",
39292         "504"
39293       ],
39294       [
39295         "Hong Kong (香港)",
39296         "hk",
39297         "852"
39298       ],
39299       [
39300         "Hungary (Magyarország)",
39301         "hu",
39302         "36"
39303       ],
39304       [
39305         "Iceland (Ísland)",
39306         "is",
39307         "354"
39308       ],
39309       [
39310         "India (भारत)",
39311         "in",
39312         "91"
39313       ],
39314       [
39315         "Indonesia",
39316         "id",
39317         "62"
39318       ],
39319       [
39320         "Iran (‫ایران‬‎)",
39321         "ir",
39322         "98"
39323       ],
39324       [
39325         "Iraq (‫العراق‬‎)",
39326         "iq",
39327         "964"
39328       ],
39329       [
39330         "Ireland",
39331         "ie",
39332         "353"
39333       ],
39334       [
39335         "Isle of Man",
39336         "im",
39337         "44",
39338         2
39339       ],
39340       [
39341         "Israel (‫ישראל‬‎)",
39342         "il",
39343         "972"
39344       ],
39345       [
39346         "Italy (Italia)",
39347         "it",
39348         "39",
39349         0
39350       ],
39351       [
39352         "Jamaica",
39353         "jm",
39354         "1876"
39355       ],
39356       [
39357         "Japan (日本)",
39358         "jp",
39359         "81"
39360       ],
39361       [
39362         "Jersey",
39363         "je",
39364         "44",
39365         3
39366       ],
39367       [
39368         "Jordan (‫الأردن‬‎)",
39369         "jo",
39370         "962"
39371       ],
39372       [
39373         "Kazakhstan (Казахстан)",
39374         "kz",
39375         "7",
39376         1
39377       ],
39378       [
39379         "Kenya",
39380         "ke",
39381         "254"
39382       ],
39383       [
39384         "Kiribati",
39385         "ki",
39386         "686"
39387       ],
39388       [
39389         "Kosovo",
39390         "xk",
39391         "383"
39392       ],
39393       [
39394         "Kuwait (‫الكويت‬‎)",
39395         "kw",
39396         "965"
39397       ],
39398       [
39399         "Kyrgyzstan (Кыргызстан)",
39400         "kg",
39401         "996"
39402       ],
39403       [
39404         "Laos (ລາວ)",
39405         "la",
39406         "856"
39407       ],
39408       [
39409         "Latvia (Latvija)",
39410         "lv",
39411         "371"
39412       ],
39413       [
39414         "Lebanon (‫لبنان‬‎)",
39415         "lb",
39416         "961"
39417       ],
39418       [
39419         "Lesotho",
39420         "ls",
39421         "266"
39422       ],
39423       [
39424         "Liberia",
39425         "lr",
39426         "231"
39427       ],
39428       [
39429         "Libya (‫ليبيا‬‎)",
39430         "ly",
39431         "218"
39432       ],
39433       [
39434         "Liechtenstein",
39435         "li",
39436         "423"
39437       ],
39438       [
39439         "Lithuania (Lietuva)",
39440         "lt",
39441         "370"
39442       ],
39443       [
39444         "Luxembourg",
39445         "lu",
39446         "352"
39447       ],
39448       [
39449         "Macau (澳門)",
39450         "mo",
39451         "853"
39452       ],
39453       [
39454         "Macedonia (FYROM) (Македонија)",
39455         "mk",
39456         "389"
39457       ],
39458       [
39459         "Madagascar (Madagasikara)",
39460         "mg",
39461         "261"
39462       ],
39463       [
39464         "Malawi",
39465         "mw",
39466         "265"
39467       ],
39468       [
39469         "Malaysia",
39470         "my",
39471         "60"
39472       ],
39473       [
39474         "Maldives",
39475         "mv",
39476         "960"
39477       ],
39478       [
39479         "Mali",
39480         "ml",
39481         "223"
39482       ],
39483       [
39484         "Malta",
39485         "mt",
39486         "356"
39487       ],
39488       [
39489         "Marshall Islands",
39490         "mh",
39491         "692"
39492       ],
39493       [
39494         "Martinique",
39495         "mq",
39496         "596"
39497       ],
39498       [
39499         "Mauritania (‫موريتانيا‬‎)",
39500         "mr",
39501         "222"
39502       ],
39503       [
39504         "Mauritius (Moris)",
39505         "mu",
39506         "230"
39507       ],
39508       [
39509         "Mayotte",
39510         "yt",
39511         "262",
39512         1
39513       ],
39514       [
39515         "Mexico (México)",
39516         "mx",
39517         "52"
39518       ],
39519       [
39520         "Micronesia",
39521         "fm",
39522         "691"
39523       ],
39524       [
39525         "Moldova (Republica Moldova)",
39526         "md",
39527         "373"
39528       ],
39529       [
39530         "Monaco",
39531         "mc",
39532         "377"
39533       ],
39534       [
39535         "Mongolia (Монгол)",
39536         "mn",
39537         "976"
39538       ],
39539       [
39540         "Montenegro (Crna Gora)",
39541         "me",
39542         "382"
39543       ],
39544       [
39545         "Montserrat",
39546         "ms",
39547         "1664"
39548       ],
39549       [
39550         "Morocco (‫المغرب‬‎)",
39551         "ma",
39552         "212",
39553         0
39554       ],
39555       [
39556         "Mozambique (Moçambique)",
39557         "mz",
39558         "258"
39559       ],
39560       [
39561         "Myanmar (Burma) (မြန်မာ)",
39562         "mm",
39563         "95"
39564       ],
39565       [
39566         "Namibia (Namibië)",
39567         "na",
39568         "264"
39569       ],
39570       [
39571         "Nauru",
39572         "nr",
39573         "674"
39574       ],
39575       [
39576         "Nepal (नेपाल)",
39577         "np",
39578         "977"
39579       ],
39580       [
39581         "Netherlands (Nederland)",
39582         "nl",
39583         "31"
39584       ],
39585       [
39586         "New Caledonia (Nouvelle-Calédonie)",
39587         "nc",
39588         "687"
39589       ],
39590       [
39591         "New Zealand",
39592         "nz",
39593         "64"
39594       ],
39595       [
39596         "Nicaragua",
39597         "ni",
39598         "505"
39599       ],
39600       [
39601         "Niger (Nijar)",
39602         "ne",
39603         "227"
39604       ],
39605       [
39606         "Nigeria",
39607         "ng",
39608         "234"
39609       ],
39610       [
39611         "Niue",
39612         "nu",
39613         "683"
39614       ],
39615       [
39616         "Norfolk Island",
39617         "nf",
39618         "672"
39619       ],
39620       [
39621         "North Korea (조선 민주주의 인민 공화국)",
39622         "kp",
39623         "850"
39624       ],
39625       [
39626         "Northern Mariana Islands",
39627         "mp",
39628         "1670"
39629       ],
39630       [
39631         "Norway (Norge)",
39632         "no",
39633         "47",
39634         0
39635       ],
39636       [
39637         "Oman (‫عُمان‬‎)",
39638         "om",
39639         "968"
39640       ],
39641       [
39642         "Pakistan (‫پاکستان‬‎)",
39643         "pk",
39644         "92"
39645       ],
39646       [
39647         "Palau",
39648         "pw",
39649         "680"
39650       ],
39651       [
39652         "Palestine (‫فلسطين‬‎)",
39653         "ps",
39654         "970"
39655       ],
39656       [
39657         "Panama (Panamá)",
39658         "pa",
39659         "507"
39660       ],
39661       [
39662         "Papua New Guinea",
39663         "pg",
39664         "675"
39665       ],
39666       [
39667         "Paraguay",
39668         "py",
39669         "595"
39670       ],
39671       [
39672         "Peru (Perú)",
39673         "pe",
39674         "51"
39675       ],
39676       [
39677         "Philippines",
39678         "ph",
39679         "63"
39680       ],
39681       [
39682         "Poland (Polska)",
39683         "pl",
39684         "48"
39685       ],
39686       [
39687         "Portugal",
39688         "pt",
39689         "351"
39690       ],
39691       [
39692         "Puerto Rico",
39693         "pr",
39694         "1",
39695         3,
39696         ["787", "939"]
39697       ],
39698       [
39699         "Qatar (‫قطر‬‎)",
39700         "qa",
39701         "974"
39702       ],
39703       [
39704         "Réunion (La Réunion)",
39705         "re",
39706         "262",
39707         0
39708       ],
39709       [
39710         "Romania (România)",
39711         "ro",
39712         "40"
39713       ],
39714       [
39715         "Russia (Россия)",
39716         "ru",
39717         "7",
39718         0
39719       ],
39720       [
39721         "Rwanda",
39722         "rw",
39723         "250"
39724       ],
39725       [
39726         "Saint Barthélemy",
39727         "bl",
39728         "590",
39729         1
39730       ],
39731       [
39732         "Saint Helena",
39733         "sh",
39734         "290"
39735       ],
39736       [
39737         "Saint Kitts and Nevis",
39738         "kn",
39739         "1869"
39740       ],
39741       [
39742         "Saint Lucia",
39743         "lc",
39744         "1758"
39745       ],
39746       [
39747         "Saint Martin (Saint-Martin (partie française))",
39748         "mf",
39749         "590",
39750         2
39751       ],
39752       [
39753         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39754         "pm",
39755         "508"
39756       ],
39757       [
39758         "Saint Vincent and the Grenadines",
39759         "vc",
39760         "1784"
39761       ],
39762       [
39763         "Samoa",
39764         "ws",
39765         "685"
39766       ],
39767       [
39768         "San Marino",
39769         "sm",
39770         "378"
39771       ],
39772       [
39773         "São Tomé and Príncipe (São Tomé e Príncipe)",
39774         "st",
39775         "239"
39776       ],
39777       [
39778         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39779         "sa",
39780         "966"
39781       ],
39782       [
39783         "Senegal (Sénégal)",
39784         "sn",
39785         "221"
39786       ],
39787       [
39788         "Serbia (Србија)",
39789         "rs",
39790         "381"
39791       ],
39792       [
39793         "Seychelles",
39794         "sc",
39795         "248"
39796       ],
39797       [
39798         "Sierra Leone",
39799         "sl",
39800         "232"
39801       ],
39802       [
39803         "Singapore",
39804         "sg",
39805         "65"
39806       ],
39807       [
39808         "Sint Maarten",
39809         "sx",
39810         "1721"
39811       ],
39812       [
39813         "Slovakia (Slovensko)",
39814         "sk",
39815         "421"
39816       ],
39817       [
39818         "Slovenia (Slovenija)",
39819         "si",
39820         "386"
39821       ],
39822       [
39823         "Solomon Islands",
39824         "sb",
39825         "677"
39826       ],
39827       [
39828         "Somalia (Soomaaliya)",
39829         "so",
39830         "252"
39831       ],
39832       [
39833         "South Africa",
39834         "za",
39835         "27"
39836       ],
39837       [
39838         "South Korea (대한민국)",
39839         "kr",
39840         "82"
39841       ],
39842       [
39843         "South Sudan (‫جنوب السودان‬‎)",
39844         "ss",
39845         "211"
39846       ],
39847       [
39848         "Spain (España)",
39849         "es",
39850         "34"
39851       ],
39852       [
39853         "Sri Lanka (ශ්‍රී ලංකාව)",
39854         "lk",
39855         "94"
39856       ],
39857       [
39858         "Sudan (‫السودان‬‎)",
39859         "sd",
39860         "249"
39861       ],
39862       [
39863         "Suriname",
39864         "sr",
39865         "597"
39866       ],
39867       [
39868         "Svalbard and Jan Mayen",
39869         "sj",
39870         "47",
39871         1
39872       ],
39873       [
39874         "Swaziland",
39875         "sz",
39876         "268"
39877       ],
39878       [
39879         "Sweden (Sverige)",
39880         "se",
39881         "46"
39882       ],
39883       [
39884         "Switzerland (Schweiz)",
39885         "ch",
39886         "41"
39887       ],
39888       [
39889         "Syria (‫سوريا‬‎)",
39890         "sy",
39891         "963"
39892       ],
39893       [
39894         "Taiwan (台灣)",
39895         "tw",
39896         "886"
39897       ],
39898       [
39899         "Tajikistan",
39900         "tj",
39901         "992"
39902       ],
39903       [
39904         "Tanzania",
39905         "tz",
39906         "255"
39907       ],
39908       [
39909         "Thailand (ไทย)",
39910         "th",
39911         "66"
39912       ],
39913       [
39914         "Timor-Leste",
39915         "tl",
39916         "670"
39917       ],
39918       [
39919         "Togo",
39920         "tg",
39921         "228"
39922       ],
39923       [
39924         "Tokelau",
39925         "tk",
39926         "690"
39927       ],
39928       [
39929         "Tonga",
39930         "to",
39931         "676"
39932       ],
39933       [
39934         "Trinidad and Tobago",
39935         "tt",
39936         "1868"
39937       ],
39938       [
39939         "Tunisia (‫تونس‬‎)",
39940         "tn",
39941         "216"
39942       ],
39943       [
39944         "Turkey (Türkiye)",
39945         "tr",
39946         "90"
39947       ],
39948       [
39949         "Turkmenistan",
39950         "tm",
39951         "993"
39952       ],
39953       [
39954         "Turks and Caicos Islands",
39955         "tc",
39956         "1649"
39957       ],
39958       [
39959         "Tuvalu",
39960         "tv",
39961         "688"
39962       ],
39963       [
39964         "U.S. Virgin Islands",
39965         "vi",
39966         "1340"
39967       ],
39968       [
39969         "Uganda",
39970         "ug",
39971         "256"
39972       ],
39973       [
39974         "Ukraine (Україна)",
39975         "ua",
39976         "380"
39977       ],
39978       [
39979         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39980         "ae",
39981         "971"
39982       ],
39983       [
39984         "United Kingdom",
39985         "gb",
39986         "44",
39987         0
39988       ],
39989       [
39990         "United States",
39991         "us",
39992         "1",
39993         0
39994       ],
39995       [
39996         "Uruguay",
39997         "uy",
39998         "598"
39999       ],
40000       [
40001         "Uzbekistan (Oʻzbekiston)",
40002         "uz",
40003         "998"
40004       ],
40005       [
40006         "Vanuatu",
40007         "vu",
40008         "678"
40009       ],
40010       [
40011         "Vatican City (Città del Vaticano)",
40012         "va",
40013         "39",
40014         1
40015       ],
40016       [
40017         "Venezuela",
40018         "ve",
40019         "58"
40020       ],
40021       [
40022         "Vietnam (Việt Nam)",
40023         "vn",
40024         "84"
40025       ],
40026       [
40027         "Wallis and Futuna (Wallis-et-Futuna)",
40028         "wf",
40029         "681"
40030       ],
40031       [
40032         "Western Sahara (‫الصحراء الغربية‬‎)",
40033         "eh",
40034         "212",
40035         1
40036       ],
40037       [
40038         "Yemen (‫اليمن‬‎)",
40039         "ye",
40040         "967"
40041       ],
40042       [
40043         "Zambia",
40044         "zm",
40045         "260"
40046       ],
40047       [
40048         "Zimbabwe",
40049         "zw",
40050         "263"
40051       ],
40052       [
40053         "Åland Islands",
40054         "ax",
40055         "358",
40056         1
40057       ]
40058   ];
40059   
40060   return d;
40061 }/**
40062 *    This script refer to:
40063 *    Title: International Telephone Input
40064 *    Author: Jack O'Connor
40065 *    Code version:  v12.1.12
40066 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40067 **/
40068
40069 /**
40070  * @class Roo.bootstrap.PhoneInput
40071  * @extends Roo.bootstrap.TriggerField
40072  * An input with International dial-code selection
40073  
40074  * @cfg {String} defaultDialCode default '+852'
40075  * @cfg {Array} preferedCountries default []
40076   
40077  * @constructor
40078  * Create a new PhoneInput.
40079  * @param {Object} config Configuration options
40080  */
40081
40082 Roo.bootstrap.PhoneInput = function(config) {
40083     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40084 };
40085
40086 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40087         
40088         listWidth: undefined,
40089         
40090         selectedClass: 'active',
40091         
40092         invalidClass : "has-warning",
40093         
40094         validClass: 'has-success',
40095         
40096         allowed: '0123456789',
40097         
40098         max_length: 15,
40099         
40100         /**
40101          * @cfg {String} defaultDialCode The default dial code when initializing the input
40102          */
40103         defaultDialCode: '+852',
40104         
40105         /**
40106          * @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
40107          */
40108         preferedCountries: false,
40109         
40110         getAutoCreate : function()
40111         {
40112             var data = Roo.bootstrap.PhoneInputData();
40113             var align = this.labelAlign || this.parentLabelAlign();
40114             var id = Roo.id();
40115             
40116             this.allCountries = [];
40117             this.dialCodeMapping = [];
40118             
40119             for (var i = 0; i < data.length; i++) {
40120               var c = data[i];
40121               this.allCountries[i] = {
40122                 name: c[0],
40123                 iso2: c[1],
40124                 dialCode: c[2],
40125                 priority: c[3] || 0,
40126                 areaCodes: c[4] || null
40127               };
40128               this.dialCodeMapping[c[2]] = {
40129                   name: c[0],
40130                   iso2: c[1],
40131                   priority: c[3] || 0,
40132                   areaCodes: c[4] || null
40133               };
40134             }
40135             
40136             var cfg = {
40137                 cls: 'form-group',
40138                 cn: []
40139             };
40140             
40141             var input =  {
40142                 tag: 'input',
40143                 id : id,
40144                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40145                 maxlength: this.max_length,
40146                 cls : 'form-control tel-input',
40147                 autocomplete: 'new-password'
40148             };
40149             
40150             var hiddenInput = {
40151                 tag: 'input',
40152                 type: 'hidden',
40153                 cls: 'hidden-tel-input'
40154             };
40155             
40156             if (this.name) {
40157                 hiddenInput.name = this.name;
40158             }
40159             
40160             if (this.disabled) {
40161                 input.disabled = true;
40162             }
40163             
40164             var flag_container = {
40165                 tag: 'div',
40166                 cls: 'flag-box',
40167                 cn: [
40168                     {
40169                         tag: 'div',
40170                         cls: 'flag'
40171                     },
40172                     {
40173                         tag: 'div',
40174                         cls: 'caret'
40175                     }
40176                 ]
40177             };
40178             
40179             var box = {
40180                 tag: 'div',
40181                 cls: this.hasFeedback ? 'has-feedback' : '',
40182                 cn: [
40183                     hiddenInput,
40184                     input,
40185                     {
40186                         tag: 'input',
40187                         cls: 'dial-code-holder',
40188                         disabled: true
40189                     }
40190                 ]
40191             };
40192             
40193             var container = {
40194                 cls: 'roo-select2-container input-group',
40195                 cn: [
40196                     flag_container,
40197                     box
40198                 ]
40199             };
40200             
40201             if (this.fieldLabel.length) {
40202                 var indicator = {
40203                     tag: 'i',
40204                     tooltip: 'This field is required'
40205                 };
40206                 
40207                 var label = {
40208                     tag: 'label',
40209                     'for':  id,
40210                     cls: 'control-label',
40211                     cn: []
40212                 };
40213                 
40214                 var label_text = {
40215                     tag: 'span',
40216                     html: this.fieldLabel
40217                 };
40218                 
40219                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40220                 label.cn = [
40221                     indicator,
40222                     label_text
40223                 ];
40224                 
40225                 if(this.indicatorpos == 'right') {
40226                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40227                     label.cn = [
40228                         label_text,
40229                         indicator
40230                     ];
40231                 }
40232                 
40233                 if(align == 'left') {
40234                     container = {
40235                         tag: 'div',
40236                         cn: [
40237                             container
40238                         ]
40239                     };
40240                     
40241                     if(this.labelWidth > 12){
40242                         label.style = "width: " + this.labelWidth + 'px';
40243                     }
40244                     if(this.labelWidth < 13 && this.labelmd == 0){
40245                         this.labelmd = this.labelWidth;
40246                     }
40247                     if(this.labellg > 0){
40248                         label.cls += ' col-lg-' + this.labellg;
40249                         input.cls += ' col-lg-' + (12 - this.labellg);
40250                     }
40251                     if(this.labelmd > 0){
40252                         label.cls += ' col-md-' + this.labelmd;
40253                         container.cls += ' col-md-' + (12 - this.labelmd);
40254                     }
40255                     if(this.labelsm > 0){
40256                         label.cls += ' col-sm-' + this.labelsm;
40257                         container.cls += ' col-sm-' + (12 - this.labelsm);
40258                     }
40259                     if(this.labelxs > 0){
40260                         label.cls += ' col-xs-' + this.labelxs;
40261                         container.cls += ' col-xs-' + (12 - this.labelxs);
40262                     }
40263                 }
40264             }
40265             
40266             cfg.cn = [
40267                 label,
40268                 container
40269             ];
40270             
40271             var settings = this;
40272             
40273             ['xs','sm','md','lg'].map(function(size){
40274                 if (settings[size]) {
40275                     cfg.cls += ' col-' + size + '-' + settings[size];
40276                 }
40277             });
40278             
40279             this.store = new Roo.data.Store({
40280                 proxy : new Roo.data.MemoryProxy({}),
40281                 reader : new Roo.data.JsonReader({
40282                     fields : [
40283                         {
40284                             'name' : 'name',
40285                             'type' : 'string'
40286                         },
40287                         {
40288                             'name' : 'iso2',
40289                             'type' : 'string'
40290                         },
40291                         {
40292                             'name' : 'dialCode',
40293                             'type' : 'string'
40294                         },
40295                         {
40296                             'name' : 'priority',
40297                             'type' : 'string'
40298                         },
40299                         {
40300                             'name' : 'areaCodes',
40301                             'type' : 'string'
40302                         }
40303                     ]
40304                 })
40305             });
40306             
40307             if(!this.preferedCountries) {
40308                 this.preferedCountries = [
40309                     'hk',
40310                     'gb',
40311                     'us'
40312                 ];
40313             }
40314             
40315             var p = this.preferedCountries.reverse();
40316             
40317             if(p) {
40318                 for (var i = 0; i < p.length; i++) {
40319                     for (var j = 0; j < this.allCountries.length; j++) {
40320                         if(this.allCountries[j].iso2 == p[i]) {
40321                             var t = this.allCountries[j];
40322                             this.allCountries.splice(j,1);
40323                             this.allCountries.unshift(t);
40324                         }
40325                     } 
40326                 }
40327             }
40328             
40329             this.store.proxy.data = {
40330                 success: true,
40331                 data: this.allCountries
40332             };
40333             
40334             return cfg;
40335         },
40336         
40337         initEvents : function()
40338         {
40339             this.createList();
40340             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40341             
40342             this.indicator = this.indicatorEl();
40343             this.flag = this.flagEl();
40344             this.dialCodeHolder = this.dialCodeHolderEl();
40345             
40346             this.trigger = this.el.select('div.flag-box',true).first();
40347             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40348             
40349             var _this = this;
40350             
40351             (function(){
40352                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40353                 _this.list.setWidth(lw);
40354             }).defer(100);
40355             
40356             this.list.on('mouseover', this.onViewOver, this);
40357             this.list.on('mousemove', this.onViewMove, this);
40358             this.inputEl().on("keyup", this.onKeyUp, this);
40359             this.inputEl().on("keypress", this.onKeyPress, this);
40360             
40361             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40362
40363             this.view = new Roo.View(this.list, this.tpl, {
40364                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40365             });
40366             
40367             this.view.on('click', this.onViewClick, this);
40368             this.setValue(this.defaultDialCode);
40369         },
40370         
40371         onTriggerClick : function(e)
40372         {
40373             Roo.log('trigger click');
40374             if(this.disabled){
40375                 return;
40376             }
40377             
40378             if(this.isExpanded()){
40379                 this.collapse();
40380                 this.hasFocus = false;
40381             }else {
40382                 this.store.load({});
40383                 this.hasFocus = true;
40384                 this.expand();
40385             }
40386         },
40387         
40388         isExpanded : function()
40389         {
40390             return this.list.isVisible();
40391         },
40392         
40393         collapse : function()
40394         {
40395             if(!this.isExpanded()){
40396                 return;
40397             }
40398             this.list.hide();
40399             Roo.get(document).un('mousedown', this.collapseIf, this);
40400             Roo.get(document).un('mousewheel', this.collapseIf, this);
40401             this.fireEvent('collapse', this);
40402             this.validate();
40403         },
40404         
40405         expand : function()
40406         {
40407             Roo.log('expand');
40408
40409             if(this.isExpanded() || !this.hasFocus){
40410                 return;
40411             }
40412             
40413             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40414             this.list.setWidth(lw);
40415             
40416             this.list.show();
40417             this.restrictHeight();
40418             
40419             Roo.get(document).on('mousedown', this.collapseIf, this);
40420             Roo.get(document).on('mousewheel', this.collapseIf, this);
40421             
40422             this.fireEvent('expand', this);
40423         },
40424         
40425         restrictHeight : function()
40426         {
40427             this.list.alignTo(this.inputEl(), this.listAlign);
40428             this.list.alignTo(this.inputEl(), this.listAlign);
40429         },
40430         
40431         onViewOver : function(e, t)
40432         {
40433             if(this.inKeyMode){
40434                 return;
40435             }
40436             var item = this.view.findItemFromChild(t);
40437             
40438             if(item){
40439                 var index = this.view.indexOf(item);
40440                 this.select(index, false);
40441             }
40442         },
40443
40444         // private
40445         onViewClick : function(view, doFocus, el, e)
40446         {
40447             var index = this.view.getSelectedIndexes()[0];
40448             
40449             var r = this.store.getAt(index);
40450             
40451             if(r){
40452                 this.onSelect(r, index);
40453             }
40454             if(doFocus !== false && !this.blockFocus){
40455                 this.inputEl().focus();
40456             }
40457         },
40458         
40459         onViewMove : function(e, t)
40460         {
40461             this.inKeyMode = false;
40462         },
40463         
40464         select : function(index, scrollIntoView)
40465         {
40466             this.selectedIndex = index;
40467             this.view.select(index);
40468             if(scrollIntoView !== false){
40469                 var el = this.view.getNode(index);
40470                 if(el){
40471                     this.list.scrollChildIntoView(el, false);
40472                 }
40473             }
40474         },
40475         
40476         createList : function()
40477         {
40478             this.list = Roo.get(document.body).createChild({
40479                 tag: 'ul',
40480                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40481                 style: 'display:none'
40482             });
40483             
40484             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40485         },
40486         
40487         collapseIf : function(e)
40488         {
40489             var in_combo  = e.within(this.el);
40490             var in_list =  e.within(this.list);
40491             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40492             
40493             if (in_combo || in_list || is_list) {
40494                 return;
40495             }
40496             this.collapse();
40497         },
40498         
40499         onSelect : function(record, index)
40500         {
40501             if(this.fireEvent('beforeselect', this, record, index) !== false){
40502                 
40503                 this.setFlagClass(record.data.iso2);
40504                 this.setDialCode(record.data.dialCode);
40505                 this.hasFocus = false;
40506                 this.collapse();
40507                 this.fireEvent('select', this, record, index);
40508             }
40509         },
40510         
40511         flagEl : function()
40512         {
40513             var flag = this.el.select('div.flag',true).first();
40514             if(!flag){
40515                 return false;
40516             }
40517             return flag;
40518         },
40519         
40520         dialCodeHolderEl : function()
40521         {
40522             var d = this.el.select('input.dial-code-holder',true).first();
40523             if(!d){
40524                 return false;
40525             }
40526             return d;
40527         },
40528         
40529         setDialCode : function(v)
40530         {
40531             this.dialCodeHolder.dom.value = '+'+v;
40532         },
40533         
40534         setFlagClass : function(n)
40535         {
40536             this.flag.dom.className = 'flag '+n;
40537         },
40538         
40539         getValue : function()
40540         {
40541             var v = this.inputEl().getValue();
40542             if(this.dialCodeHolder) {
40543                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40544             }
40545             return v;
40546         },
40547         
40548         setValue : function(v)
40549         {
40550             var d = this.getDialCode(v);
40551             
40552             //invalid dial code
40553             if(v.length == 0 || !d || d.length == 0) {
40554                 if(this.rendered){
40555                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40556                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40557                 }
40558                 return;
40559             }
40560             
40561             //valid dial code
40562             this.setFlagClass(this.dialCodeMapping[d].iso2);
40563             this.setDialCode(d);
40564             this.inputEl().dom.value = v.replace('+'+d,'');
40565             this.hiddenEl().dom.value = this.getValue();
40566             
40567             this.validate();
40568         },
40569         
40570         getDialCode : function(v)
40571         {
40572             v = v ||  '';
40573             
40574             if (v.length == 0) {
40575                 return this.dialCodeHolder.dom.value;
40576             }
40577             
40578             var dialCode = "";
40579             if (v.charAt(0) != "+") {
40580                 return false;
40581             }
40582             var numericChars = "";
40583             for (var i = 1; i < v.length; i++) {
40584               var c = v.charAt(i);
40585               if (!isNaN(c)) {
40586                 numericChars += c;
40587                 if (this.dialCodeMapping[numericChars]) {
40588                   dialCode = v.substr(1, i);
40589                 }
40590                 if (numericChars.length == 4) {
40591                   break;
40592                 }
40593               }
40594             }
40595             return dialCode;
40596         },
40597         
40598         reset : function()
40599         {
40600             this.setValue(this.defaultDialCode);
40601             this.validate();
40602         },
40603         
40604         hiddenEl : function()
40605         {
40606             return this.el.select('input.hidden-tel-input',true).first();
40607         },
40608         
40609         // after setting val
40610         onKeyUp : function(e){
40611             this.setValue(this.getValue());
40612         },
40613         
40614         onKeyPress : function(e){
40615             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40616                 e.stopEvent();
40617             }
40618         }
40619         
40620 });
40621 /**
40622  * @class Roo.bootstrap.MoneyField
40623  * @extends Roo.bootstrap.ComboBox
40624  * Bootstrap MoneyField class
40625  * 
40626  * @constructor
40627  * Create a new MoneyField.
40628  * @param {Object} config Configuration options
40629  */
40630
40631 Roo.bootstrap.MoneyField = function(config) {
40632     
40633     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40634     
40635 };
40636
40637 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40638     
40639     /**
40640      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40641      */
40642     allowDecimals : true,
40643     /**
40644      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40645      */
40646     decimalSeparator : ".",
40647     /**
40648      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40649      */
40650     decimalPrecision : 0,
40651     /**
40652      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40653      */
40654     allowNegative : true,
40655     /**
40656      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40657      */
40658     allowZero: true,
40659     /**
40660      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40661      */
40662     minValue : Number.NEGATIVE_INFINITY,
40663     /**
40664      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40665      */
40666     maxValue : Number.MAX_VALUE,
40667     /**
40668      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40669      */
40670     minText : "The minimum value for this field is {0}",
40671     /**
40672      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40673      */
40674     maxText : "The maximum value for this field is {0}",
40675     /**
40676      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40677      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40678      */
40679     nanText : "{0} is not a valid number",
40680     /**
40681      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40682      */
40683     castInt : true,
40684     /**
40685      * @cfg {String} defaults currency of the MoneyField
40686      * value should be in lkey
40687      */
40688     defaultCurrency : false,
40689     /**
40690      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40691      */
40692     thousandsDelimiter : false,
40693     /**
40694      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40695      */
40696     max_length: false,
40697     
40698     inputlg : 9,
40699     inputmd : 9,
40700     inputsm : 9,
40701     inputxs : 6,
40702     
40703     store : false,
40704     
40705     getAutoCreate : function()
40706     {
40707         var align = this.labelAlign || this.parentLabelAlign();
40708         
40709         var id = Roo.id();
40710
40711         var cfg = {
40712             cls: 'form-group',
40713             cn: []
40714         };
40715
40716         var input =  {
40717             tag: 'input',
40718             id : id,
40719             cls : 'form-control roo-money-amount-input',
40720             autocomplete: 'new-password'
40721         };
40722         
40723         var hiddenInput = {
40724             tag: 'input',
40725             type: 'hidden',
40726             id: Roo.id(),
40727             cls: 'hidden-number-input'
40728         };
40729         
40730         if(this.max_length) {
40731             input.maxlength = this.max_length; 
40732         }
40733         
40734         if (this.name) {
40735             hiddenInput.name = this.name;
40736         }
40737
40738         if (this.disabled) {
40739             input.disabled = true;
40740         }
40741
40742         var clg = 12 - this.inputlg;
40743         var cmd = 12 - this.inputmd;
40744         var csm = 12 - this.inputsm;
40745         var cxs = 12 - this.inputxs;
40746         
40747         var container = {
40748             tag : 'div',
40749             cls : 'row roo-money-field',
40750             cn : [
40751                 {
40752                     tag : 'div',
40753                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40754                     cn : [
40755                         {
40756                             tag : 'div',
40757                             cls: 'roo-select2-container input-group',
40758                             cn: [
40759                                 {
40760                                     tag : 'input',
40761                                     cls : 'form-control roo-money-currency-input',
40762                                     autocomplete: 'new-password',
40763                                     readOnly : 1,
40764                                     name : this.currencyName
40765                                 },
40766                                 {
40767                                     tag :'span',
40768                                     cls : 'input-group-addon',
40769                                     cn : [
40770                                         {
40771                                             tag: 'span',
40772                                             cls: 'caret'
40773                                         }
40774                                     ]
40775                                 }
40776                             ]
40777                         }
40778                     ]
40779                 },
40780                 {
40781                     tag : 'div',
40782                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40783                     cn : [
40784                         {
40785                             tag: 'div',
40786                             cls: this.hasFeedback ? 'has-feedback' : '',
40787                             cn: [
40788                                 input
40789                             ]
40790                         }
40791                     ]
40792                 }
40793             ]
40794             
40795         };
40796         
40797         if (this.fieldLabel.length) {
40798             var indicator = {
40799                 tag: 'i',
40800                 tooltip: 'This field is required'
40801             };
40802
40803             var label = {
40804                 tag: 'label',
40805                 'for':  id,
40806                 cls: 'control-label',
40807                 cn: []
40808             };
40809
40810             var label_text = {
40811                 tag: 'span',
40812                 html: this.fieldLabel
40813             };
40814
40815             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40816             label.cn = [
40817                 indicator,
40818                 label_text
40819             ];
40820
40821             if(this.indicatorpos == 'right') {
40822                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40823                 label.cn = [
40824                     label_text,
40825                     indicator
40826                 ];
40827             }
40828
40829             if(align == 'left') {
40830                 container = {
40831                     tag: 'div',
40832                     cn: [
40833                         container
40834                     ]
40835                 };
40836
40837                 if(this.labelWidth > 12){
40838                     label.style = "width: " + this.labelWidth + 'px';
40839                 }
40840                 if(this.labelWidth < 13 && this.labelmd == 0){
40841                     this.labelmd = this.labelWidth;
40842                 }
40843                 if(this.labellg > 0){
40844                     label.cls += ' col-lg-' + this.labellg;
40845                     input.cls += ' col-lg-' + (12 - this.labellg);
40846                 }
40847                 if(this.labelmd > 0){
40848                     label.cls += ' col-md-' + this.labelmd;
40849                     container.cls += ' col-md-' + (12 - this.labelmd);
40850                 }
40851                 if(this.labelsm > 0){
40852                     label.cls += ' col-sm-' + this.labelsm;
40853                     container.cls += ' col-sm-' + (12 - this.labelsm);
40854                 }
40855                 if(this.labelxs > 0){
40856                     label.cls += ' col-xs-' + this.labelxs;
40857                     container.cls += ' col-xs-' + (12 - this.labelxs);
40858                 }
40859             }
40860         }
40861
40862         cfg.cn = [
40863             label,
40864             container,
40865             hiddenInput
40866         ];
40867         
40868         var settings = this;
40869
40870         ['xs','sm','md','lg'].map(function(size){
40871             if (settings[size]) {
40872                 cfg.cls += ' col-' + size + '-' + settings[size];
40873             }
40874         });
40875         
40876         return cfg;
40877     },
40878     
40879     initEvents : function()
40880     {
40881         this.indicator = this.indicatorEl();
40882         
40883         this.initCurrencyEvent();
40884         
40885         this.initNumberEvent();
40886     },
40887     
40888     initCurrencyEvent : function()
40889     {
40890         if (!this.store) {
40891             throw "can not find store for combo";
40892         }
40893         
40894         this.store = Roo.factory(this.store, Roo.data);
40895         this.store.parent = this;
40896         
40897         this.createList();
40898         
40899         this.triggerEl = this.el.select('.input-group-addon', true).first();
40900         
40901         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40902         
40903         var _this = this;
40904         
40905         (function(){
40906             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40907             _this.list.setWidth(lw);
40908         }).defer(100);
40909         
40910         this.list.on('mouseover', this.onViewOver, this);
40911         this.list.on('mousemove', this.onViewMove, this);
40912         this.list.on('scroll', this.onViewScroll, this);
40913         
40914         if(!this.tpl){
40915             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40916         }
40917         
40918         this.view = new Roo.View(this.list, this.tpl, {
40919             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40920         });
40921         
40922         this.view.on('click', this.onViewClick, this);
40923         
40924         this.store.on('beforeload', this.onBeforeLoad, this);
40925         this.store.on('load', this.onLoad, this);
40926         this.store.on('loadexception', this.onLoadException, this);
40927         
40928         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40929             "up" : function(e){
40930                 this.inKeyMode = true;
40931                 this.selectPrev();
40932             },
40933
40934             "down" : function(e){
40935                 if(!this.isExpanded()){
40936                     this.onTriggerClick();
40937                 }else{
40938                     this.inKeyMode = true;
40939                     this.selectNext();
40940                 }
40941             },
40942
40943             "enter" : function(e){
40944                 this.collapse();
40945                 
40946                 if(this.fireEvent("specialkey", this, e)){
40947                     this.onViewClick(false);
40948                 }
40949                 
40950                 return true;
40951             },
40952
40953             "esc" : function(e){
40954                 this.collapse();
40955             },
40956
40957             "tab" : function(e){
40958                 this.collapse();
40959                 
40960                 if(this.fireEvent("specialkey", this, e)){
40961                     this.onViewClick(false);
40962                 }
40963                 
40964                 return true;
40965             },
40966
40967             scope : this,
40968
40969             doRelay : function(foo, bar, hname){
40970                 if(hname == 'down' || this.scope.isExpanded()){
40971                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40972                 }
40973                 return true;
40974             },
40975
40976             forceKeyDown: true
40977         });
40978         
40979         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40980         
40981     },
40982     
40983     initNumberEvent : function(e)
40984     {
40985         this.inputEl().on("keydown" , this.fireKey,  this);
40986         this.inputEl().on("focus", this.onFocus,  this);
40987         this.inputEl().on("blur", this.onBlur,  this);
40988         
40989         this.inputEl().relayEvent('keyup', this);
40990         
40991         if(this.indicator){
40992             this.indicator.addClass('invisible');
40993         }
40994  
40995         this.originalValue = this.getValue();
40996         
40997         if(this.validationEvent == 'keyup'){
40998             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40999             this.inputEl().on('keyup', this.filterValidation, this);
41000         }
41001         else if(this.validationEvent !== false){
41002             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41003         }
41004         
41005         if(this.selectOnFocus){
41006             this.on("focus", this.preFocus, this);
41007             
41008         }
41009         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41010             this.inputEl().on("keypress", this.filterKeys, this);
41011         } else {
41012             this.inputEl().relayEvent('keypress', this);
41013         }
41014         
41015         var allowed = "0123456789";
41016         
41017         if(this.allowDecimals){
41018             allowed += this.decimalSeparator;
41019         }
41020         
41021         if(this.allowNegative){
41022             allowed += "-";
41023         }
41024         
41025         if(this.thousandsDelimiter) {
41026             allowed += ",";
41027         }
41028         
41029         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41030         
41031         var keyPress = function(e){
41032             
41033             var k = e.getKey();
41034             
41035             var c = e.getCharCode();
41036             
41037             if(
41038                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41039                     allowed.indexOf(String.fromCharCode(c)) === -1
41040             ){
41041                 e.stopEvent();
41042                 return;
41043             }
41044             
41045             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41046                 return;
41047             }
41048             
41049             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41050                 e.stopEvent();
41051             }
41052         };
41053         
41054         this.inputEl().on("keypress", keyPress, this);
41055         
41056     },
41057     
41058     onTriggerClick : function(e)
41059     {   
41060         if(this.disabled){
41061             return;
41062         }
41063         
41064         this.page = 0;
41065         this.loadNext = false;
41066         
41067         if(this.isExpanded()){
41068             this.collapse();
41069             return;
41070         }
41071         
41072         this.hasFocus = true;
41073         
41074         if(this.triggerAction == 'all') {
41075             this.doQuery(this.allQuery, true);
41076             return;
41077         }
41078         
41079         this.doQuery(this.getRawValue());
41080     },
41081     
41082     getCurrency : function()
41083     {   
41084         var v = this.currencyEl().getValue();
41085         
41086         return v;
41087     },
41088     
41089     restrictHeight : function()
41090     {
41091         this.list.alignTo(this.currencyEl(), this.listAlign);
41092         this.list.alignTo(this.currencyEl(), this.listAlign);
41093     },
41094     
41095     onViewClick : function(view, doFocus, el, e)
41096     {
41097         var index = this.view.getSelectedIndexes()[0];
41098         
41099         var r = this.store.getAt(index);
41100         
41101         if(r){
41102             this.onSelect(r, index);
41103         }
41104     },
41105     
41106     onSelect : function(record, index){
41107         
41108         if(this.fireEvent('beforeselect', this, record, index) !== false){
41109         
41110             this.setFromCurrencyData(index > -1 ? record.data : false);
41111             
41112             this.collapse();
41113             
41114             this.fireEvent('select', this, record, index);
41115         }
41116     },
41117     
41118     setFromCurrencyData : function(o)
41119     {
41120         var currency = '';
41121         
41122         this.lastCurrency = o;
41123         
41124         if (this.currencyField) {
41125             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41126         } else {
41127             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41128         }
41129         
41130         this.lastSelectionText = currency;
41131         
41132         //setting default currency
41133         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41134             this.setCurrency(this.defaultCurrency);
41135             return;
41136         }
41137         
41138         this.setCurrency(currency);
41139     },
41140     
41141     setFromData : function(o)
41142     {
41143         var c = {};
41144         
41145         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41146         
41147         this.setFromCurrencyData(c);
41148         
41149         var value = '';
41150         
41151         if (this.name) {
41152             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41153         } else {
41154             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41155         }
41156         
41157         this.setValue(value);
41158         
41159     },
41160     
41161     setCurrency : function(v)
41162     {   
41163         this.currencyValue = v;
41164         
41165         if(this.rendered){
41166             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41167             this.validate();
41168         }
41169     },
41170     
41171     setValue : function(v)
41172     {
41173         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41174         
41175         this.value = v;
41176         
41177         if(this.rendered){
41178             
41179             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41180             
41181             this.inputEl().dom.value = (v == '') ? '' :
41182                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41183             
41184             if(!this.allowZero && v === '0') {
41185                 this.hiddenEl().dom.value = '';
41186                 this.inputEl().dom.value = '';
41187             }
41188             
41189             this.validate();
41190         }
41191     },
41192     
41193     getRawValue : function()
41194     {
41195         var v = this.inputEl().getValue();
41196         
41197         return v;
41198     },
41199     
41200     getValue : function()
41201     {
41202         return this.fixPrecision(this.parseValue(this.getRawValue()));
41203     },
41204     
41205     parseValue : function(value)
41206     {
41207         if(this.thousandsDelimiter) {
41208             value += "";
41209             r = new RegExp(",", "g");
41210             value = value.replace(r, "");
41211         }
41212         
41213         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41214         return isNaN(value) ? '' : value;
41215         
41216     },
41217     
41218     fixPrecision : function(value)
41219     {
41220         if(this.thousandsDelimiter) {
41221             value += "";
41222             r = new RegExp(",", "g");
41223             value = value.replace(r, "");
41224         }
41225         
41226         var nan = isNaN(value);
41227         
41228         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41229             return nan ? '' : value;
41230         }
41231         return parseFloat(value).toFixed(this.decimalPrecision);
41232     },
41233     
41234     decimalPrecisionFcn : function(v)
41235     {
41236         return Math.floor(v);
41237     },
41238     
41239     validateValue : function(value)
41240     {
41241         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41242             return false;
41243         }
41244         
41245         var num = this.parseValue(value);
41246         
41247         if(isNaN(num)){
41248             this.markInvalid(String.format(this.nanText, value));
41249             return false;
41250         }
41251         
41252         if(num < this.minValue){
41253             this.markInvalid(String.format(this.minText, this.minValue));
41254             return false;
41255         }
41256         
41257         if(num > this.maxValue){
41258             this.markInvalid(String.format(this.maxText, this.maxValue));
41259             return false;
41260         }
41261         
41262         return true;
41263     },
41264     
41265     validate : function()
41266     {
41267         if(this.disabled || this.allowBlank){
41268             this.markValid();
41269             return true;
41270         }
41271         
41272         var currency = this.getCurrency();
41273         
41274         if(this.validateValue(this.getRawValue()) && currency.length){
41275             this.markValid();
41276             return true;
41277         }
41278         
41279         this.markInvalid();
41280         return false;
41281     },
41282     
41283     getName: function()
41284     {
41285         return this.name;
41286     },
41287     
41288     beforeBlur : function()
41289     {
41290         if(!this.castInt){
41291             return;
41292         }
41293         
41294         var v = this.parseValue(this.getRawValue());
41295         
41296         if(v || v == 0){
41297             this.setValue(v);
41298         }
41299     },
41300     
41301     onBlur : function()
41302     {
41303         this.beforeBlur();
41304         
41305         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41306             //this.el.removeClass(this.focusClass);
41307         }
41308         
41309         this.hasFocus = false;
41310         
41311         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41312             this.validate();
41313         }
41314         
41315         var v = this.getValue();
41316         
41317         if(String(v) !== String(this.startValue)){
41318             this.fireEvent('change', this, v, this.startValue);
41319         }
41320         
41321         this.fireEvent("blur", this);
41322     },
41323     
41324     inputEl : function()
41325     {
41326         return this.el.select('.roo-money-amount-input', true).first();
41327     },
41328     
41329     currencyEl : function()
41330     {
41331         return this.el.select('.roo-money-currency-input', true).first();
41332     },
41333     
41334     hiddenEl : function()
41335     {
41336         return this.el.select('input.hidden-number-input',true).first();
41337     }
41338     
41339 });