sync
[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             
2921             var view_height = Roo.lib.Dom.getViewportHeight(true);
2922             
2923             Roo.log("dialog height: "+this.height);
2924             Roo.log("view height:" + view_height);
2925             
2926             this.setSize(
2927                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2928                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
2929             );
2930             return;
2931         }
2932         
2933         if(this.max_width !== 0) {
2934             
2935             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2936             
2937             if(this.height) {
2938                 this.setSize(w, this.height);
2939                 return;
2940             }
2941             
2942             if(this.max_height) {
2943                 this.setSize(w,Math.min(
2944                     this.max_height,
2945                     Roo.lib.Dom.getViewportHeight(true) - 60
2946                 ));
2947                 
2948                 return;
2949             }
2950             
2951             if(!this.fit_content) {
2952                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2953                 return;
2954             }
2955             
2956             this.setSize(w, Math.min(
2957                 60 +
2958                 this.headerEl.getHeight() + 
2959                 this.footerEl.getHeight() + 
2960                 this.getChildHeight(this.bodyEl.dom.childNodes),
2961                 Roo.lib.Dom.getViewportHeight(true) - 60)
2962             );
2963         }
2964         
2965     },
2966
2967     setSize : function(w,h)
2968     {
2969         if (!w && !h) {
2970             return;
2971         }
2972         
2973         this.resizeTo(w,h);
2974     },
2975
2976     show : function() {
2977
2978         if (!this.rendered) {
2979             this.render();
2980         }
2981
2982         //this.el.setStyle('display', 'block');
2983         this.el.removeClass('hideing');
2984         this.el.dom.style.display='block';
2985         
2986         Roo.get(document.body).addClass('modal-open');
2987  
2988         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2989             var _this = this;
2990             (function(){
2991                 this.el.addClass('show');
2992                 this.el.addClass('in');
2993             }).defer(50, this);
2994         }else{
2995             this.el.addClass('show');
2996             this.el.addClass('in');
2997         }
2998
2999         // not sure how we can show data in here..
3000         //if (this.tmpl) {
3001         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3002         //}
3003
3004         Roo.get(document.body).addClass("x-body-masked");
3005         
3006         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
3007         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3008         this.maskEl.dom.style.display = 'block';
3009         this.maskEl.addClass('show');
3010         
3011         
3012         this.resize();
3013         
3014         this.fireEvent('show', this);
3015
3016         // set zindex here - otherwise it appears to be ignored...
3017         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3018
3019         (function () {
3020             this.items.forEach( function(e) {
3021                 e.layout ? e.layout() : false;
3022
3023             });
3024         }).defer(100,this);
3025
3026     },
3027     hide : function()
3028     {
3029         if(this.fireEvent("beforehide", this) !== false){
3030             
3031             this.maskEl.removeClass('show');
3032             
3033             this.maskEl.dom.style.display = '';
3034             Roo.get(document.body).removeClass("x-body-masked");
3035             this.el.removeClass('in');
3036             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3037
3038             if(this.animate){ // why
3039                 this.el.addClass('hideing');
3040                 this.el.removeClass('show');
3041                 (function(){
3042                     if (!this.el.hasClass('hideing')) {
3043                         return; // it's been shown again...
3044                     }
3045                     
3046                     this.el.dom.style.display='';
3047
3048                     Roo.get(document.body).removeClass('modal-open');
3049                     this.el.removeClass('hideing');
3050                 }).defer(150,this);
3051                 
3052             }else{
3053                 this.el.removeClass('show');
3054                 this.el.dom.style.display='';
3055                 Roo.get(document.body).removeClass('modal-open');
3056
3057             }
3058             this.fireEvent('hide', this);
3059         }
3060     },
3061     isVisible : function()
3062     {
3063         
3064         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3065         
3066     },
3067
3068     addButton : function(str, cb)
3069     {
3070
3071
3072         var b = Roo.apply({}, { html : str } );
3073         b.xns = b.xns || Roo.bootstrap;
3074         b.xtype = b.xtype || 'Button';
3075         if (typeof(b.listeners) == 'undefined') {
3076             b.listeners = { click : cb.createDelegate(this)  };
3077         }
3078
3079         var btn = Roo.factory(b);
3080
3081         btn.render(this.getButtonContainer());
3082
3083         return btn;
3084
3085     },
3086
3087     setDefaultButton : function(btn)
3088     {
3089         //this.el.select('.modal-footer').()
3090     },
3091
3092     resizeTo: function(w,h)
3093     {
3094         this.dialogEl.setWidth(w);
3095         
3096         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 30; // dialog margin-bottom: 30  
3097
3098         this.bodyEl.setHeight(h - diff);
3099         
3100         this.fireEvent('resize', this);
3101     },
3102     
3103     setContentSize  : function(w, h)
3104     {
3105
3106     },
3107     onButtonClick: function(btn,e)
3108     {
3109         //Roo.log([a,b,c]);
3110         this.fireEvent('btnclick', btn.name, e);
3111     },
3112      /**
3113      * Set the title of the Dialog
3114      * @param {String} str new Title
3115      */
3116     setTitle: function(str) {
3117         this.titleEl.dom.innerHTML = str;
3118     },
3119     /**
3120      * Set the body of the Dialog
3121      * @param {String} str new Title
3122      */
3123     setBody: function(str) {
3124         this.bodyEl.dom.innerHTML = str;
3125     },
3126     /**
3127      * Set the body of the Dialog using the template
3128      * @param {Obj} data - apply this data to the template and replace the body contents.
3129      */
3130     applyBody: function(obj)
3131     {
3132         if (!this.tmpl) {
3133             Roo.log("Error - using apply Body without a template");
3134             //code
3135         }
3136         this.tmpl.overwrite(this.bodyEl, obj);
3137     },
3138     
3139     getChildHeight : function(child_nodes)
3140     {
3141         if(
3142             !child_nodes ||
3143             child_nodes.length == 0
3144         ) {
3145             return;
3146         }
3147         
3148         var child_height = 0;
3149         
3150         for(var i = 0; i < child_nodes.length; i++) {
3151             
3152             /*
3153             * for modal with tabs...
3154             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3155                 
3156                 var layout_childs = child_nodes[i].childNodes;
3157                 
3158                 for(var j = 0; j < layout_childs.length; j++) {
3159                     
3160                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3161                         
3162                         var layout_body_childs = layout_childs[j].childNodes;
3163                         
3164                         for(var k = 0; k < layout_body_childs.length; k++) {
3165                             
3166                             if(layout_body_childs[k].classList.contains('navbar')) {
3167                                 child_height += layout_body_childs[k].offsetHeight;
3168                                 continue;
3169                             }
3170                             
3171                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3172                                 
3173                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3174                                 
3175                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3176                                     
3177                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3178                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3179                                         continue;
3180                                     }
3181                                     
3182                                 }
3183                                 
3184                             }
3185                             
3186                         }
3187                     }
3188                 }
3189                 continue;
3190             }
3191             */
3192             
3193             child_height += child_nodes[i].offsetHeight;
3194             // Roo.log(child_nodes[i].offsetHeight);
3195         }
3196         
3197         return child_height;
3198     }
3199
3200 });
3201
3202
3203 Roo.apply(Roo.bootstrap.Modal,  {
3204     /**
3205          * Button config that displays a single OK button
3206          * @type Object
3207          */
3208         OK :  [{
3209             name : 'ok',
3210             weight : 'primary',
3211             html : 'OK'
3212         }],
3213         /**
3214          * Button config that displays Yes and No buttons
3215          * @type Object
3216          */
3217         YESNO : [
3218             {
3219                 name  : 'no',
3220                 html : 'No'
3221             },
3222             {
3223                 name  :'yes',
3224                 weight : 'primary',
3225                 html : 'Yes'
3226             }
3227         ],
3228
3229         /**
3230          * Button config that displays OK and Cancel buttons
3231          * @type Object
3232          */
3233         OKCANCEL : [
3234             {
3235                name : 'cancel',
3236                 html : 'Cancel'
3237             },
3238             {
3239                 name : 'ok',
3240                 weight : 'primary',
3241                 html : 'OK'
3242             }
3243         ],
3244         /**
3245          * Button config that displays Yes, No and Cancel buttons
3246          * @type Object
3247          */
3248         YESNOCANCEL : [
3249             {
3250                 name : 'yes',
3251                 weight : 'primary',
3252                 html : 'Yes'
3253             },
3254             {
3255                 name : 'no',
3256                 html : 'No'
3257             },
3258             {
3259                 name : 'cancel',
3260                 html : 'Cancel'
3261             }
3262         ],
3263         
3264         zIndex : 10001
3265 });
3266 /*
3267  * - LGPL
3268  *
3269  * messagebox - can be used as a replace
3270  * 
3271  */
3272 /**
3273  * @class Roo.MessageBox
3274  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3275  * Example usage:
3276  *<pre><code>
3277 // Basic alert:
3278 Roo.Msg.alert('Status', 'Changes saved successfully.');
3279
3280 // Prompt for user data:
3281 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3282     if (btn == 'ok'){
3283         // process text value...
3284     }
3285 });
3286
3287 // Show a dialog using config options:
3288 Roo.Msg.show({
3289    title:'Save Changes?',
3290    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3291    buttons: Roo.Msg.YESNOCANCEL,
3292    fn: processResult,
3293    animEl: 'elId'
3294 });
3295 </code></pre>
3296  * @singleton
3297  */
3298 Roo.bootstrap.MessageBox = function(){
3299     var dlg, opt, mask, waitTimer;
3300     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3301     var buttons, activeTextEl, bwidth;
3302
3303     
3304     // private
3305     var handleButton = function(button){
3306         dlg.hide();
3307         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3308     };
3309
3310     // private
3311     var handleHide = function(){
3312         if(opt && opt.cls){
3313             dlg.el.removeClass(opt.cls);
3314         }
3315         //if(waitTimer){
3316         //    Roo.TaskMgr.stop(waitTimer);
3317         //    waitTimer = null;
3318         //}
3319     };
3320
3321     // private
3322     var updateButtons = function(b){
3323         var width = 0;
3324         if(!b){
3325             buttons["ok"].hide();
3326             buttons["cancel"].hide();
3327             buttons["yes"].hide();
3328             buttons["no"].hide();
3329             dlg.footerEl.hide();
3330             
3331             return width;
3332         }
3333         dlg.footerEl.show();
3334         for(var k in buttons){
3335             if(typeof buttons[k] != "function"){
3336                 if(b[k]){
3337                     buttons[k].show();
3338                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3339                     width += buttons[k].el.getWidth()+15;
3340                 }else{
3341                     buttons[k].hide();
3342                 }
3343             }
3344         }
3345         return width;
3346     };
3347
3348     // private
3349     var handleEsc = function(d, k, e){
3350         if(opt && opt.closable !== false){
3351             dlg.hide();
3352         }
3353         if(e){
3354             e.stopEvent();
3355         }
3356     };
3357
3358     return {
3359         /**
3360          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3361          * @return {Roo.BasicDialog} The BasicDialog element
3362          */
3363         getDialog : function(){
3364            if(!dlg){
3365                 dlg = new Roo.bootstrap.Modal( {
3366                     //draggable: true,
3367                     //resizable:false,
3368                     //constraintoviewport:false,
3369                     //fixedcenter:true,
3370                     //collapsible : false,
3371                     //shim:true,
3372                     //modal: true,
3373                 //    width: 'auto',
3374                   //  height:100,
3375                     //buttonAlign:"center",
3376                     closeClick : function(){
3377                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3378                             handleButton("no");
3379                         }else{
3380                             handleButton("cancel");
3381                         }
3382                     }
3383                 });
3384                 dlg.render();
3385                 dlg.on("hide", handleHide);
3386                 mask = dlg.mask;
3387                 //dlg.addKeyListener(27, handleEsc);
3388                 buttons = {};
3389                 this.buttons = buttons;
3390                 var bt = this.buttonText;
3391                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3392                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3393                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3394                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3395                 //Roo.log(buttons);
3396                 bodyEl = dlg.bodyEl.createChild({
3397
3398                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3399                         '<textarea class="roo-mb-textarea"></textarea>' +
3400                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3401                 });
3402                 msgEl = bodyEl.dom.firstChild;
3403                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3404                 textboxEl.enableDisplayMode();
3405                 textboxEl.addKeyListener([10,13], function(){
3406                     if(dlg.isVisible() && opt && opt.buttons){
3407                         if(opt.buttons.ok){
3408                             handleButton("ok");
3409                         }else if(opt.buttons.yes){
3410                             handleButton("yes");
3411                         }
3412                     }
3413                 });
3414                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3415                 textareaEl.enableDisplayMode();
3416                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3417                 progressEl.enableDisplayMode();
3418                 
3419                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3420                 var pf = progressEl.dom.firstChild;
3421                 if (pf) {
3422                     pp = Roo.get(pf.firstChild);
3423                     pp.setHeight(pf.offsetHeight);
3424                 }
3425                 
3426             }
3427             return dlg;
3428         },
3429
3430         /**
3431          * Updates the message box body text
3432          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3433          * the XHTML-compliant non-breaking space character '&amp;#160;')
3434          * @return {Roo.MessageBox} This message box
3435          */
3436         updateText : function(text)
3437         {
3438             if(!dlg.isVisible() && !opt.width){
3439                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3440                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3441             }
3442             msgEl.innerHTML = text || '&#160;';
3443       
3444             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3445             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3446             var w = Math.max(
3447                     Math.min(opt.width || cw , this.maxWidth), 
3448                     Math.max(opt.minWidth || this.minWidth, bwidth)
3449             );
3450             if(opt.prompt){
3451                 activeTextEl.setWidth(w);
3452             }
3453             if(dlg.isVisible()){
3454                 dlg.fixedcenter = false;
3455             }
3456             // to big, make it scroll. = But as usual stupid IE does not support
3457             // !important..
3458             
3459             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3460                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3461                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3462             } else {
3463                 bodyEl.dom.style.height = '';
3464                 bodyEl.dom.style.overflowY = '';
3465             }
3466             if (cw > w) {
3467                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3468             } else {
3469                 bodyEl.dom.style.overflowX = '';
3470             }
3471             
3472             dlg.setContentSize(w, bodyEl.getHeight());
3473             if(dlg.isVisible()){
3474                 dlg.fixedcenter = true;
3475             }
3476             return this;
3477         },
3478
3479         /**
3480          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3481          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3482          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3483          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3484          * @return {Roo.MessageBox} This message box
3485          */
3486         updateProgress : function(value, text){
3487             if(text){
3488                 this.updateText(text);
3489             }
3490             
3491             if (pp) { // weird bug on my firefox - for some reason this is not defined
3492                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3493                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3494             }
3495             return this;
3496         },        
3497
3498         /**
3499          * Returns true if the message box is currently displayed
3500          * @return {Boolean} True if the message box is visible, else false
3501          */
3502         isVisible : function(){
3503             return dlg && dlg.isVisible();  
3504         },
3505
3506         /**
3507          * Hides the message box if it is displayed
3508          */
3509         hide : function(){
3510             if(this.isVisible()){
3511                 dlg.hide();
3512             }  
3513         },
3514
3515         /**
3516          * Displays a new message box, or reinitializes an existing message box, based on the config options
3517          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3518          * The following config object properties are supported:
3519          * <pre>
3520 Property    Type             Description
3521 ----------  ---------------  ------------------------------------------------------------------------------------
3522 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3523                                    closes (defaults to undefined)
3524 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3525                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3526 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3527                                    progress and wait dialogs will ignore this property and always hide the
3528                                    close button as they can only be closed programmatically.
3529 cls               String           A custom CSS class to apply to the message box element
3530 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3531                                    displayed (defaults to 75)
3532 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3533                                    function will be btn (the name of the button that was clicked, if applicable,
3534                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3535                                    Progress and wait dialogs will ignore this option since they do not respond to
3536                                    user actions and can only be closed programmatically, so any required function
3537                                    should be called by the same code after it closes the dialog.
3538 icon              String           A CSS class that provides a background image to be used as an icon for
3539                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3540 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3541 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3542 modal             Boolean          False to allow user interaction with the page while the message box is
3543                                    displayed (defaults to true)
3544 msg               String           A string that will replace the existing message box body text (defaults
3545                                    to the XHTML-compliant non-breaking space character '&#160;')
3546 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3547 progress          Boolean          True to display a progress bar (defaults to false)
3548 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3549 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3550 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3551 title             String           The title text
3552 value             String           The string value to set into the active textbox element if displayed
3553 wait              Boolean          True to display a progress bar (defaults to false)
3554 width             Number           The width of the dialog in pixels
3555 </pre>
3556          *
3557          * Example usage:
3558          * <pre><code>
3559 Roo.Msg.show({
3560    title: 'Address',
3561    msg: 'Please enter your address:',
3562    width: 300,
3563    buttons: Roo.MessageBox.OKCANCEL,
3564    multiline: true,
3565    fn: saveAddress,
3566    animEl: 'addAddressBtn'
3567 });
3568 </code></pre>
3569          * @param {Object} config Configuration options
3570          * @return {Roo.MessageBox} This message box
3571          */
3572         show : function(options)
3573         {
3574             
3575             // this causes nightmares if you show one dialog after another
3576             // especially on callbacks..
3577              
3578             if(this.isVisible()){
3579                 
3580                 this.hide();
3581                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3582                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3583                 Roo.log("New Dialog Message:" +  options.msg )
3584                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3585                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3586                 
3587             }
3588             var d = this.getDialog();
3589             opt = options;
3590             d.setTitle(opt.title || "&#160;");
3591             d.closeEl.setDisplayed(opt.closable !== false);
3592             activeTextEl = textboxEl;
3593             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3594             if(opt.prompt){
3595                 if(opt.multiline){
3596                     textboxEl.hide();
3597                     textareaEl.show();
3598                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3599                         opt.multiline : this.defaultTextHeight);
3600                     activeTextEl = textareaEl;
3601                 }else{
3602                     textboxEl.show();
3603                     textareaEl.hide();
3604                 }
3605             }else{
3606                 textboxEl.hide();
3607                 textareaEl.hide();
3608             }
3609             progressEl.setDisplayed(opt.progress === true);
3610             if (opt.progress) {
3611                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3612             }
3613             this.updateProgress(0);
3614             activeTextEl.dom.value = opt.value || "";
3615             if(opt.prompt){
3616                 dlg.setDefaultButton(activeTextEl);
3617             }else{
3618                 var bs = opt.buttons;
3619                 var db = null;
3620                 if(bs && bs.ok){
3621                     db = buttons["ok"];
3622                 }else if(bs && bs.yes){
3623                     db = buttons["yes"];
3624                 }
3625                 dlg.setDefaultButton(db);
3626             }
3627             bwidth = updateButtons(opt.buttons);
3628             this.updateText(opt.msg);
3629             if(opt.cls){
3630                 d.el.addClass(opt.cls);
3631             }
3632             d.proxyDrag = opt.proxyDrag === true;
3633             d.modal = opt.modal !== false;
3634             d.mask = opt.modal !== false ? mask : false;
3635             if(!d.isVisible()){
3636                 // force it to the end of the z-index stack so it gets a cursor in FF
3637                 document.body.appendChild(dlg.el.dom);
3638                 d.animateTarget = null;
3639                 d.show(options.animEl);
3640             }
3641             return this;
3642         },
3643
3644         /**
3645          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3646          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3647          * and closing the message box when the process is complete.
3648          * @param {String} title The title bar text
3649          * @param {String} msg The message box body text
3650          * @return {Roo.MessageBox} This message box
3651          */
3652         progress : function(title, msg){
3653             this.show({
3654                 title : title,
3655                 msg : msg,
3656                 buttons: false,
3657                 progress:true,
3658                 closable:false,
3659                 minWidth: this.minProgressWidth,
3660                 modal : true
3661             });
3662             return this;
3663         },
3664
3665         /**
3666          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3667          * If a callback function is passed it will be called after the user clicks the button, and the
3668          * id of the button that was clicked will be passed as the only parameter to the callback
3669          * (could also be the top-right close button).
3670          * @param {String} title The title bar text
3671          * @param {String} msg The message box body text
3672          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3673          * @param {Object} scope (optional) The scope of the callback function
3674          * @return {Roo.MessageBox} This message box
3675          */
3676         alert : function(title, msg, fn, scope)
3677         {
3678             this.show({
3679                 title : title,
3680                 msg : msg,
3681                 buttons: this.OK,
3682                 fn: fn,
3683                 closable : false,
3684                 scope : scope,
3685                 modal : true
3686             });
3687             return this;
3688         },
3689
3690         /**
3691          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3692          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3693          * You are responsible for closing the message box when the process is complete.
3694          * @param {String} msg The message box body text
3695          * @param {String} title (optional) The title bar text
3696          * @return {Roo.MessageBox} This message box
3697          */
3698         wait : function(msg, title){
3699             this.show({
3700                 title : title,
3701                 msg : msg,
3702                 buttons: false,
3703                 closable:false,
3704                 progress:true,
3705                 modal:true,
3706                 width:300,
3707                 wait:true
3708             });
3709             waitTimer = Roo.TaskMgr.start({
3710                 run: function(i){
3711                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3712                 },
3713                 interval: 1000
3714             });
3715             return this;
3716         },
3717
3718         /**
3719          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3720          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3721          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3722          * @param {String} title The title bar text
3723          * @param {String} msg The message box body text
3724          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3725          * @param {Object} scope (optional) The scope of the callback function
3726          * @return {Roo.MessageBox} This message box
3727          */
3728         confirm : function(title, msg, fn, scope){
3729             this.show({
3730                 title : title,
3731                 msg : msg,
3732                 buttons: this.YESNO,
3733                 fn: fn,
3734                 scope : scope,
3735                 modal : true
3736             });
3737             return this;
3738         },
3739
3740         /**
3741          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3742          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3743          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3744          * (could also be the top-right close button) and the text that was entered will be passed as the two
3745          * parameters to the callback.
3746          * @param {String} title The title bar text
3747          * @param {String} msg The message box body text
3748          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3749          * @param {Object} scope (optional) The scope of the callback function
3750          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3751          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3752          * @return {Roo.MessageBox} This message box
3753          */
3754         prompt : function(title, msg, fn, scope, multiline){
3755             this.show({
3756                 title : title,
3757                 msg : msg,
3758                 buttons: this.OKCANCEL,
3759                 fn: fn,
3760                 minWidth:250,
3761                 scope : scope,
3762                 prompt:true,
3763                 multiline: multiline,
3764                 modal : true
3765             });
3766             return this;
3767         },
3768
3769         /**
3770          * Button config that displays a single OK button
3771          * @type Object
3772          */
3773         OK : {ok:true},
3774         /**
3775          * Button config that displays Yes and No buttons
3776          * @type Object
3777          */
3778         YESNO : {yes:true, no:true},
3779         /**
3780          * Button config that displays OK and Cancel buttons
3781          * @type Object
3782          */
3783         OKCANCEL : {ok:true, cancel:true},
3784         /**
3785          * Button config that displays Yes, No and Cancel buttons
3786          * @type Object
3787          */
3788         YESNOCANCEL : {yes:true, no:true, cancel:true},
3789
3790         /**
3791          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3792          * @type Number
3793          */
3794         defaultTextHeight : 75,
3795         /**
3796          * The maximum width in pixels of the message box (defaults to 600)
3797          * @type Number
3798          */
3799         maxWidth : 600,
3800         /**
3801          * The minimum width in pixels of the message box (defaults to 100)
3802          * @type Number
3803          */
3804         minWidth : 100,
3805         /**
3806          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3807          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3808          * @type Number
3809          */
3810         minProgressWidth : 250,
3811         /**
3812          * An object containing the default button text strings that can be overriden for localized language support.
3813          * Supported properties are: ok, cancel, yes and no.
3814          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3815          * @type Object
3816          */
3817         buttonText : {
3818             ok : "OK",
3819             cancel : "Cancel",
3820             yes : "Yes",
3821             no : "No"
3822         }
3823     };
3824 }();
3825
3826 /**
3827  * Shorthand for {@link Roo.MessageBox}
3828  */
3829 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3830 Roo.Msg = Roo.Msg || Roo.MessageBox;
3831 /*
3832  * - LGPL
3833  *
3834  * navbar
3835  * 
3836  */
3837
3838 /**
3839  * @class Roo.bootstrap.Navbar
3840  * @extends Roo.bootstrap.Component
3841  * Bootstrap Navbar class
3842
3843  * @constructor
3844  * Create a new Navbar
3845  * @param {Object} config The config object
3846  */
3847
3848
3849 Roo.bootstrap.Navbar = function(config){
3850     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3851     this.addEvents({
3852         // raw events
3853         /**
3854          * @event beforetoggle
3855          * Fire before toggle the menu
3856          * @param {Roo.EventObject} e
3857          */
3858         "beforetoggle" : true
3859     });
3860 };
3861
3862 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3863     
3864     
3865    
3866     // private
3867     navItems : false,
3868     loadMask : false,
3869     
3870     
3871     getAutoCreate : function(){
3872         
3873         
3874         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3875         
3876     },
3877     
3878     initEvents :function ()
3879     {
3880         //Roo.log(this.el.select('.navbar-toggle',true));
3881         this.el.select('.navbar-toggle',true).on('click', function() {
3882             if(this.fireEvent('beforetoggle', this) !== false){
3883                 var ce = this.el.select('.navbar-collapse',true).first();
3884                 ce.toggleClass('in'); // old...
3885                 if (ce.hasClass('collapse')) {
3886                     // show it...
3887                     ce.removeClass('collapse');
3888                     ce.addClass('show');
3889                     var h = ce.getHeight();
3890                     Roo.log(h);
3891                     ce.removeClass('show');
3892                     // at this point we should be able to see it..
3893                     ce.addClass('collapsing');
3894                     
3895                     ce.setHeight(0); // resize it ...
3896                     ce.on('transitionend', function() {
3897                         Roo.log('done transition');
3898                         ce.removeClass('collapsing');
3899                         ce.addClass('show');
3900                         ce.removeClass('collapse');
3901
3902                         ce.dom.style.height = '';
3903                     }, this, { single: true} );
3904                     ce.setHeight(h);
3905                     
3906                 } else {
3907                     ce.setHeight(ce.getHeight());
3908                     ce.removeClass('show');
3909                     ce.addClass('collapsing');
3910                     
3911                     ce.on('transitionend', function() {
3912                         ce.dom.style.height = '';
3913                         ce.removeClass('collapsing');
3914                         ce.addClass('collapse');
3915                     }, this, { single: true} );
3916                     ce.setHeight(0);
3917                 }
3918             }
3919             
3920         }, this);
3921         
3922         var mark = {
3923             tag: "div",
3924             cls:"x-dlg-mask"
3925         };
3926         
3927         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3928         
3929         var size = this.el.getSize();
3930         this.maskEl.setSize(size.width, size.height);
3931         this.maskEl.enableDisplayMode("block");
3932         this.maskEl.hide();
3933         
3934         if(this.loadMask){
3935             this.maskEl.show();
3936         }
3937     },
3938     
3939     
3940     getChildContainer : function()
3941     {
3942         if (this.el.select('.collapse').getCount()) {
3943             return this.el.select('.collapse',true).first();
3944         }
3945         
3946         return this.el;
3947     },
3948     
3949     mask : function()
3950     {
3951         this.maskEl.show();
3952     },
3953     
3954     unmask : function()
3955     {
3956         this.maskEl.hide();
3957     } 
3958     
3959     
3960     
3961     
3962 });
3963
3964
3965
3966  
3967
3968  /*
3969  * - LGPL
3970  *
3971  * navbar
3972  * 
3973  */
3974
3975 /**
3976  * @class Roo.bootstrap.NavSimplebar
3977  * @extends Roo.bootstrap.Navbar
3978  * Bootstrap Sidebar class
3979  *
3980  * @cfg {Boolean} inverse is inverted color
3981  * 
3982  * @cfg {String} type (nav | pills | tabs)
3983  * @cfg {Boolean} arrangement stacked | justified
3984  * @cfg {String} align (left | right) alignment
3985  * 
3986  * @cfg {Boolean} main (true|false) main nav bar? default false
3987  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3988  * 
3989  * @cfg {String} tag (header|footer|nav|div) default is nav 
3990
3991  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3992  * 
3993  * 
3994  * @constructor
3995  * Create a new Sidebar
3996  * @param {Object} config The config object
3997  */
3998
3999
4000 Roo.bootstrap.NavSimplebar = function(config){
4001     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4002 };
4003
4004 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
4005     
4006     inverse: false,
4007     
4008     type: false,
4009     arrangement: '',
4010     align : false,
4011     
4012     weight : 'light',
4013     
4014     main : false,
4015     
4016     
4017     tag : false,
4018     
4019     
4020     getAutoCreate : function(){
4021         
4022         
4023         var cfg = {
4024             tag : this.tag || 'div',
4025             cls : 'navbar navbar-expand-lg roo-navbar-simple'
4026         };
4027         if (['light','white'].indexOf(this.weight) > -1) {
4028             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4029         }
4030         cfg.cls += ' bg-' + this.weight;
4031         
4032         if (this.inverse) {
4033             cfg.cls += ' navbar-inverse';
4034             
4035         }
4036         
4037         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4038         
4039         //if (Roo.bootstrap.version == 4) {
4040         //    return cfg;
4041         //}
4042         
4043         cfg.cn = [
4044             {
4045                 cls: 'nav',
4046                 tag : 'ul'
4047             }
4048         ];
4049         
4050          
4051         this.type = this.type || 'nav';
4052         if (['tabs','pills'].indexOf(this.type) != -1) {
4053             cfg.cn[0].cls += ' nav-' + this.type
4054         
4055         
4056         } else {
4057             if (this.type!=='nav') {
4058                 Roo.log('nav type must be nav/tabs/pills')
4059             }
4060             cfg.cn[0].cls += ' navbar-nav'
4061         }
4062         
4063         
4064         
4065         
4066         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4067             cfg.cn[0].cls += ' nav-' + this.arrangement;
4068         }
4069         
4070         
4071         if (this.align === 'right') {
4072             cfg.cn[0].cls += ' navbar-right';
4073         }
4074         
4075         
4076         
4077         
4078         return cfg;
4079     
4080         
4081     }
4082     
4083     
4084     
4085 });
4086
4087
4088
4089  
4090
4091  
4092        /*
4093  * - LGPL
4094  *
4095  * navbar
4096  * navbar-fixed-top
4097  * navbar-expand-md  fixed-top 
4098  */
4099
4100 /**
4101  * @class Roo.bootstrap.NavHeaderbar
4102  * @extends Roo.bootstrap.NavSimplebar
4103  * Bootstrap Sidebar class
4104  *
4105  * @cfg {String} brand what is brand
4106  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4107  * @cfg {String} brand_href href of the brand
4108  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4109  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4110  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4111  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4112  * 
4113  * @constructor
4114  * Create a new Sidebar
4115  * @param {Object} config The config object
4116  */
4117
4118
4119 Roo.bootstrap.NavHeaderbar = function(config){
4120     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4121       
4122 };
4123
4124 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4125     
4126     position: '',
4127     brand: '',
4128     brand_href: false,
4129     srButton : true,
4130     autohide : false,
4131     desktopCenter : false,
4132    
4133     
4134     getAutoCreate : function(){
4135         
4136         var   cfg = {
4137             tag: this.nav || 'nav',
4138             cls: 'navbar navbar-expand-md',
4139             role: 'navigation',
4140             cn: []
4141         };
4142         
4143         var cn = cfg.cn;
4144         if (this.desktopCenter) {
4145             cn.push({cls : 'container', cn : []});
4146             cn = cn[0].cn;
4147         }
4148         
4149         if(this.srButton){
4150             var btn = {
4151                 tag: 'button',
4152                 type: 'button',
4153                 cls: 'navbar-toggle navbar-toggler',
4154                 'data-toggle': 'collapse',
4155                 cn: [
4156                     {
4157                         tag: 'span',
4158                         cls: 'sr-only',
4159                         html: 'Toggle navigation'
4160                     },
4161                     {
4162                         tag: 'span',
4163                         cls: 'icon-bar navbar-toggler-icon'
4164                     },
4165                     {
4166                         tag: 'span',
4167                         cls: 'icon-bar'
4168                     },
4169                     {
4170                         tag: 'span',
4171                         cls: 'icon-bar'
4172                     }
4173                 ]
4174             };
4175             
4176             cn.push( Roo.bootstrap.version == 4 ? btn : {
4177                 tag: 'div',
4178                 cls: 'navbar-header',
4179                 cn: [
4180                     btn
4181                 ]
4182             });
4183         }
4184         
4185         cn.push({
4186             tag: 'div',
4187             cls: 'collapse navbar-collapse',
4188             cn : []
4189         });
4190         
4191         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4192         
4193         if (['light','white'].indexOf(this.weight) > -1) {
4194             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4195         }
4196         cfg.cls += ' bg-' + this.weight;
4197         
4198         
4199         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4200             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4201             
4202             // tag can override this..
4203             
4204             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4205         }
4206         
4207         if (this.brand !== '') {
4208             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4209             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4210                 tag: 'a',
4211                 href: this.brand_href ? this.brand_href : '#',
4212                 cls: 'navbar-brand',
4213                 cn: [
4214                 this.brand
4215                 ]
4216             });
4217         }
4218         
4219         if(this.main){
4220             cfg.cls += ' main-nav';
4221         }
4222         
4223         
4224         return cfg;
4225
4226         
4227     },
4228     getHeaderChildContainer : function()
4229     {
4230         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4231             return this.el.select('.navbar-header',true).first();
4232         }
4233         
4234         return this.getChildContainer();
4235     },
4236     
4237     
4238     initEvents : function()
4239     {
4240         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4241         
4242         if (this.autohide) {
4243             
4244             var prevScroll = 0;
4245             var ft = this.el;
4246             
4247             Roo.get(document).on('scroll',function(e) {
4248                 var ns = Roo.get(document).getScroll().top;
4249                 var os = prevScroll;
4250                 prevScroll = ns;
4251                 
4252                 if(ns > os){
4253                     ft.removeClass('slideDown');
4254                     ft.addClass('slideUp');
4255                     return;
4256                 }
4257                 ft.removeClass('slideUp');
4258                 ft.addClass('slideDown');
4259                  
4260               
4261           },this);
4262         }
4263     }    
4264     
4265 });
4266
4267
4268
4269  
4270
4271  /*
4272  * - LGPL
4273  *
4274  * navbar
4275  * 
4276  */
4277
4278 /**
4279  * @class Roo.bootstrap.NavSidebar
4280  * @extends Roo.bootstrap.Navbar
4281  * Bootstrap Sidebar class
4282  * 
4283  * @constructor
4284  * Create a new Sidebar
4285  * @param {Object} config The config object
4286  */
4287
4288
4289 Roo.bootstrap.NavSidebar = function(config){
4290     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4291 };
4292
4293 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4294     
4295     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4296     
4297     getAutoCreate : function(){
4298         
4299         
4300         return  {
4301             tag: 'div',
4302             cls: 'sidebar sidebar-nav'
4303         };
4304     
4305         
4306     }
4307     
4308     
4309     
4310 });
4311
4312
4313
4314  
4315
4316  /*
4317  * - LGPL
4318  *
4319  * nav group
4320  * 
4321  */
4322
4323 /**
4324  * @class Roo.bootstrap.NavGroup
4325  * @extends Roo.bootstrap.Component
4326  * Bootstrap NavGroup class
4327  * @cfg {String} align (left|right)
4328  * @cfg {Boolean} inverse
4329  * @cfg {String} type (nav|pills|tab) default nav
4330  * @cfg {String} navId - reference Id for navbar.
4331
4332  * 
4333  * @constructor
4334  * Create a new nav group
4335  * @param {Object} config The config object
4336  */
4337
4338 Roo.bootstrap.NavGroup = function(config){
4339     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4340     this.navItems = [];
4341    
4342     Roo.bootstrap.NavGroup.register(this);
4343      this.addEvents({
4344         /**
4345              * @event changed
4346              * Fires when the active item changes
4347              * @param {Roo.bootstrap.NavGroup} this
4348              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4349              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4350          */
4351         'changed': true
4352      });
4353     
4354 };
4355
4356 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4357     
4358     align: '',
4359     inverse: false,
4360     form: false,
4361     type: 'nav',
4362     navId : '',
4363     // private
4364     
4365     navItems : false, 
4366     
4367     getAutoCreate : function()
4368     {
4369         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4370         
4371         cfg = {
4372             tag : 'ul',
4373             cls: 'nav' 
4374         };
4375         if (Roo.bootstrap.version == 4) {
4376             if (['tabs','pills'].indexOf(this.type) != -1) {
4377                 cfg.cls += ' nav-' + this.type; 
4378             } else {
4379                 cfg.cls += ' navbar-nav';
4380             }
4381         } else {
4382             if (['tabs','pills'].indexOf(this.type) != -1) {
4383                 cfg.cls += ' nav-' + this.type
4384             } else {
4385                 if (this.type !== 'nav') {
4386                     Roo.log('nav type must be nav/tabs/pills')
4387                 }
4388                 cfg.cls += ' navbar-nav'
4389             }
4390         }
4391         
4392         if (this.parent() && this.parent().sidebar) {
4393             cfg = {
4394                 tag: 'ul',
4395                 cls: 'dashboard-menu sidebar-menu'
4396             };
4397             
4398             return cfg;
4399         }
4400         
4401         if (this.form === true) {
4402             cfg = {
4403                 tag: 'form',
4404                 cls: 'navbar-form form-inline'
4405             };
4406             
4407             if (this.align === 'right') {
4408                 cfg.cls += ' navbar-right ml-md-auto';
4409             } else {
4410                 cfg.cls += ' navbar-left';
4411             }
4412         }
4413         
4414         if (this.align === 'right') {
4415             cfg.cls += ' navbar-right ml-md-auto';
4416         } else {
4417             cfg.cls += ' mr-auto';
4418         }
4419         
4420         if (this.inverse) {
4421             cfg.cls += ' navbar-inverse';
4422             
4423         }
4424         
4425         
4426         return cfg;
4427     },
4428     /**
4429     * sets the active Navigation item
4430     * @param {Roo.bootstrap.NavItem} the new current navitem
4431     */
4432     setActiveItem : function(item)
4433     {
4434         var prev = false;
4435         Roo.each(this.navItems, function(v){
4436             if (v == item) {
4437                 return ;
4438             }
4439             if (v.isActive()) {
4440                 v.setActive(false, true);
4441                 prev = v;
4442                 
4443             }
4444             
4445         });
4446
4447         item.setActive(true, true);
4448         this.fireEvent('changed', this, item, prev);
4449         
4450         
4451     },
4452     /**
4453     * gets the active Navigation item
4454     * @return {Roo.bootstrap.NavItem} the current navitem
4455     */
4456     getActive : function()
4457     {
4458         
4459         var prev = false;
4460         Roo.each(this.navItems, function(v){
4461             
4462             if (v.isActive()) {
4463                 prev = v;
4464                 
4465             }
4466             
4467         });
4468         return prev;
4469     },
4470     
4471     indexOfNav : function()
4472     {
4473         
4474         var prev = false;
4475         Roo.each(this.navItems, function(v,i){
4476             
4477             if (v.isActive()) {
4478                 prev = i;
4479                 
4480             }
4481             
4482         });
4483         return prev;
4484     },
4485     /**
4486     * adds a Navigation item
4487     * @param {Roo.bootstrap.NavItem} the navitem to add
4488     */
4489     addItem : function(cfg)
4490     {
4491         if (this.form && Roo.bootstrap.version == 4) {
4492             cfg.tag = 'div';
4493         }
4494         var cn = new Roo.bootstrap.NavItem(cfg);
4495         this.register(cn);
4496         cn.parentId = this.id;
4497         cn.onRender(this.el, null);
4498         return cn;
4499     },
4500     /**
4501     * register a Navigation item
4502     * @param {Roo.bootstrap.NavItem} the navitem to add
4503     */
4504     register : function(item)
4505     {
4506         this.navItems.push( item);
4507         item.navId = this.navId;
4508     
4509     },
4510     
4511     /**
4512     * clear all the Navigation item
4513     */
4514    
4515     clearAll : function()
4516     {
4517         this.navItems = [];
4518         this.el.dom.innerHTML = '';
4519     },
4520     
4521     getNavItem: function(tabId)
4522     {
4523         var ret = false;
4524         Roo.each(this.navItems, function(e) {
4525             if (e.tabId == tabId) {
4526                ret =  e;
4527                return false;
4528             }
4529             return true;
4530             
4531         });
4532         return ret;
4533     },
4534     
4535     setActiveNext : function()
4536     {
4537         var i = this.indexOfNav(this.getActive());
4538         if (i > this.navItems.length) {
4539             return;
4540         }
4541         this.setActiveItem(this.navItems[i+1]);
4542     },
4543     setActivePrev : function()
4544     {
4545         var i = this.indexOfNav(this.getActive());
4546         if (i  < 1) {
4547             return;
4548         }
4549         this.setActiveItem(this.navItems[i-1]);
4550     },
4551     clearWasActive : function(except) {
4552         Roo.each(this.navItems, function(e) {
4553             if (e.tabId != except.tabId && e.was_active) {
4554                e.was_active = false;
4555                return false;
4556             }
4557             return true;
4558             
4559         });
4560     },
4561     getWasActive : function ()
4562     {
4563         var r = false;
4564         Roo.each(this.navItems, function(e) {
4565             if (e.was_active) {
4566                r = e;
4567                return false;
4568             }
4569             return true;
4570             
4571         });
4572         return r;
4573     }
4574     
4575     
4576 });
4577
4578  
4579 Roo.apply(Roo.bootstrap.NavGroup, {
4580     
4581     groups: {},
4582      /**
4583     * register a Navigation Group
4584     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4585     */
4586     register : function(navgrp)
4587     {
4588         this.groups[navgrp.navId] = navgrp;
4589         
4590     },
4591     /**
4592     * fetch a Navigation Group based on the navigation ID
4593     * @param {string} the navgroup to add
4594     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4595     */
4596     get: function(navId) {
4597         if (typeof(this.groups[navId]) == 'undefined') {
4598             return false;
4599             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4600         }
4601         return this.groups[navId] ;
4602     }
4603     
4604     
4605     
4606 });
4607
4608  /*
4609  * - LGPL
4610  *
4611  * row
4612  * 
4613  */
4614
4615 /**
4616  * @class Roo.bootstrap.NavItem
4617  * @extends Roo.bootstrap.Component
4618  * Bootstrap Navbar.NavItem class
4619  * @cfg {String} href  link to
4620  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
4621
4622  * @cfg {String} html content of button
4623  * @cfg {String} badge text inside badge
4624  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4625  * @cfg {String} glyphicon DEPRICATED - use fa
4626  * @cfg {String} icon DEPRICATED - use fa
4627  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4628  * @cfg {Boolean} active Is item active
4629  * @cfg {Boolean} disabled Is item disabled
4630  
4631  * @cfg {Boolean} preventDefault (true | false) default false
4632  * @cfg {String} tabId the tab that this item activates.
4633  * @cfg {String} tagtype (a|span) render as a href or span?
4634  * @cfg {Boolean} animateRef (true|false) link to element default false  
4635   
4636  * @constructor
4637  * Create a new Navbar Item
4638  * @param {Object} config The config object
4639  */
4640 Roo.bootstrap.NavItem = function(config){
4641     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4642     this.addEvents({
4643         // raw events
4644         /**
4645          * @event click
4646          * The raw click event for the entire grid.
4647          * @param {Roo.EventObject} e
4648          */
4649         "click" : true,
4650          /**
4651             * @event changed
4652             * Fires when the active item active state changes
4653             * @param {Roo.bootstrap.NavItem} this
4654             * @param {boolean} state the new state
4655              
4656          */
4657         'changed': true,
4658         /**
4659             * @event scrollto
4660             * Fires when scroll to element
4661             * @param {Roo.bootstrap.NavItem} this
4662             * @param {Object} options
4663             * @param {Roo.EventObject} e
4664              
4665          */
4666         'scrollto': true
4667     });
4668    
4669 };
4670
4671 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4672     
4673     href: false,
4674     html: '',
4675     badge: '',
4676     icon: false,
4677     fa : false,
4678     glyphicon: false,
4679     active: false,
4680     preventDefault : false,
4681     tabId : false,
4682     tagtype : 'a',
4683     tag: 'li',
4684     disabled : false,
4685     animateRef : false,
4686     was_active : false,
4687     button_weight : '',
4688     button_outline : false,
4689     
4690     navLink: false,
4691     
4692     getAutoCreate : function(){
4693          
4694         var cfg = {
4695             tag: this.tag,
4696             cls: 'nav-item'
4697         };
4698         
4699         if (this.active) {
4700             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4701         }
4702         if (this.disabled) {
4703             cfg.cls += ' disabled';
4704         }
4705         
4706         // BS4 only?
4707         if (this.button_weight.length) {
4708             cfg.tag = this.href ? 'a' : 'button';
4709             cfg.html = this.html || '';
4710             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
4711             if (this.href) {
4712                 cfg.href = this.href;
4713             }
4714             if (this.fa) {
4715                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
4716             }
4717             
4718             // menu .. should add dropdown-menu class - so no need for carat..
4719             
4720             if (this.badge !== '') {
4721                  
4722                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4723             }
4724             return cfg;
4725         }
4726         
4727         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4728             cfg.cn = [
4729                 {
4730                     tag: this.tagtype,
4731                     href : this.href || "#",
4732                     html: this.html || ''
4733                 }
4734             ];
4735             if (this.tagtype == 'a') {
4736                 cfg.cn[0].cls = 'nav-link';
4737             }
4738             if (this.icon) {
4739                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
4740             }
4741             if (this.fa) {
4742                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
4743             }
4744             if(this.glyphicon) {
4745                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4746             }
4747             
4748             if (this.menu) {
4749                 
4750                 cfg.cn[0].html += " <span class='caret'></span>";
4751              
4752             }
4753             
4754             if (this.badge !== '') {
4755                  
4756                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4757             }
4758         }
4759         
4760         
4761         
4762         return cfg;
4763     },
4764     onRender : function(ct, position)
4765     {
4766        // Roo.log("Call onRender: " + this.xtype);
4767         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
4768             this.tag = 'div';
4769         }
4770         
4771         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
4772         this.navLink = this.el.select('.nav-link',true).first();
4773         return ret;
4774     },
4775       
4776     
4777     initEvents: function() 
4778     {
4779         if (typeof (this.menu) != 'undefined') {
4780             this.menu.parentType = this.xtype;
4781             this.menu.triggerEl = this.el;
4782             this.menu = this.addxtype(Roo.apply({}, this.menu));
4783         }
4784         
4785         this.el.select('a',true).on('click', this.onClick, this);
4786         
4787         if(this.tagtype == 'span'){
4788             this.el.select('span',true).on('click', this.onClick, this);
4789         }
4790        
4791         // at this point parent should be available..
4792         this.parent().register(this);
4793     },
4794     
4795     onClick : function(e)
4796     {
4797         if (e.getTarget('.dropdown-menu-item')) {
4798             // did you click on a menu itemm.... - then don't trigger onclick..
4799             return;
4800         }
4801         
4802         if(
4803                 this.preventDefault || 
4804                 this.href == '#' 
4805         ){
4806             Roo.log("NavItem - prevent Default?");
4807             e.preventDefault();
4808         }
4809         
4810         if (this.disabled) {
4811             return;
4812         }
4813         
4814         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4815         if (tg && tg.transition) {
4816             Roo.log("waiting for the transitionend");
4817             return;
4818         }
4819         
4820         
4821         
4822         //Roo.log("fire event clicked");
4823         if(this.fireEvent('click', this, e) === false){
4824             return;
4825         };
4826         
4827         if(this.tagtype == 'span'){
4828             return;
4829         }
4830         
4831         //Roo.log(this.href);
4832         var ael = this.el.select('a',true).first();
4833         //Roo.log(ael);
4834         
4835         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4836             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4837             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4838                 return; // ignore... - it's a 'hash' to another page.
4839             }
4840             Roo.log("NavItem - prevent Default?");
4841             e.preventDefault();
4842             this.scrollToElement(e);
4843         }
4844         
4845         
4846         var p =  this.parent();
4847    
4848         if (['tabs','pills'].indexOf(p.type)!==-1) {
4849             if (typeof(p.setActiveItem) !== 'undefined') {
4850                 p.setActiveItem(this);
4851             }
4852         }
4853         
4854         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4855         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4856             // remove the collapsed menu expand...
4857             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4858         }
4859     },
4860     
4861     isActive: function () {
4862         return this.active
4863     },
4864     setActive : function(state, fire, is_was_active)
4865     {
4866         if (this.active && !state && this.navId) {
4867             this.was_active = true;
4868             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4869             if (nv) {
4870                 nv.clearWasActive(this);
4871             }
4872             
4873         }
4874         this.active = state;
4875         
4876         if (!state ) {
4877             this.el.removeClass('active');
4878             this.navLink ? this.navLink.removeClass('active') : false;
4879         } else if (!this.el.hasClass('active')) {
4880             
4881             this.el.addClass('active');
4882             if (Roo.bootstrap.version == 4 && this.navLink ) {
4883                 this.navLink.addClass('active');
4884             }
4885             
4886         }
4887         if (fire) {
4888             this.fireEvent('changed', this, state);
4889         }
4890         
4891         // show a panel if it's registered and related..
4892         
4893         if (!this.navId || !this.tabId || !state || is_was_active) {
4894             return;
4895         }
4896         
4897         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4898         if (!tg) {
4899             return;
4900         }
4901         var pan = tg.getPanelByName(this.tabId);
4902         if (!pan) {
4903             return;
4904         }
4905         // if we can not flip to new panel - go back to old nav highlight..
4906         if (false == tg.showPanel(pan)) {
4907             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4908             if (nv) {
4909                 var onav = nv.getWasActive();
4910                 if (onav) {
4911                     onav.setActive(true, false, true);
4912                 }
4913             }
4914             
4915         }
4916         
4917         
4918         
4919     },
4920      // this should not be here...
4921     setDisabled : function(state)
4922     {
4923         this.disabled = state;
4924         if (!state ) {
4925             this.el.removeClass('disabled');
4926         } else if (!this.el.hasClass('disabled')) {
4927             this.el.addClass('disabled');
4928         }
4929         
4930     },
4931     
4932     /**
4933      * Fetch the element to display the tooltip on.
4934      * @return {Roo.Element} defaults to this.el
4935      */
4936     tooltipEl : function()
4937     {
4938         return this.el.select('' + this.tagtype + '', true).first();
4939     },
4940     
4941     scrollToElement : function(e)
4942     {
4943         var c = document.body;
4944         
4945         /*
4946          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4947          */
4948         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4949             c = document.documentElement;
4950         }
4951         
4952         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4953         
4954         if(!target){
4955             return;
4956         }
4957
4958         var o = target.calcOffsetsTo(c);
4959         
4960         var options = {
4961             target : target,
4962             value : o[1]
4963         };
4964         
4965         this.fireEvent('scrollto', this, options, e);
4966         
4967         Roo.get(c).scrollTo('top', options.value, true);
4968         
4969         return;
4970     }
4971 });
4972  
4973
4974  /*
4975  * - LGPL
4976  *
4977  * sidebar item
4978  *
4979  *  li
4980  *    <span> icon </span>
4981  *    <span> text </span>
4982  *    <span>badge </span>
4983  */
4984
4985 /**
4986  * @class Roo.bootstrap.NavSidebarItem
4987  * @extends Roo.bootstrap.NavItem
4988  * Bootstrap Navbar.NavSidebarItem class
4989  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4990  * {Boolean} open is the menu open
4991  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4992  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4993  * {String} buttonSize (sm|md|lg)the extra classes for the button
4994  * {Boolean} showArrow show arrow next to the text (default true)
4995  * @constructor
4996  * Create a new Navbar Button
4997  * @param {Object} config The config object
4998  */
4999 Roo.bootstrap.NavSidebarItem = function(config){
5000     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5001     this.addEvents({
5002         // raw events
5003         /**
5004          * @event click
5005          * The raw click event for the entire grid.
5006          * @param {Roo.EventObject} e
5007          */
5008         "click" : true,
5009          /**
5010             * @event changed
5011             * Fires when the active item active state changes
5012             * @param {Roo.bootstrap.NavSidebarItem} this
5013             * @param {boolean} state the new state
5014              
5015          */
5016         'changed': true
5017     });
5018    
5019 };
5020
5021 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
5022     
5023     badgeWeight : 'default',
5024     
5025     open: false,
5026     
5027     buttonView : false,
5028     
5029     buttonWeight : 'default',
5030     
5031     buttonSize : 'md',
5032     
5033     showArrow : true,
5034     
5035     getAutoCreate : function(){
5036         
5037         
5038         var a = {
5039                 tag: 'a',
5040                 href : this.href || '#',
5041                 cls: '',
5042                 html : '',
5043                 cn : []
5044         };
5045         
5046         if(this.buttonView){
5047             a = {
5048                 tag: 'button',
5049                 href : this.href || '#',
5050                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5051                 html : this.html,
5052                 cn : []
5053             };
5054         }
5055         
5056         var cfg = {
5057             tag: 'li',
5058             cls: '',
5059             cn: [ a ]
5060         };
5061         
5062         if (this.active) {
5063             cfg.cls += ' active';
5064         }
5065         
5066         if (this.disabled) {
5067             cfg.cls += ' disabled';
5068         }
5069         if (this.open) {
5070             cfg.cls += ' open x-open';
5071         }
5072         // left icon..
5073         if (this.glyphicon || this.icon) {
5074             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5075             a.cn.push({ tag : 'i', cls : c }) ;
5076         }
5077         
5078         if(!this.buttonView){
5079             var span = {
5080                 tag: 'span',
5081                 html : this.html || ''
5082             };
5083
5084             a.cn.push(span);
5085             
5086         }
5087         
5088         if (this.badge !== '') {
5089             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5090         }
5091         
5092         if (this.menu) {
5093             
5094             if(this.showArrow){
5095                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5096             }
5097             
5098             a.cls += ' dropdown-toggle treeview' ;
5099         }
5100         
5101         return cfg;
5102     },
5103     
5104     initEvents : function()
5105     { 
5106         if (typeof (this.menu) != 'undefined') {
5107             this.menu.parentType = this.xtype;
5108             this.menu.triggerEl = this.el;
5109             this.menu = this.addxtype(Roo.apply({}, this.menu));
5110         }
5111         
5112         this.el.on('click', this.onClick, this);
5113         
5114         if(this.badge !== ''){
5115             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5116         }
5117         
5118     },
5119     
5120     onClick : function(e)
5121     {
5122         if(this.disabled){
5123             e.preventDefault();
5124             return;
5125         }
5126         
5127         if(this.preventDefault){
5128             e.preventDefault();
5129         }
5130         
5131         this.fireEvent('click', this);
5132     },
5133     
5134     disable : function()
5135     {
5136         this.setDisabled(true);
5137     },
5138     
5139     enable : function()
5140     {
5141         this.setDisabled(false);
5142     },
5143     
5144     setDisabled : function(state)
5145     {
5146         if(this.disabled == state){
5147             return;
5148         }
5149         
5150         this.disabled = state;
5151         
5152         if (state) {
5153             this.el.addClass('disabled');
5154             return;
5155         }
5156         
5157         this.el.removeClass('disabled');
5158         
5159         return;
5160     },
5161     
5162     setActive : function(state)
5163     {
5164         if(this.active == state){
5165             return;
5166         }
5167         
5168         this.active = state;
5169         
5170         if (state) {
5171             this.el.addClass('active');
5172             return;
5173         }
5174         
5175         this.el.removeClass('active');
5176         
5177         return;
5178     },
5179     
5180     isActive: function () 
5181     {
5182         return this.active;
5183     },
5184     
5185     setBadge : function(str)
5186     {
5187         if(!this.badgeEl){
5188             return;
5189         }
5190         
5191         this.badgeEl.dom.innerHTML = str;
5192     }
5193     
5194    
5195      
5196  
5197 });
5198  
5199
5200  /*
5201  * - LGPL
5202  *
5203  * row
5204  * 
5205  */
5206
5207 /**
5208  * @class Roo.bootstrap.Row
5209  * @extends Roo.bootstrap.Component
5210  * Bootstrap Row class (contains columns...)
5211  * 
5212  * @constructor
5213  * Create a new Row
5214  * @param {Object} config The config object
5215  */
5216
5217 Roo.bootstrap.Row = function(config){
5218     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5219 };
5220
5221 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5222     
5223     getAutoCreate : function(){
5224        return {
5225             cls: 'row clearfix'
5226        };
5227     }
5228     
5229     
5230 });
5231
5232  
5233
5234  /*
5235  * - LGPL
5236  *
5237  * element
5238  * 
5239  */
5240
5241 /**
5242  * @class Roo.bootstrap.Element
5243  * @extends Roo.bootstrap.Component
5244  * Bootstrap Element class
5245  * @cfg {String} html contents of the element
5246  * @cfg {String} tag tag of the element
5247  * @cfg {String} cls class of the element
5248  * @cfg {Boolean} preventDefault (true|false) default false
5249  * @cfg {Boolean} clickable (true|false) default false
5250  * 
5251  * @constructor
5252  * Create a new Element
5253  * @param {Object} config The config object
5254  */
5255
5256 Roo.bootstrap.Element = function(config){
5257     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5258     
5259     this.addEvents({
5260         // raw events
5261         /**
5262          * @event click
5263          * When a element is chick
5264          * @param {Roo.bootstrap.Element} this
5265          * @param {Roo.EventObject} e
5266          */
5267         "click" : true
5268     });
5269 };
5270
5271 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5272     
5273     tag: 'div',
5274     cls: '',
5275     html: '',
5276     preventDefault: false, 
5277     clickable: false,
5278     
5279     getAutoCreate : function(){
5280         
5281         var cfg = {
5282             tag: this.tag,
5283             // cls: this.cls, double assign in parent class Component.js :: onRender
5284             html: this.html
5285         };
5286         
5287         return cfg;
5288     },
5289     
5290     initEvents: function() 
5291     {
5292         Roo.bootstrap.Element.superclass.initEvents.call(this);
5293         
5294         if(this.clickable){
5295             this.el.on('click', this.onClick, this);
5296         }
5297         
5298     },
5299     
5300     onClick : function(e)
5301     {
5302         if(this.preventDefault){
5303             e.preventDefault();
5304         }
5305         
5306         this.fireEvent('click', this, e);
5307     },
5308     
5309     getValue : function()
5310     {
5311         return this.el.dom.innerHTML;
5312     },
5313     
5314     setValue : function(value)
5315     {
5316         this.el.dom.innerHTML = value;
5317     }
5318    
5319 });
5320
5321  
5322
5323  /*
5324  * - LGPL
5325  *
5326  * pagination
5327  * 
5328  */
5329
5330 /**
5331  * @class Roo.bootstrap.Pagination
5332  * @extends Roo.bootstrap.Component
5333  * Bootstrap Pagination class
5334  * @cfg {String} size xs | sm | md | lg
5335  * @cfg {Boolean} inverse false | true
5336  * 
5337  * @constructor
5338  * Create a new Pagination
5339  * @param {Object} config The config object
5340  */
5341
5342 Roo.bootstrap.Pagination = function(config){
5343     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5344 };
5345
5346 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5347     
5348     cls: false,
5349     size: false,
5350     inverse: false,
5351     
5352     getAutoCreate : function(){
5353         var cfg = {
5354             tag: 'ul',
5355                 cls: 'pagination'
5356         };
5357         if (this.inverse) {
5358             cfg.cls += ' inverse';
5359         }
5360         if (this.html) {
5361             cfg.html=this.html;
5362         }
5363         if (this.cls) {
5364             cfg.cls += " " + this.cls;
5365         }
5366         return cfg;
5367     }
5368    
5369 });
5370
5371  
5372
5373  /*
5374  * - LGPL
5375  *
5376  * Pagination item
5377  * 
5378  */
5379
5380
5381 /**
5382  * @class Roo.bootstrap.PaginationItem
5383  * @extends Roo.bootstrap.Component
5384  * Bootstrap PaginationItem class
5385  * @cfg {String} html text
5386  * @cfg {String} href the link
5387  * @cfg {Boolean} preventDefault (true | false) default true
5388  * @cfg {Boolean} active (true | false) default false
5389  * @cfg {Boolean} disabled default false
5390  * 
5391  * 
5392  * @constructor
5393  * Create a new PaginationItem
5394  * @param {Object} config The config object
5395  */
5396
5397
5398 Roo.bootstrap.PaginationItem = function(config){
5399     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5400     this.addEvents({
5401         // raw events
5402         /**
5403          * @event click
5404          * The raw click event for the entire grid.
5405          * @param {Roo.EventObject} e
5406          */
5407         "click" : true
5408     });
5409 };
5410
5411 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5412     
5413     href : false,
5414     html : false,
5415     preventDefault: true,
5416     active : false,
5417     cls : false,
5418     disabled: false,
5419     
5420     getAutoCreate : function(){
5421         var cfg= {
5422             tag: 'li',
5423             cn: [
5424                 {
5425                     tag : 'a',
5426                     href : this.href ? this.href : '#',
5427                     html : this.html ? this.html : ''
5428                 }
5429             ]
5430         };
5431         
5432         if(this.cls){
5433             cfg.cls = this.cls;
5434         }
5435         
5436         if(this.disabled){
5437             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5438         }
5439         
5440         if(this.active){
5441             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5442         }
5443         
5444         return cfg;
5445     },
5446     
5447     initEvents: function() {
5448         
5449         this.el.on('click', this.onClick, this);
5450         
5451     },
5452     onClick : function(e)
5453     {
5454         Roo.log('PaginationItem on click ');
5455         if(this.preventDefault){
5456             e.preventDefault();
5457         }
5458         
5459         if(this.disabled){
5460             return;
5461         }
5462         
5463         this.fireEvent('click', this, e);
5464     }
5465    
5466 });
5467
5468  
5469
5470  /*
5471  * - LGPL
5472  *
5473  * slider
5474  * 
5475  */
5476
5477
5478 /**
5479  * @class Roo.bootstrap.Slider
5480  * @extends Roo.bootstrap.Component
5481  * Bootstrap Slider class
5482  *    
5483  * @constructor
5484  * Create a new Slider
5485  * @param {Object} config The config object
5486  */
5487
5488 Roo.bootstrap.Slider = function(config){
5489     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5490 };
5491
5492 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5493     
5494     getAutoCreate : function(){
5495         
5496         var cfg = {
5497             tag: 'div',
5498             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5499             cn: [
5500                 {
5501                     tag: 'a',
5502                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5503                 }
5504             ]
5505         };
5506         
5507         return cfg;
5508     }
5509    
5510 });
5511
5512  /*
5513  * Based on:
5514  * Ext JS Library 1.1.1
5515  * Copyright(c) 2006-2007, Ext JS, LLC.
5516  *
5517  * Originally Released Under LGPL - original licence link has changed is not relivant.
5518  *
5519  * Fork - LGPL
5520  * <script type="text/javascript">
5521  */
5522  
5523
5524 /**
5525  * @class Roo.grid.ColumnModel
5526  * @extends Roo.util.Observable
5527  * This is the default implementation of a ColumnModel used by the Grid. It defines
5528  * the columns in the grid.
5529  * <br>Usage:<br>
5530  <pre><code>
5531  var colModel = new Roo.grid.ColumnModel([
5532         {header: "Ticker", width: 60, sortable: true, locked: true},
5533         {header: "Company Name", width: 150, sortable: true},
5534         {header: "Market Cap.", width: 100, sortable: true},
5535         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5536         {header: "Employees", width: 100, sortable: true, resizable: false}
5537  ]);
5538  </code></pre>
5539  * <p>
5540  
5541  * The config options listed for this class are options which may appear in each
5542  * individual column definition.
5543  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5544  * @constructor
5545  * @param {Object} config An Array of column config objects. See this class's
5546  * config objects for details.
5547 */
5548 Roo.grid.ColumnModel = function(config){
5549         /**
5550      * The config passed into the constructor
5551      */
5552     this.config = config;
5553     this.lookup = {};
5554
5555     // if no id, create one
5556     // if the column does not have a dataIndex mapping,
5557     // map it to the order it is in the config
5558     for(var i = 0, len = config.length; i < len; i++){
5559         var c = config[i];
5560         if(typeof c.dataIndex == "undefined"){
5561             c.dataIndex = i;
5562         }
5563         if(typeof c.renderer == "string"){
5564             c.renderer = Roo.util.Format[c.renderer];
5565         }
5566         if(typeof c.id == "undefined"){
5567             c.id = Roo.id();
5568         }
5569         if(c.editor && c.editor.xtype){
5570             c.editor  = Roo.factory(c.editor, Roo.grid);
5571         }
5572         if(c.editor && c.editor.isFormField){
5573             c.editor = new Roo.grid.GridEditor(c.editor);
5574         }
5575         this.lookup[c.id] = c;
5576     }
5577
5578     /**
5579      * The width of columns which have no width specified (defaults to 100)
5580      * @type Number
5581      */
5582     this.defaultWidth = 100;
5583
5584     /**
5585      * Default sortable of columns which have no sortable specified (defaults to false)
5586      * @type Boolean
5587      */
5588     this.defaultSortable = false;
5589
5590     this.addEvents({
5591         /**
5592              * @event widthchange
5593              * Fires when the width of a column changes.
5594              * @param {ColumnModel} this
5595              * @param {Number} columnIndex The column index
5596              * @param {Number} newWidth The new width
5597              */
5598             "widthchange": true,
5599         /**
5600              * @event headerchange
5601              * Fires when the text of a header changes.
5602              * @param {ColumnModel} this
5603              * @param {Number} columnIndex The column index
5604              * @param {Number} newText The new header text
5605              */
5606             "headerchange": true,
5607         /**
5608              * @event hiddenchange
5609              * Fires when a column is hidden or "unhidden".
5610              * @param {ColumnModel} this
5611              * @param {Number} columnIndex The column index
5612              * @param {Boolean} hidden true if hidden, false otherwise
5613              */
5614             "hiddenchange": true,
5615             /**
5616          * @event columnmoved
5617          * Fires when a column is moved.
5618          * @param {ColumnModel} this
5619          * @param {Number} oldIndex
5620          * @param {Number} newIndex
5621          */
5622         "columnmoved" : true,
5623         /**
5624          * @event columlockchange
5625          * Fires when a column's locked state is changed
5626          * @param {ColumnModel} this
5627          * @param {Number} colIndex
5628          * @param {Boolean} locked true if locked
5629          */
5630         "columnlockchange" : true
5631     });
5632     Roo.grid.ColumnModel.superclass.constructor.call(this);
5633 };
5634 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5635     /**
5636      * @cfg {String} header The header text to display in the Grid view.
5637      */
5638     /**
5639      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5640      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5641      * specified, the column's index is used as an index into the Record's data Array.
5642      */
5643     /**
5644      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5645      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5646      */
5647     /**
5648      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5649      * Defaults to the value of the {@link #defaultSortable} property.
5650      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5651      */
5652     /**
5653      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5654      */
5655     /**
5656      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5657      */
5658     /**
5659      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5660      */
5661     /**
5662      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5663      */
5664     /**
5665      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5666      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5667      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5668      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5669      */
5670        /**
5671      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5672      */
5673     /**
5674      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5675      */
5676     /**
5677      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5678      */
5679     /**
5680      * @cfg {String} cursor (Optional)
5681      */
5682     /**
5683      * @cfg {String} tooltip (Optional)
5684      */
5685     /**
5686      * @cfg {Number} xs (Optional)
5687      */
5688     /**
5689      * @cfg {Number} sm (Optional)
5690      */
5691     /**
5692      * @cfg {Number} md (Optional)
5693      */
5694     /**
5695      * @cfg {Number} lg (Optional)
5696      */
5697     /**
5698      * Returns the id of the column at the specified index.
5699      * @param {Number} index The column index
5700      * @return {String} the id
5701      */
5702     getColumnId : function(index){
5703         return this.config[index].id;
5704     },
5705
5706     /**
5707      * Returns the column for a specified id.
5708      * @param {String} id The column id
5709      * @return {Object} the column
5710      */
5711     getColumnById : function(id){
5712         return this.lookup[id];
5713     },
5714
5715     
5716     /**
5717      * Returns the column for a specified dataIndex.
5718      * @param {String} dataIndex The column dataIndex
5719      * @return {Object|Boolean} the column or false if not found
5720      */
5721     getColumnByDataIndex: function(dataIndex){
5722         var index = this.findColumnIndex(dataIndex);
5723         return index > -1 ? this.config[index] : false;
5724     },
5725     
5726     /**
5727      * Returns the index for a specified column id.
5728      * @param {String} id The column id
5729      * @return {Number} the index, or -1 if not found
5730      */
5731     getIndexById : function(id){
5732         for(var i = 0, len = this.config.length; i < len; i++){
5733             if(this.config[i].id == id){
5734                 return i;
5735             }
5736         }
5737         return -1;
5738     },
5739     
5740     /**
5741      * Returns the index for a specified column dataIndex.
5742      * @param {String} dataIndex The column dataIndex
5743      * @return {Number} the index, or -1 if not found
5744      */
5745     
5746     findColumnIndex : function(dataIndex){
5747         for(var i = 0, len = this.config.length; i < len; i++){
5748             if(this.config[i].dataIndex == dataIndex){
5749                 return i;
5750             }
5751         }
5752         return -1;
5753     },
5754     
5755     
5756     moveColumn : function(oldIndex, newIndex){
5757         var c = this.config[oldIndex];
5758         this.config.splice(oldIndex, 1);
5759         this.config.splice(newIndex, 0, c);
5760         this.dataMap = null;
5761         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5762     },
5763
5764     isLocked : function(colIndex){
5765         return this.config[colIndex].locked === true;
5766     },
5767
5768     setLocked : function(colIndex, value, suppressEvent){
5769         if(this.isLocked(colIndex) == value){
5770             return;
5771         }
5772         this.config[colIndex].locked = value;
5773         if(!suppressEvent){
5774             this.fireEvent("columnlockchange", this, colIndex, value);
5775         }
5776     },
5777
5778     getTotalLockedWidth : function(){
5779         var totalWidth = 0;
5780         for(var i = 0; i < this.config.length; i++){
5781             if(this.isLocked(i) && !this.isHidden(i)){
5782                 this.totalWidth += this.getColumnWidth(i);
5783             }
5784         }
5785         return totalWidth;
5786     },
5787
5788     getLockedCount : function(){
5789         for(var i = 0, len = this.config.length; i < len; i++){
5790             if(!this.isLocked(i)){
5791                 return i;
5792             }
5793         }
5794         
5795         return this.config.length;
5796     },
5797
5798     /**
5799      * Returns the number of columns.
5800      * @return {Number}
5801      */
5802     getColumnCount : function(visibleOnly){
5803         if(visibleOnly === true){
5804             var c = 0;
5805             for(var i = 0, len = this.config.length; i < len; i++){
5806                 if(!this.isHidden(i)){
5807                     c++;
5808                 }
5809             }
5810             return c;
5811         }
5812         return this.config.length;
5813     },
5814
5815     /**
5816      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5817      * @param {Function} fn
5818      * @param {Object} scope (optional)
5819      * @return {Array} result
5820      */
5821     getColumnsBy : function(fn, scope){
5822         var r = [];
5823         for(var i = 0, len = this.config.length; i < len; i++){
5824             var c = this.config[i];
5825             if(fn.call(scope||this, c, i) === true){
5826                 r[r.length] = c;
5827             }
5828         }
5829         return r;
5830     },
5831
5832     /**
5833      * Returns true if the specified column is sortable.
5834      * @param {Number} col The column index
5835      * @return {Boolean}
5836      */
5837     isSortable : function(col){
5838         if(typeof this.config[col].sortable == "undefined"){
5839             return this.defaultSortable;
5840         }
5841         return this.config[col].sortable;
5842     },
5843
5844     /**
5845      * Returns the rendering (formatting) function defined for the column.
5846      * @param {Number} col The column index.
5847      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5848      */
5849     getRenderer : function(col){
5850         if(!this.config[col].renderer){
5851             return Roo.grid.ColumnModel.defaultRenderer;
5852         }
5853         return this.config[col].renderer;
5854     },
5855
5856     /**
5857      * Sets the rendering (formatting) function for a column.
5858      * @param {Number} col The column index
5859      * @param {Function} fn The function to use to process the cell's raw data
5860      * to return HTML markup for the grid view. The render function is called with
5861      * the following parameters:<ul>
5862      * <li>Data value.</li>
5863      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5864      * <li>css A CSS style string to apply to the table cell.</li>
5865      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5866      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5867      * <li>Row index</li>
5868      * <li>Column index</li>
5869      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5870      */
5871     setRenderer : function(col, fn){
5872         this.config[col].renderer = fn;
5873     },
5874
5875     /**
5876      * Returns the width for the specified column.
5877      * @param {Number} col The column index
5878      * @return {Number}
5879      */
5880     getColumnWidth : function(col){
5881         return this.config[col].width * 1 || this.defaultWidth;
5882     },
5883
5884     /**
5885      * Sets the width for a column.
5886      * @param {Number} col The column index
5887      * @param {Number} width The new width
5888      */
5889     setColumnWidth : function(col, width, suppressEvent){
5890         this.config[col].width = width;
5891         this.totalWidth = null;
5892         if(!suppressEvent){
5893              this.fireEvent("widthchange", this, col, width);
5894         }
5895     },
5896
5897     /**
5898      * Returns the total width of all columns.
5899      * @param {Boolean} includeHidden True to include hidden column widths
5900      * @return {Number}
5901      */
5902     getTotalWidth : function(includeHidden){
5903         if(!this.totalWidth){
5904             this.totalWidth = 0;
5905             for(var i = 0, len = this.config.length; i < len; i++){
5906                 if(includeHidden || !this.isHidden(i)){
5907                     this.totalWidth += this.getColumnWidth(i);
5908                 }
5909             }
5910         }
5911         return this.totalWidth;
5912     },
5913
5914     /**
5915      * Returns the header for the specified column.
5916      * @param {Number} col The column index
5917      * @return {String}
5918      */
5919     getColumnHeader : function(col){
5920         return this.config[col].header;
5921     },
5922
5923     /**
5924      * Sets the header for a column.
5925      * @param {Number} col The column index
5926      * @param {String} header The new header
5927      */
5928     setColumnHeader : function(col, header){
5929         this.config[col].header = header;
5930         this.fireEvent("headerchange", this, col, header);
5931     },
5932
5933     /**
5934      * Returns the tooltip for the specified column.
5935      * @param {Number} col The column index
5936      * @return {String}
5937      */
5938     getColumnTooltip : function(col){
5939             return this.config[col].tooltip;
5940     },
5941     /**
5942      * Sets the tooltip for a column.
5943      * @param {Number} col The column index
5944      * @param {String} tooltip The new tooltip
5945      */
5946     setColumnTooltip : function(col, tooltip){
5947             this.config[col].tooltip = tooltip;
5948     },
5949
5950     /**
5951      * Returns the dataIndex for the specified column.
5952      * @param {Number} col The column index
5953      * @return {Number}
5954      */
5955     getDataIndex : function(col){
5956         return this.config[col].dataIndex;
5957     },
5958
5959     /**
5960      * Sets the dataIndex for a column.
5961      * @param {Number} col The column index
5962      * @param {Number} dataIndex The new dataIndex
5963      */
5964     setDataIndex : function(col, dataIndex){
5965         this.config[col].dataIndex = dataIndex;
5966     },
5967
5968     
5969     
5970     /**
5971      * Returns true if the cell is editable.
5972      * @param {Number} colIndex The column index
5973      * @param {Number} rowIndex The row index - this is nto actually used..?
5974      * @return {Boolean}
5975      */
5976     isCellEditable : function(colIndex, rowIndex){
5977         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5978     },
5979
5980     /**
5981      * Returns the editor defined for the cell/column.
5982      * return false or null to disable editing.
5983      * @param {Number} colIndex The column index
5984      * @param {Number} rowIndex The row index
5985      * @return {Object}
5986      */
5987     getCellEditor : function(colIndex, rowIndex){
5988         return this.config[colIndex].editor;
5989     },
5990
5991     /**
5992      * Sets if a column is editable.
5993      * @param {Number} col The column index
5994      * @param {Boolean} editable True if the column is editable
5995      */
5996     setEditable : function(col, editable){
5997         this.config[col].editable = editable;
5998     },
5999
6000
6001     /**
6002      * Returns true if the column is hidden.
6003      * @param {Number} colIndex The column index
6004      * @return {Boolean}
6005      */
6006     isHidden : function(colIndex){
6007         return this.config[colIndex].hidden;
6008     },
6009
6010
6011     /**
6012      * Returns true if the column width cannot be changed
6013      */
6014     isFixed : function(colIndex){
6015         return this.config[colIndex].fixed;
6016     },
6017
6018     /**
6019      * Returns true if the column can be resized
6020      * @return {Boolean}
6021      */
6022     isResizable : function(colIndex){
6023         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6024     },
6025     /**
6026      * Sets if a column is hidden.
6027      * @param {Number} colIndex The column index
6028      * @param {Boolean} hidden True if the column is hidden
6029      */
6030     setHidden : function(colIndex, hidden){
6031         this.config[colIndex].hidden = hidden;
6032         this.totalWidth = null;
6033         this.fireEvent("hiddenchange", this, colIndex, hidden);
6034     },
6035
6036     /**
6037      * Sets the editor for a column.
6038      * @param {Number} col The column index
6039      * @param {Object} editor The editor object
6040      */
6041     setEditor : function(col, editor){
6042         this.config[col].editor = editor;
6043     }
6044 });
6045
6046 Roo.grid.ColumnModel.defaultRenderer = function(value)
6047 {
6048     if(typeof value == "object") {
6049         return value;
6050     }
6051         if(typeof value == "string" && value.length < 1){
6052             return "&#160;";
6053         }
6054     
6055         return String.format("{0}", value);
6056 };
6057
6058 // Alias for backwards compatibility
6059 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6060 /*
6061  * Based on:
6062  * Ext JS Library 1.1.1
6063  * Copyright(c) 2006-2007, Ext JS, LLC.
6064  *
6065  * Originally Released Under LGPL - original licence link has changed is not relivant.
6066  *
6067  * Fork - LGPL
6068  * <script type="text/javascript">
6069  */
6070  
6071 /**
6072  * @class Roo.LoadMask
6073  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6074  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6075  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6076  * element's UpdateManager load indicator and will be destroyed after the initial load.
6077  * @constructor
6078  * Create a new LoadMask
6079  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6080  * @param {Object} config The config object
6081  */
6082 Roo.LoadMask = function(el, config){
6083     this.el = Roo.get(el);
6084     Roo.apply(this, config);
6085     if(this.store){
6086         this.store.on('beforeload', this.onBeforeLoad, this);
6087         this.store.on('load', this.onLoad, this);
6088         this.store.on('loadexception', this.onLoadException, this);
6089         this.removeMask = false;
6090     }else{
6091         var um = this.el.getUpdateManager();
6092         um.showLoadIndicator = false; // disable the default indicator
6093         um.on('beforeupdate', this.onBeforeLoad, this);
6094         um.on('update', this.onLoad, this);
6095         um.on('failure', this.onLoad, this);
6096         this.removeMask = true;
6097     }
6098 };
6099
6100 Roo.LoadMask.prototype = {
6101     /**
6102      * @cfg {Boolean} removeMask
6103      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6104      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6105      */
6106     /**
6107      * @cfg {String} msg
6108      * The text to display in a centered loading message box (defaults to 'Loading...')
6109      */
6110     msg : 'Loading...',
6111     /**
6112      * @cfg {String} msgCls
6113      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6114      */
6115     msgCls : 'x-mask-loading',
6116
6117     /**
6118      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6119      * @type Boolean
6120      */
6121     disabled: false,
6122
6123     /**
6124      * Disables the mask to prevent it from being displayed
6125      */
6126     disable : function(){
6127        this.disabled = true;
6128     },
6129
6130     /**
6131      * Enables the mask so that it can be displayed
6132      */
6133     enable : function(){
6134         this.disabled = false;
6135     },
6136     
6137     onLoadException : function()
6138     {
6139         Roo.log(arguments);
6140         
6141         if (typeof(arguments[3]) != 'undefined') {
6142             Roo.MessageBox.alert("Error loading",arguments[3]);
6143         } 
6144         /*
6145         try {
6146             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6147                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6148             }   
6149         } catch(e) {
6150             
6151         }
6152         */
6153     
6154         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6155     },
6156     // private
6157     onLoad : function()
6158     {
6159         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6160     },
6161
6162     // private
6163     onBeforeLoad : function(){
6164         if(!this.disabled){
6165             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6166         }
6167     },
6168
6169     // private
6170     destroy : function(){
6171         if(this.store){
6172             this.store.un('beforeload', this.onBeforeLoad, this);
6173             this.store.un('load', this.onLoad, this);
6174             this.store.un('loadexception', this.onLoadException, this);
6175         }else{
6176             var um = this.el.getUpdateManager();
6177             um.un('beforeupdate', this.onBeforeLoad, this);
6178             um.un('update', this.onLoad, this);
6179             um.un('failure', this.onLoad, this);
6180         }
6181     }
6182 };/*
6183  * - LGPL
6184  *
6185  * table
6186  * 
6187  */
6188
6189 /**
6190  * @class Roo.bootstrap.Table
6191  * @extends Roo.bootstrap.Component
6192  * Bootstrap Table class
6193  * @cfg {String} cls table class
6194  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6195  * @cfg {String} bgcolor Specifies the background color for a table
6196  * @cfg {Number} border Specifies whether the table cells should have borders or not
6197  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6198  * @cfg {Number} cellspacing Specifies the space between cells
6199  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6200  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6201  * @cfg {String} sortable Specifies that the table should be sortable
6202  * @cfg {String} summary Specifies a summary of the content of a table
6203  * @cfg {Number} width Specifies the width of a table
6204  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6205  * 
6206  * @cfg {boolean} striped Should the rows be alternative striped
6207  * @cfg {boolean} bordered Add borders to the table
6208  * @cfg {boolean} hover Add hover highlighting
6209  * @cfg {boolean} condensed Format condensed
6210  * @cfg {boolean} responsive Format condensed
6211  * @cfg {Boolean} loadMask (true|false) default false
6212  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6213  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6214  * @cfg {Boolean} rowSelection (true|false) default false
6215  * @cfg {Boolean} cellSelection (true|false) default false
6216  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6217  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6218  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6219  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6220  
6221  * 
6222  * @constructor
6223  * Create a new Table
6224  * @param {Object} config The config object
6225  */
6226
6227 Roo.bootstrap.Table = function(config){
6228     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6229     
6230   
6231     
6232     // BC...
6233     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6234     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6235     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6236     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6237     
6238     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6239     if (this.sm) {
6240         this.sm.grid = this;
6241         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6242         this.sm = this.selModel;
6243         this.sm.xmodule = this.xmodule || false;
6244     }
6245     
6246     if (this.cm && typeof(this.cm.config) == 'undefined') {
6247         this.colModel = new Roo.grid.ColumnModel(this.cm);
6248         this.cm = this.colModel;
6249         this.cm.xmodule = this.xmodule || false;
6250     }
6251     if (this.store) {
6252         this.store= Roo.factory(this.store, Roo.data);
6253         this.ds = this.store;
6254         this.ds.xmodule = this.xmodule || false;
6255          
6256     }
6257     if (this.footer && this.store) {
6258         this.footer.dataSource = this.ds;
6259         this.footer = Roo.factory(this.footer);
6260     }
6261     
6262     /** @private */
6263     this.addEvents({
6264         /**
6265          * @event cellclick
6266          * Fires when a cell is clicked
6267          * @param {Roo.bootstrap.Table} this
6268          * @param {Roo.Element} el
6269          * @param {Number} rowIndex
6270          * @param {Number} columnIndex
6271          * @param {Roo.EventObject} e
6272          */
6273         "cellclick" : true,
6274         /**
6275          * @event celldblclick
6276          * Fires when a cell is double clicked
6277          * @param {Roo.bootstrap.Table} this
6278          * @param {Roo.Element} el
6279          * @param {Number} rowIndex
6280          * @param {Number} columnIndex
6281          * @param {Roo.EventObject} e
6282          */
6283         "celldblclick" : true,
6284         /**
6285          * @event rowclick
6286          * Fires when a row is clicked
6287          * @param {Roo.bootstrap.Table} this
6288          * @param {Roo.Element} el
6289          * @param {Number} rowIndex
6290          * @param {Roo.EventObject} e
6291          */
6292         "rowclick" : true,
6293         /**
6294          * @event rowdblclick
6295          * Fires when a row is double clicked
6296          * @param {Roo.bootstrap.Table} this
6297          * @param {Roo.Element} el
6298          * @param {Number} rowIndex
6299          * @param {Roo.EventObject} e
6300          */
6301         "rowdblclick" : true,
6302         /**
6303          * @event mouseover
6304          * Fires when a mouseover occur
6305          * @param {Roo.bootstrap.Table} this
6306          * @param {Roo.Element} el
6307          * @param {Number} rowIndex
6308          * @param {Number} columnIndex
6309          * @param {Roo.EventObject} e
6310          */
6311         "mouseover" : true,
6312         /**
6313          * @event mouseout
6314          * Fires when a mouseout occur
6315          * @param {Roo.bootstrap.Table} this
6316          * @param {Roo.Element} el
6317          * @param {Number} rowIndex
6318          * @param {Number} columnIndex
6319          * @param {Roo.EventObject} e
6320          */
6321         "mouseout" : true,
6322         /**
6323          * @event rowclass
6324          * Fires when a row is rendered, so you can change add a style to it.
6325          * @param {Roo.bootstrap.Table} this
6326          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6327          */
6328         'rowclass' : true,
6329           /**
6330          * @event rowsrendered
6331          * Fires when all the  rows have been rendered
6332          * @param {Roo.bootstrap.Table} this
6333          */
6334         'rowsrendered' : true,
6335         /**
6336          * @event contextmenu
6337          * The raw contextmenu event for the entire grid.
6338          * @param {Roo.EventObject} e
6339          */
6340         "contextmenu" : true,
6341         /**
6342          * @event rowcontextmenu
6343          * Fires when a row is right clicked
6344          * @param {Roo.bootstrap.Table} this
6345          * @param {Number} rowIndex
6346          * @param {Roo.EventObject} e
6347          */
6348         "rowcontextmenu" : true,
6349         /**
6350          * @event cellcontextmenu
6351          * Fires when a cell is right clicked
6352          * @param {Roo.bootstrap.Table} this
6353          * @param {Number} rowIndex
6354          * @param {Number} cellIndex
6355          * @param {Roo.EventObject} e
6356          */
6357          "cellcontextmenu" : true,
6358          /**
6359          * @event headercontextmenu
6360          * Fires when a header is right clicked
6361          * @param {Roo.bootstrap.Table} this
6362          * @param {Number} columnIndex
6363          * @param {Roo.EventObject} e
6364          */
6365         "headercontextmenu" : true
6366     });
6367 };
6368
6369 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6370     
6371     cls: false,
6372     align: false,
6373     bgcolor: false,
6374     border: false,
6375     cellpadding: false,
6376     cellspacing: false,
6377     frame: false,
6378     rules: false,
6379     sortable: false,
6380     summary: false,
6381     width: false,
6382     striped : false,
6383     scrollBody : false,
6384     bordered: false,
6385     hover:  false,
6386     condensed : false,
6387     responsive : false,
6388     sm : false,
6389     cm : false,
6390     store : false,
6391     loadMask : false,
6392     footerShow : true,
6393     headerShow : true,
6394   
6395     rowSelection : false,
6396     cellSelection : false,
6397     layout : false,
6398     
6399     // Roo.Element - the tbody
6400     mainBody: false,
6401     // Roo.Element - thead element
6402     mainHead: false,
6403     
6404     container: false, // used by gridpanel...
6405     
6406     lazyLoad : false,
6407     
6408     CSS : Roo.util.CSS,
6409     
6410     auto_hide_footer : false,
6411     
6412     getAutoCreate : function()
6413     {
6414         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6415         
6416         cfg = {
6417             tag: 'table',
6418             cls : 'table',
6419             cn : []
6420         };
6421         if (this.scrollBody) {
6422             cfg.cls += ' table-body-fixed';
6423         }    
6424         if (this.striped) {
6425             cfg.cls += ' table-striped';
6426         }
6427         
6428         if (this.hover) {
6429             cfg.cls += ' table-hover';
6430         }
6431         if (this.bordered) {
6432             cfg.cls += ' table-bordered';
6433         }
6434         if (this.condensed) {
6435             cfg.cls += ' table-condensed';
6436         }
6437         if (this.responsive) {
6438             cfg.cls += ' table-responsive';
6439         }
6440         
6441         if (this.cls) {
6442             cfg.cls+=  ' ' +this.cls;
6443         }
6444         
6445         // this lot should be simplifed...
6446         var _t = this;
6447         var cp = [
6448             'align',
6449             'bgcolor',
6450             'border',
6451             'cellpadding',
6452             'cellspacing',
6453             'frame',
6454             'rules',
6455             'sortable',
6456             'summary',
6457             'width'
6458         ].forEach(function(k) {
6459             if (_t[k]) {
6460                 cfg[k] = _t[k];
6461             }
6462         });
6463         
6464         
6465         if (this.layout) {
6466             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6467         }
6468         
6469         if(this.store || this.cm){
6470             if(this.headerShow){
6471                 cfg.cn.push(this.renderHeader());
6472             }
6473             
6474             cfg.cn.push(this.renderBody());
6475             
6476             if(this.footerShow){
6477                 cfg.cn.push(this.renderFooter());
6478             }
6479             // where does this come from?
6480             //cfg.cls+=  ' TableGrid';
6481         }
6482         
6483         return { cn : [ cfg ] };
6484     },
6485     
6486     initEvents : function()
6487     {   
6488         if(!this.store || !this.cm){
6489             return;
6490         }
6491         if (this.selModel) {
6492             this.selModel.initEvents();
6493         }
6494         
6495         
6496         //Roo.log('initEvents with ds!!!!');
6497         
6498         this.mainBody = this.el.select('tbody', true).first();
6499         this.mainHead = this.el.select('thead', true).first();
6500         this.mainFoot = this.el.select('tfoot', true).first();
6501         
6502         
6503         
6504         var _this = this;
6505         
6506         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6507             e.on('click', _this.sort, _this);
6508         });
6509         
6510         this.mainBody.on("click", this.onClick, this);
6511         this.mainBody.on("dblclick", this.onDblClick, this);
6512         
6513         // why is this done????? = it breaks dialogs??
6514         //this.parent().el.setStyle('position', 'relative');
6515         
6516         
6517         if (this.footer) {
6518             this.footer.parentId = this.id;
6519             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6520             
6521             if(this.lazyLoad){
6522                 this.el.select('tfoot tr td').first().addClass('hide');
6523             }
6524         } 
6525         
6526         if(this.loadMask) {
6527             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6528         }
6529         
6530         this.store.on('load', this.onLoad, this);
6531         this.store.on('beforeload', this.onBeforeLoad, this);
6532         this.store.on('update', this.onUpdate, this);
6533         this.store.on('add', this.onAdd, this);
6534         this.store.on("clear", this.clear, this);
6535         
6536         this.el.on("contextmenu", this.onContextMenu, this);
6537         
6538         this.mainBody.on('scroll', this.onBodyScroll, this);
6539         
6540         this.cm.on("headerchange", this.onHeaderChange, this);
6541         
6542         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6543         
6544     },
6545     
6546     onContextMenu : function(e, t)
6547     {
6548         this.processEvent("contextmenu", e);
6549     },
6550     
6551     processEvent : function(name, e)
6552     {
6553         if (name != 'touchstart' ) {
6554             this.fireEvent(name, e);    
6555         }
6556         
6557         var t = e.getTarget();
6558         
6559         var cell = Roo.get(t);
6560         
6561         if(!cell){
6562             return;
6563         }
6564         
6565         if(cell.findParent('tfoot', false, true)){
6566             return;
6567         }
6568         
6569         if(cell.findParent('thead', false, true)){
6570             
6571             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6572                 cell = Roo.get(t).findParent('th', false, true);
6573                 if (!cell) {
6574                     Roo.log("failed to find th in thead?");
6575                     Roo.log(e.getTarget());
6576                     return;
6577                 }
6578             }
6579             
6580             var cellIndex = cell.dom.cellIndex;
6581             
6582             var ename = name == 'touchstart' ? 'click' : name;
6583             this.fireEvent("header" + ename, this, cellIndex, e);
6584             
6585             return;
6586         }
6587         
6588         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6589             cell = Roo.get(t).findParent('td', false, true);
6590             if (!cell) {
6591                 Roo.log("failed to find th in tbody?");
6592                 Roo.log(e.getTarget());
6593                 return;
6594             }
6595         }
6596         
6597         var row = cell.findParent('tr', false, true);
6598         var cellIndex = cell.dom.cellIndex;
6599         var rowIndex = row.dom.rowIndex - 1;
6600         
6601         if(row !== false){
6602             
6603             this.fireEvent("row" + name, this, rowIndex, e);
6604             
6605             if(cell !== false){
6606             
6607                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6608             }
6609         }
6610         
6611     },
6612     
6613     onMouseover : function(e, el)
6614     {
6615         var cell = Roo.get(el);
6616         
6617         if(!cell){
6618             return;
6619         }
6620         
6621         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6622             cell = cell.findParent('td', false, true);
6623         }
6624         
6625         var row = cell.findParent('tr', false, true);
6626         var cellIndex = cell.dom.cellIndex;
6627         var rowIndex = row.dom.rowIndex - 1; // start from 0
6628         
6629         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6630         
6631     },
6632     
6633     onMouseout : function(e, el)
6634     {
6635         var cell = Roo.get(el);
6636         
6637         if(!cell){
6638             return;
6639         }
6640         
6641         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6642             cell = cell.findParent('td', false, true);
6643         }
6644         
6645         var row = cell.findParent('tr', false, true);
6646         var cellIndex = cell.dom.cellIndex;
6647         var rowIndex = row.dom.rowIndex - 1; // start from 0
6648         
6649         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6650         
6651     },
6652     
6653     onClick : function(e, el)
6654     {
6655         var cell = Roo.get(el);
6656         
6657         if(!cell || (!this.cellSelection && !this.rowSelection)){
6658             return;
6659         }
6660         
6661         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6662             cell = cell.findParent('td', false, true);
6663         }
6664         
6665         if(!cell || typeof(cell) == 'undefined'){
6666             return;
6667         }
6668         
6669         var row = cell.findParent('tr', false, true);
6670         
6671         if(!row || typeof(row) == 'undefined'){
6672             return;
6673         }
6674         
6675         var cellIndex = cell.dom.cellIndex;
6676         var rowIndex = this.getRowIndex(row);
6677         
6678         // why??? - should these not be based on SelectionModel?
6679         if(this.cellSelection){
6680             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6681         }
6682         
6683         if(this.rowSelection){
6684             this.fireEvent('rowclick', this, row, rowIndex, e);
6685         }
6686         
6687         
6688     },
6689         
6690     onDblClick : function(e,el)
6691     {
6692         var cell = Roo.get(el);
6693         
6694         if(!cell || (!this.cellSelection && !this.rowSelection)){
6695             return;
6696         }
6697         
6698         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6699             cell = cell.findParent('td', false, true);
6700         }
6701         
6702         if(!cell || typeof(cell) == 'undefined'){
6703             return;
6704         }
6705         
6706         var row = cell.findParent('tr', false, true);
6707         
6708         if(!row || typeof(row) == 'undefined'){
6709             return;
6710         }
6711         
6712         var cellIndex = cell.dom.cellIndex;
6713         var rowIndex = this.getRowIndex(row);
6714         
6715         if(this.cellSelection){
6716             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6717         }
6718         
6719         if(this.rowSelection){
6720             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6721         }
6722     },
6723     
6724     sort : function(e,el)
6725     {
6726         var col = Roo.get(el);
6727         
6728         if(!col.hasClass('sortable')){
6729             return;
6730         }
6731         
6732         var sort = col.attr('sort');
6733         var dir = 'ASC';
6734         
6735         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6736             dir = 'DESC';
6737         }
6738         
6739         this.store.sortInfo = {field : sort, direction : dir};
6740         
6741         if (this.footer) {
6742             Roo.log("calling footer first");
6743             this.footer.onClick('first');
6744         } else {
6745         
6746             this.store.load({ params : { start : 0 } });
6747         }
6748     },
6749     
6750     renderHeader : function()
6751     {
6752         var header = {
6753             tag: 'thead',
6754             cn : []
6755         };
6756         
6757         var cm = this.cm;
6758         this.totalWidth = 0;
6759         
6760         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6761             
6762             var config = cm.config[i];
6763             
6764             var c = {
6765                 tag: 'th',
6766                 cls : 'x-hcol-' + i,
6767                 style : '',
6768                 html: cm.getColumnHeader(i)
6769             };
6770             
6771             var hh = '';
6772             
6773             if(typeof(config.sortable) != 'undefined' && config.sortable){
6774                 c.cls = 'sortable';
6775                 c.html = '<i class="glyphicon"></i>' + c.html;
6776             }
6777             
6778             if(typeof(config.lgHeader) != 'undefined'){
6779                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6780             }
6781             
6782             if(typeof(config.mdHeader) != 'undefined'){
6783                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6784             }
6785             
6786             if(typeof(config.smHeader) != 'undefined'){
6787                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6788             }
6789             
6790             if(typeof(config.xsHeader) != 'undefined'){
6791                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6792             }
6793             
6794             if(hh.length){
6795                 c.html = hh;
6796             }
6797             
6798             if(typeof(config.tooltip) != 'undefined'){
6799                 c.tooltip = config.tooltip;
6800             }
6801             
6802             if(typeof(config.colspan) != 'undefined'){
6803                 c.colspan = config.colspan;
6804             }
6805             
6806             if(typeof(config.hidden) != 'undefined' && config.hidden){
6807                 c.style += ' display:none;';
6808             }
6809             
6810             if(typeof(config.dataIndex) != 'undefined'){
6811                 c.sort = config.dataIndex;
6812             }
6813             
6814            
6815             
6816             if(typeof(config.align) != 'undefined' && config.align.length){
6817                 c.style += ' text-align:' + config.align + ';';
6818             }
6819             
6820             if(typeof(config.width) != 'undefined'){
6821                 c.style += ' width:' + config.width + 'px;';
6822                 this.totalWidth += config.width;
6823             } else {
6824                 this.totalWidth += 100; // assume minimum of 100 per column?
6825             }
6826             
6827             if(typeof(config.cls) != 'undefined'){
6828                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6829             }
6830             
6831             ['xs','sm','md','lg'].map(function(size){
6832                 
6833                 if(typeof(config[size]) == 'undefined'){
6834                     return;
6835                 }
6836                 
6837                 if (!config[size]) { // 0 = hidden
6838                     c.cls += ' hidden-' + size;
6839                     return;
6840                 }
6841                 
6842                 c.cls += ' col-' + size + '-' + config[size];
6843
6844             });
6845             
6846             header.cn.push(c)
6847         }
6848         
6849         return header;
6850     },
6851     
6852     renderBody : function()
6853     {
6854         var body = {
6855             tag: 'tbody',
6856             cn : [
6857                 {
6858                     tag: 'tr',
6859                     cn : [
6860                         {
6861                             tag : 'td',
6862                             colspan :  this.cm.getColumnCount()
6863                         }
6864                     ]
6865                 }
6866             ]
6867         };
6868         
6869         return body;
6870     },
6871     
6872     renderFooter : function()
6873     {
6874         var footer = {
6875             tag: 'tfoot',
6876             cn : [
6877                 {
6878                     tag: 'tr',
6879                     cn : [
6880                         {
6881                             tag : 'td',
6882                             colspan :  this.cm.getColumnCount()
6883                         }
6884                     ]
6885                 }
6886             ]
6887         };
6888         
6889         return footer;
6890     },
6891     
6892     
6893     
6894     onLoad : function()
6895     {
6896 //        Roo.log('ds onload');
6897         this.clear();
6898         
6899         var _this = this;
6900         var cm = this.cm;
6901         var ds = this.store;
6902         
6903         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6904             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6905             if (_this.store.sortInfo) {
6906                     
6907                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6908                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6909                 }
6910                 
6911                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6912                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6913                 }
6914             }
6915         });
6916         
6917         var tbody =  this.mainBody;
6918               
6919         if(ds.getCount() > 0){
6920             ds.data.each(function(d,rowIndex){
6921                 var row =  this.renderRow(cm, ds, rowIndex);
6922                 
6923                 tbody.createChild(row);
6924                 
6925                 var _this = this;
6926                 
6927                 if(row.cellObjects.length){
6928                     Roo.each(row.cellObjects, function(r){
6929                         _this.renderCellObject(r);
6930                     })
6931                 }
6932                 
6933             }, this);
6934         }
6935         
6936         var tfoot = this.el.select('tfoot', true).first();
6937         
6938         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6939             
6940             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6941             
6942             var total = this.ds.getTotalCount();
6943             
6944             if(this.footer.pageSize < total){
6945                 this.mainFoot.show();
6946             }
6947         }
6948         
6949         Roo.each(this.el.select('tbody td', true).elements, function(e){
6950             e.on('mouseover', _this.onMouseover, _this);
6951         });
6952         
6953         Roo.each(this.el.select('tbody td', true).elements, function(e){
6954             e.on('mouseout', _this.onMouseout, _this);
6955         });
6956         this.fireEvent('rowsrendered', this);
6957         
6958         this.autoSize();
6959     },
6960     
6961     
6962     onUpdate : function(ds,record)
6963     {
6964         this.refreshRow(record);
6965         this.autoSize();
6966     },
6967     
6968     onRemove : function(ds, record, index, isUpdate){
6969         if(isUpdate !== true){
6970             this.fireEvent("beforerowremoved", this, index, record);
6971         }
6972         var bt = this.mainBody.dom;
6973         
6974         var rows = this.el.select('tbody > tr', true).elements;
6975         
6976         if(typeof(rows[index]) != 'undefined'){
6977             bt.removeChild(rows[index].dom);
6978         }
6979         
6980 //        if(bt.rows[index]){
6981 //            bt.removeChild(bt.rows[index]);
6982 //        }
6983         
6984         if(isUpdate !== true){
6985             //this.stripeRows(index);
6986             //this.syncRowHeights(index, index);
6987             //this.layout();
6988             this.fireEvent("rowremoved", this, index, record);
6989         }
6990     },
6991     
6992     onAdd : function(ds, records, rowIndex)
6993     {
6994         //Roo.log('on Add called');
6995         // - note this does not handle multiple adding very well..
6996         var bt = this.mainBody.dom;
6997         for (var i =0 ; i < records.length;i++) {
6998             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6999             //Roo.log(records[i]);
7000             //Roo.log(this.store.getAt(rowIndex+i));
7001             this.insertRow(this.store, rowIndex + i, false);
7002             return;
7003         }
7004         
7005     },
7006     
7007     
7008     refreshRow : function(record){
7009         var ds = this.store, index;
7010         if(typeof record == 'number'){
7011             index = record;
7012             record = ds.getAt(index);
7013         }else{
7014             index = ds.indexOf(record);
7015         }
7016         this.insertRow(ds, index, true);
7017         this.autoSize();
7018         this.onRemove(ds, record, index+1, true);
7019         this.autoSize();
7020         //this.syncRowHeights(index, index);
7021         //this.layout();
7022         this.fireEvent("rowupdated", this, index, record);
7023     },
7024     
7025     insertRow : function(dm, rowIndex, isUpdate){
7026         
7027         if(!isUpdate){
7028             this.fireEvent("beforerowsinserted", this, rowIndex);
7029         }
7030             //var s = this.getScrollState();
7031         var row = this.renderRow(this.cm, this.store, rowIndex);
7032         // insert before rowIndex..
7033         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7034         
7035         var _this = this;
7036                 
7037         if(row.cellObjects.length){
7038             Roo.each(row.cellObjects, function(r){
7039                 _this.renderCellObject(r);
7040             })
7041         }
7042             
7043         if(!isUpdate){
7044             this.fireEvent("rowsinserted", this, rowIndex);
7045             //this.syncRowHeights(firstRow, lastRow);
7046             //this.stripeRows(firstRow);
7047             //this.layout();
7048         }
7049         
7050     },
7051     
7052     
7053     getRowDom : function(rowIndex)
7054     {
7055         var rows = this.el.select('tbody > tr', true).elements;
7056         
7057         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7058         
7059     },
7060     // returns the object tree for a tr..
7061   
7062     
7063     renderRow : function(cm, ds, rowIndex) 
7064     {
7065         var d = ds.getAt(rowIndex);
7066         
7067         var row = {
7068             tag : 'tr',
7069             cls : 'x-row-' + rowIndex,
7070             cn : []
7071         };
7072             
7073         var cellObjects = [];
7074         
7075         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7076             var config = cm.config[i];
7077             
7078             var renderer = cm.getRenderer(i);
7079             var value = '';
7080             var id = false;
7081             
7082             if(typeof(renderer) !== 'undefined'){
7083                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7084             }
7085             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7086             // and are rendered into the cells after the row is rendered - using the id for the element.
7087             
7088             if(typeof(value) === 'object'){
7089                 id = Roo.id();
7090                 cellObjects.push({
7091                     container : id,
7092                     cfg : value 
7093                 })
7094             }
7095             
7096             var rowcfg = {
7097                 record: d,
7098                 rowIndex : rowIndex,
7099                 colIndex : i,
7100                 rowClass : ''
7101             };
7102
7103             this.fireEvent('rowclass', this, rowcfg);
7104             
7105             var td = {
7106                 tag: 'td',
7107                 cls : rowcfg.rowClass + ' x-col-' + i,
7108                 style: '',
7109                 html: (typeof(value) === 'object') ? '' : value
7110             };
7111             
7112             if (id) {
7113                 td.id = id;
7114             }
7115             
7116             if(typeof(config.colspan) != 'undefined'){
7117                 td.colspan = config.colspan;
7118             }
7119             
7120             if(typeof(config.hidden) != 'undefined' && config.hidden){
7121                 td.style += ' display:none;';
7122             }
7123             
7124             if(typeof(config.align) != 'undefined' && config.align.length){
7125                 td.style += ' text-align:' + config.align + ';';
7126             }
7127             if(typeof(config.valign) != 'undefined' && config.valign.length){
7128                 td.style += ' vertical-align:' + config.valign + ';';
7129             }
7130             
7131             if(typeof(config.width) != 'undefined'){
7132                 td.style += ' width:' +  config.width + 'px;';
7133             }
7134             
7135             if(typeof(config.cursor) != 'undefined'){
7136                 td.style += ' cursor:' +  config.cursor + ';';
7137             }
7138             
7139             if(typeof(config.cls) != 'undefined'){
7140                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7141             }
7142             
7143             ['xs','sm','md','lg'].map(function(size){
7144                 
7145                 if(typeof(config[size]) == 'undefined'){
7146                     return;
7147                 }
7148                 
7149                 if (!config[size]) { // 0 = hidden
7150                     td.cls += ' hidden-' + size;
7151                     return;
7152                 }
7153                 
7154                 td.cls += ' col-' + size + '-' + config[size];
7155
7156             });
7157             
7158             row.cn.push(td);
7159            
7160         }
7161         
7162         row.cellObjects = cellObjects;
7163         
7164         return row;
7165           
7166     },
7167     
7168     
7169     
7170     onBeforeLoad : function()
7171     {
7172         
7173     },
7174      /**
7175      * Remove all rows
7176      */
7177     clear : function()
7178     {
7179         this.el.select('tbody', true).first().dom.innerHTML = '';
7180     },
7181     /**
7182      * Show or hide a row.
7183      * @param {Number} rowIndex to show or hide
7184      * @param {Boolean} state hide
7185      */
7186     setRowVisibility : function(rowIndex, state)
7187     {
7188         var bt = this.mainBody.dom;
7189         
7190         var rows = this.el.select('tbody > tr', true).elements;
7191         
7192         if(typeof(rows[rowIndex]) == 'undefined'){
7193             return;
7194         }
7195         rows[rowIndex].dom.style.display = state ? '' : 'none';
7196     },
7197     
7198     
7199     getSelectionModel : function(){
7200         if(!this.selModel){
7201             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7202         }
7203         return this.selModel;
7204     },
7205     /*
7206      * Render the Roo.bootstrap object from renderder
7207      */
7208     renderCellObject : function(r)
7209     {
7210         var _this = this;
7211         
7212         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7213         
7214         var t = r.cfg.render(r.container);
7215         
7216         if(r.cfg.cn){
7217             Roo.each(r.cfg.cn, function(c){
7218                 var child = {
7219                     container: t.getChildContainer(),
7220                     cfg: c
7221                 };
7222                 _this.renderCellObject(child);
7223             })
7224         }
7225     },
7226     
7227     getRowIndex : function(row)
7228     {
7229         var rowIndex = -1;
7230         
7231         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7232             if(el != row){
7233                 return;
7234             }
7235             
7236             rowIndex = index;
7237         });
7238         
7239         return rowIndex;
7240     },
7241      /**
7242      * Returns the grid's underlying element = used by panel.Grid
7243      * @return {Element} The element
7244      */
7245     getGridEl : function(){
7246         return this.el;
7247     },
7248      /**
7249      * Forces a resize - used by panel.Grid
7250      * @return {Element} The element
7251      */
7252     autoSize : function()
7253     {
7254         //var ctr = Roo.get(this.container.dom.parentElement);
7255         var ctr = Roo.get(this.el.dom);
7256         
7257         var thd = this.getGridEl().select('thead',true).first();
7258         var tbd = this.getGridEl().select('tbody', true).first();
7259         var tfd = this.getGridEl().select('tfoot', true).first();
7260         
7261         var cw = ctr.getWidth();
7262         
7263         if (tbd) {
7264             
7265             tbd.setSize(ctr.getWidth(),
7266                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7267             );
7268             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7269             cw -= barsize;
7270         }
7271         cw = Math.max(cw, this.totalWidth);
7272         this.getGridEl().select('tr',true).setWidth(cw);
7273         // resize 'expandable coloumn?
7274         
7275         return; // we doe not have a view in this design..
7276         
7277     },
7278     onBodyScroll: function()
7279     {
7280         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7281         if(this.mainHead){
7282             this.mainHead.setStyle({
7283                 'position' : 'relative',
7284                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7285             });
7286         }
7287         
7288         if(this.lazyLoad){
7289             
7290             var scrollHeight = this.mainBody.dom.scrollHeight;
7291             
7292             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7293             
7294             var height = this.mainBody.getHeight();
7295             
7296             if(scrollHeight - height == scrollTop) {
7297                 
7298                 var total = this.ds.getTotalCount();
7299                 
7300                 if(this.footer.cursor + this.footer.pageSize < total){
7301                     
7302                     this.footer.ds.load({
7303                         params : {
7304                             start : this.footer.cursor + this.footer.pageSize,
7305                             limit : this.footer.pageSize
7306                         },
7307                         add : true
7308                     });
7309                 }
7310             }
7311             
7312         }
7313     },
7314     
7315     onHeaderChange : function()
7316     {
7317         var header = this.renderHeader();
7318         var table = this.el.select('table', true).first();
7319         
7320         this.mainHead.remove();
7321         this.mainHead = table.createChild(header, this.mainBody, false);
7322     },
7323     
7324     onHiddenChange : function(colModel, colIndex, hidden)
7325     {
7326         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7327         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7328         
7329         this.CSS.updateRule(thSelector, "display", "");
7330         this.CSS.updateRule(tdSelector, "display", "");
7331         
7332         if(hidden){
7333             this.CSS.updateRule(thSelector, "display", "none");
7334             this.CSS.updateRule(tdSelector, "display", "none");
7335         }
7336         
7337         this.onHeaderChange();
7338         this.onLoad();
7339     },
7340     
7341     setColumnWidth: function(col_index, width)
7342     {
7343         // width = "md-2 xs-2..."
7344         if(!this.colModel.config[col_index]) {
7345             return;
7346         }
7347         
7348         var w = width.split(" ");
7349         
7350         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7351         
7352         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7353         
7354         
7355         for(var j = 0; j < w.length; j++) {
7356             
7357             if(!w[j]) {
7358                 continue;
7359             }
7360             
7361             var size_cls = w[j].split("-");
7362             
7363             if(!Number.isInteger(size_cls[1] * 1)) {
7364                 continue;
7365             }
7366             
7367             if(!this.colModel.config[col_index][size_cls[0]]) {
7368                 continue;
7369             }
7370             
7371             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7372                 continue;
7373             }
7374             
7375             h_row[0].classList.replace(
7376                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7377                 "col-"+size_cls[0]+"-"+size_cls[1]
7378             );
7379             
7380             for(var i = 0; i < rows.length; i++) {
7381                 
7382                 var size_cls = w[j].split("-");
7383                 
7384                 if(!Number.isInteger(size_cls[1] * 1)) {
7385                     continue;
7386                 }
7387                 
7388                 if(!this.colModel.config[col_index][size_cls[0]]) {
7389                     continue;
7390                 }
7391                 
7392                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7393                     continue;
7394                 }
7395                 
7396                 rows[i].classList.replace(
7397                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7398                     "col-"+size_cls[0]+"-"+size_cls[1]
7399                 );
7400             }
7401             
7402             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7403         }
7404     }
7405 });
7406
7407  
7408
7409  /*
7410  * - LGPL
7411  *
7412  * table cell
7413  * 
7414  */
7415
7416 /**
7417  * @class Roo.bootstrap.TableCell
7418  * @extends Roo.bootstrap.Component
7419  * Bootstrap TableCell class
7420  * @cfg {String} html cell contain text
7421  * @cfg {String} cls cell class
7422  * @cfg {String} tag cell tag (td|th) default td
7423  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7424  * @cfg {String} align Aligns the content in a cell
7425  * @cfg {String} axis Categorizes cells
7426  * @cfg {String} bgcolor Specifies the background color of a cell
7427  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7428  * @cfg {Number} colspan Specifies the number of columns a cell should span
7429  * @cfg {String} headers Specifies one or more header cells a cell is related to
7430  * @cfg {Number} height Sets the height of a cell
7431  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7432  * @cfg {Number} rowspan Sets the number of rows a cell should span
7433  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7434  * @cfg {String} valign Vertical aligns the content in a cell
7435  * @cfg {Number} width Specifies the width of a cell
7436  * 
7437  * @constructor
7438  * Create a new TableCell
7439  * @param {Object} config The config object
7440  */
7441
7442 Roo.bootstrap.TableCell = function(config){
7443     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7444 };
7445
7446 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7447     
7448     html: false,
7449     cls: false,
7450     tag: false,
7451     abbr: false,
7452     align: false,
7453     axis: false,
7454     bgcolor: false,
7455     charoff: false,
7456     colspan: false,
7457     headers: false,
7458     height: false,
7459     nowrap: false,
7460     rowspan: false,
7461     scope: false,
7462     valign: false,
7463     width: false,
7464     
7465     
7466     getAutoCreate : function(){
7467         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7468         
7469         cfg = {
7470             tag: 'td'
7471         };
7472         
7473         if(this.tag){
7474             cfg.tag = this.tag;
7475         }
7476         
7477         if (this.html) {
7478             cfg.html=this.html
7479         }
7480         if (this.cls) {
7481             cfg.cls=this.cls
7482         }
7483         if (this.abbr) {
7484             cfg.abbr=this.abbr
7485         }
7486         if (this.align) {
7487             cfg.align=this.align
7488         }
7489         if (this.axis) {
7490             cfg.axis=this.axis
7491         }
7492         if (this.bgcolor) {
7493             cfg.bgcolor=this.bgcolor
7494         }
7495         if (this.charoff) {
7496             cfg.charoff=this.charoff
7497         }
7498         if (this.colspan) {
7499             cfg.colspan=this.colspan
7500         }
7501         if (this.headers) {
7502             cfg.headers=this.headers
7503         }
7504         if (this.height) {
7505             cfg.height=this.height
7506         }
7507         if (this.nowrap) {
7508             cfg.nowrap=this.nowrap
7509         }
7510         if (this.rowspan) {
7511             cfg.rowspan=this.rowspan
7512         }
7513         if (this.scope) {
7514             cfg.scope=this.scope
7515         }
7516         if (this.valign) {
7517             cfg.valign=this.valign
7518         }
7519         if (this.width) {
7520             cfg.width=this.width
7521         }
7522         
7523         
7524         return cfg;
7525     }
7526    
7527 });
7528
7529  
7530
7531  /*
7532  * - LGPL
7533  *
7534  * table row
7535  * 
7536  */
7537
7538 /**
7539  * @class Roo.bootstrap.TableRow
7540  * @extends Roo.bootstrap.Component
7541  * Bootstrap TableRow class
7542  * @cfg {String} cls row class
7543  * @cfg {String} align Aligns the content in a table row
7544  * @cfg {String} bgcolor Specifies a background color for a table row
7545  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7546  * @cfg {String} valign Vertical aligns the content in a table row
7547  * 
7548  * @constructor
7549  * Create a new TableRow
7550  * @param {Object} config The config object
7551  */
7552
7553 Roo.bootstrap.TableRow = function(config){
7554     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7555 };
7556
7557 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7558     
7559     cls: false,
7560     align: false,
7561     bgcolor: false,
7562     charoff: false,
7563     valign: false,
7564     
7565     getAutoCreate : function(){
7566         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7567         
7568         cfg = {
7569             tag: 'tr'
7570         };
7571             
7572         if(this.cls){
7573             cfg.cls = this.cls;
7574         }
7575         if(this.align){
7576             cfg.align = this.align;
7577         }
7578         if(this.bgcolor){
7579             cfg.bgcolor = this.bgcolor;
7580         }
7581         if(this.charoff){
7582             cfg.charoff = this.charoff;
7583         }
7584         if(this.valign){
7585             cfg.valign = this.valign;
7586         }
7587         
7588         return cfg;
7589     }
7590    
7591 });
7592
7593  
7594
7595  /*
7596  * - LGPL
7597  *
7598  * table body
7599  * 
7600  */
7601
7602 /**
7603  * @class Roo.bootstrap.TableBody
7604  * @extends Roo.bootstrap.Component
7605  * Bootstrap TableBody class
7606  * @cfg {String} cls element class
7607  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7608  * @cfg {String} align Aligns the content inside the element
7609  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7610  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7611  * 
7612  * @constructor
7613  * Create a new TableBody
7614  * @param {Object} config The config object
7615  */
7616
7617 Roo.bootstrap.TableBody = function(config){
7618     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7619 };
7620
7621 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7622     
7623     cls: false,
7624     tag: false,
7625     align: false,
7626     charoff: false,
7627     valign: false,
7628     
7629     getAutoCreate : function(){
7630         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7631         
7632         cfg = {
7633             tag: 'tbody'
7634         };
7635             
7636         if (this.cls) {
7637             cfg.cls=this.cls
7638         }
7639         if(this.tag){
7640             cfg.tag = this.tag;
7641         }
7642         
7643         if(this.align){
7644             cfg.align = this.align;
7645         }
7646         if(this.charoff){
7647             cfg.charoff = this.charoff;
7648         }
7649         if(this.valign){
7650             cfg.valign = this.valign;
7651         }
7652         
7653         return cfg;
7654     }
7655     
7656     
7657 //    initEvents : function()
7658 //    {
7659 //        
7660 //        if(!this.store){
7661 //            return;
7662 //        }
7663 //        
7664 //        this.store = Roo.factory(this.store, Roo.data);
7665 //        this.store.on('load', this.onLoad, this);
7666 //        
7667 //        this.store.load();
7668 //        
7669 //    },
7670 //    
7671 //    onLoad: function () 
7672 //    {   
7673 //        this.fireEvent('load', this);
7674 //    }
7675 //    
7676 //   
7677 });
7678
7679  
7680
7681  /*
7682  * Based on:
7683  * Ext JS Library 1.1.1
7684  * Copyright(c) 2006-2007, Ext JS, LLC.
7685  *
7686  * Originally Released Under LGPL - original licence link has changed is not relivant.
7687  *
7688  * Fork - LGPL
7689  * <script type="text/javascript">
7690  */
7691
7692 // as we use this in bootstrap.
7693 Roo.namespace('Roo.form');
7694  /**
7695  * @class Roo.form.Action
7696  * Internal Class used to handle form actions
7697  * @constructor
7698  * @param {Roo.form.BasicForm} el The form element or its id
7699  * @param {Object} config Configuration options
7700  */
7701
7702  
7703  
7704 // define the action interface
7705 Roo.form.Action = function(form, options){
7706     this.form = form;
7707     this.options = options || {};
7708 };
7709 /**
7710  * Client Validation Failed
7711  * @const 
7712  */
7713 Roo.form.Action.CLIENT_INVALID = 'client';
7714 /**
7715  * Server Validation Failed
7716  * @const 
7717  */
7718 Roo.form.Action.SERVER_INVALID = 'server';
7719  /**
7720  * Connect to Server Failed
7721  * @const 
7722  */
7723 Roo.form.Action.CONNECT_FAILURE = 'connect';
7724 /**
7725  * Reading Data from Server Failed
7726  * @const 
7727  */
7728 Roo.form.Action.LOAD_FAILURE = 'load';
7729
7730 Roo.form.Action.prototype = {
7731     type : 'default',
7732     failureType : undefined,
7733     response : undefined,
7734     result : undefined,
7735
7736     // interface method
7737     run : function(options){
7738
7739     },
7740
7741     // interface method
7742     success : function(response){
7743
7744     },
7745
7746     // interface method
7747     handleResponse : function(response){
7748
7749     },
7750
7751     // default connection failure
7752     failure : function(response){
7753         
7754         this.response = response;
7755         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7756         this.form.afterAction(this, false);
7757     },
7758
7759     processResponse : function(response){
7760         this.response = response;
7761         if(!response.responseText){
7762             return true;
7763         }
7764         this.result = this.handleResponse(response);
7765         return this.result;
7766     },
7767
7768     // utility functions used internally
7769     getUrl : function(appendParams){
7770         var url = this.options.url || this.form.url || this.form.el.dom.action;
7771         if(appendParams){
7772             var p = this.getParams();
7773             if(p){
7774                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7775             }
7776         }
7777         return url;
7778     },
7779
7780     getMethod : function(){
7781         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7782     },
7783
7784     getParams : function(){
7785         var bp = this.form.baseParams;
7786         var p = this.options.params;
7787         if(p){
7788             if(typeof p == "object"){
7789                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7790             }else if(typeof p == 'string' && bp){
7791                 p += '&' + Roo.urlEncode(bp);
7792             }
7793         }else if(bp){
7794             p = Roo.urlEncode(bp);
7795         }
7796         return p;
7797     },
7798
7799     createCallback : function(){
7800         return {
7801             success: this.success,
7802             failure: this.failure,
7803             scope: this,
7804             timeout: (this.form.timeout*1000),
7805             upload: this.form.fileUpload ? this.success : undefined
7806         };
7807     }
7808 };
7809
7810 Roo.form.Action.Submit = function(form, options){
7811     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7812 };
7813
7814 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7815     type : 'submit',
7816
7817     haveProgress : false,
7818     uploadComplete : false,
7819     
7820     // uploadProgress indicator.
7821     uploadProgress : function()
7822     {
7823         if (!this.form.progressUrl) {
7824             return;
7825         }
7826         
7827         if (!this.haveProgress) {
7828             Roo.MessageBox.progress("Uploading", "Uploading");
7829         }
7830         if (this.uploadComplete) {
7831            Roo.MessageBox.hide();
7832            return;
7833         }
7834         
7835         this.haveProgress = true;
7836    
7837         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7838         
7839         var c = new Roo.data.Connection();
7840         c.request({
7841             url : this.form.progressUrl,
7842             params: {
7843                 id : uid
7844             },
7845             method: 'GET',
7846             success : function(req){
7847                //console.log(data);
7848                 var rdata = false;
7849                 var edata;
7850                 try  {
7851                    rdata = Roo.decode(req.responseText)
7852                 } catch (e) {
7853                     Roo.log("Invalid data from server..");
7854                     Roo.log(edata);
7855                     return;
7856                 }
7857                 if (!rdata || !rdata.success) {
7858                     Roo.log(rdata);
7859                     Roo.MessageBox.alert(Roo.encode(rdata));
7860                     return;
7861                 }
7862                 var data = rdata.data;
7863                 
7864                 if (this.uploadComplete) {
7865                    Roo.MessageBox.hide();
7866                    return;
7867                 }
7868                    
7869                 if (data){
7870                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7871                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7872                     );
7873                 }
7874                 this.uploadProgress.defer(2000,this);
7875             },
7876        
7877             failure: function(data) {
7878                 Roo.log('progress url failed ');
7879                 Roo.log(data);
7880             },
7881             scope : this
7882         });
7883            
7884     },
7885     
7886     
7887     run : function()
7888     {
7889         // run get Values on the form, so it syncs any secondary forms.
7890         this.form.getValues();
7891         
7892         var o = this.options;
7893         var method = this.getMethod();
7894         var isPost = method == 'POST';
7895         if(o.clientValidation === false || this.form.isValid()){
7896             
7897             if (this.form.progressUrl) {
7898                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7899                     (new Date() * 1) + '' + Math.random());
7900                     
7901             } 
7902             
7903             
7904             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7905                 form:this.form.el.dom,
7906                 url:this.getUrl(!isPost),
7907                 method: method,
7908                 params:isPost ? this.getParams() : null,
7909                 isUpload: this.form.fileUpload
7910             }));
7911             
7912             this.uploadProgress();
7913
7914         }else if (o.clientValidation !== false){ // client validation failed
7915             this.failureType = Roo.form.Action.CLIENT_INVALID;
7916             this.form.afterAction(this, false);
7917         }
7918     },
7919
7920     success : function(response)
7921     {
7922         this.uploadComplete= true;
7923         if (this.haveProgress) {
7924             Roo.MessageBox.hide();
7925         }
7926         
7927         
7928         var result = this.processResponse(response);
7929         if(result === true || result.success){
7930             this.form.afterAction(this, true);
7931             return;
7932         }
7933         if(result.errors){
7934             this.form.markInvalid(result.errors);
7935             this.failureType = Roo.form.Action.SERVER_INVALID;
7936         }
7937         this.form.afterAction(this, false);
7938     },
7939     failure : function(response)
7940     {
7941         this.uploadComplete= true;
7942         if (this.haveProgress) {
7943             Roo.MessageBox.hide();
7944         }
7945         
7946         this.response = response;
7947         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7948         this.form.afterAction(this, false);
7949     },
7950     
7951     handleResponse : function(response){
7952         if(this.form.errorReader){
7953             var rs = this.form.errorReader.read(response);
7954             var errors = [];
7955             if(rs.records){
7956                 for(var i = 0, len = rs.records.length; i < len; i++) {
7957                     var r = rs.records[i];
7958                     errors[i] = r.data;
7959                 }
7960             }
7961             if(errors.length < 1){
7962                 errors = null;
7963             }
7964             return {
7965                 success : rs.success,
7966                 errors : errors
7967             };
7968         }
7969         var ret = false;
7970         try {
7971             ret = Roo.decode(response.responseText);
7972         } catch (e) {
7973             ret = {
7974                 success: false,
7975                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7976                 errors : []
7977             };
7978         }
7979         return ret;
7980         
7981     }
7982 });
7983
7984
7985 Roo.form.Action.Load = function(form, options){
7986     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7987     this.reader = this.form.reader;
7988 };
7989
7990 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7991     type : 'load',
7992
7993     run : function(){
7994         
7995         Roo.Ajax.request(Roo.apply(
7996                 this.createCallback(), {
7997                     method:this.getMethod(),
7998                     url:this.getUrl(false),
7999                     params:this.getParams()
8000         }));
8001     },
8002
8003     success : function(response){
8004         
8005         var result = this.processResponse(response);
8006         if(result === true || !result.success || !result.data){
8007             this.failureType = Roo.form.Action.LOAD_FAILURE;
8008             this.form.afterAction(this, false);
8009             return;
8010         }
8011         this.form.clearInvalid();
8012         this.form.setValues(result.data);
8013         this.form.afterAction(this, true);
8014     },
8015
8016     handleResponse : function(response){
8017         if(this.form.reader){
8018             var rs = this.form.reader.read(response);
8019             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8020             return {
8021                 success : rs.success,
8022                 data : data
8023             };
8024         }
8025         return Roo.decode(response.responseText);
8026     }
8027 });
8028
8029 Roo.form.Action.ACTION_TYPES = {
8030     'load' : Roo.form.Action.Load,
8031     'submit' : Roo.form.Action.Submit
8032 };/*
8033  * - LGPL
8034  *
8035  * form
8036  *
8037  */
8038
8039 /**
8040  * @class Roo.bootstrap.Form
8041  * @extends Roo.bootstrap.Component
8042  * Bootstrap Form class
8043  * @cfg {String} method  GET | POST (default POST)
8044  * @cfg {String} labelAlign top | left (default top)
8045  * @cfg {String} align left  | right - for navbars
8046  * @cfg {Boolean} loadMask load mask when submit (default true)
8047
8048  *
8049  * @constructor
8050  * Create a new Form
8051  * @param {Object} config The config object
8052  */
8053
8054
8055 Roo.bootstrap.Form = function(config){
8056     
8057     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8058     
8059     Roo.bootstrap.Form.popover.apply();
8060     
8061     this.addEvents({
8062         /**
8063          * @event clientvalidation
8064          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8065          * @param {Form} this
8066          * @param {Boolean} valid true if the form has passed client-side validation
8067          */
8068         clientvalidation: true,
8069         /**
8070          * @event beforeaction
8071          * Fires before any action is performed. Return false to cancel the action.
8072          * @param {Form} this
8073          * @param {Action} action The action to be performed
8074          */
8075         beforeaction: true,
8076         /**
8077          * @event actionfailed
8078          * Fires when an action fails.
8079          * @param {Form} this
8080          * @param {Action} action The action that failed
8081          */
8082         actionfailed : true,
8083         /**
8084          * @event actioncomplete
8085          * Fires when an action is completed.
8086          * @param {Form} this
8087          * @param {Action} action The action that completed
8088          */
8089         actioncomplete : true
8090     });
8091 };
8092
8093 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8094
8095      /**
8096      * @cfg {String} method
8097      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8098      */
8099     method : 'POST',
8100     /**
8101      * @cfg {String} url
8102      * The URL to use for form actions if one isn't supplied in the action options.
8103      */
8104     /**
8105      * @cfg {Boolean} fileUpload
8106      * Set to true if this form is a file upload.
8107      */
8108
8109     /**
8110      * @cfg {Object} baseParams
8111      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8112      */
8113
8114     /**
8115      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8116      */
8117     timeout: 30,
8118     /**
8119      * @cfg {Sting} align (left|right) for navbar forms
8120      */
8121     align : 'left',
8122
8123     // private
8124     activeAction : null,
8125
8126     /**
8127      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8128      * element by passing it or its id or mask the form itself by passing in true.
8129      * @type Mixed
8130      */
8131     waitMsgTarget : false,
8132
8133     loadMask : true,
8134     
8135     /**
8136      * @cfg {Boolean} errorMask (true|false) default false
8137      */
8138     errorMask : false,
8139     
8140     /**
8141      * @cfg {Number} maskOffset Default 100
8142      */
8143     maskOffset : 100,
8144     
8145     /**
8146      * @cfg {Boolean} maskBody
8147      */
8148     maskBody : false,
8149
8150     getAutoCreate : function(){
8151
8152         var cfg = {
8153             tag: 'form',
8154             method : this.method || 'POST',
8155             id : this.id || Roo.id(),
8156             cls : ''
8157         };
8158         if (this.parent().xtype.match(/^Nav/)) {
8159             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8160
8161         }
8162
8163         if (this.labelAlign == 'left' ) {
8164             cfg.cls += ' form-horizontal';
8165         }
8166
8167
8168         return cfg;
8169     },
8170     initEvents : function()
8171     {
8172         this.el.on('submit', this.onSubmit, this);
8173         // this was added as random key presses on the form where triggering form submit.
8174         this.el.on('keypress', function(e) {
8175             if (e.getCharCode() != 13) {
8176                 return true;
8177             }
8178             // we might need to allow it for textareas.. and some other items.
8179             // check e.getTarget().
8180
8181             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8182                 return true;
8183             }
8184
8185             Roo.log("keypress blocked");
8186
8187             e.preventDefault();
8188             return false;
8189         });
8190         
8191     },
8192     // private
8193     onSubmit : function(e){
8194         e.stopEvent();
8195     },
8196
8197      /**
8198      * Returns true if client-side validation on the form is successful.
8199      * @return Boolean
8200      */
8201     isValid : function(){
8202         var items = this.getItems();
8203         var valid = true;
8204         var target = false;
8205         
8206         items.each(function(f){
8207             
8208             if(f.validate()){
8209                 return;
8210             }
8211             
8212             Roo.log('invalid field: ' + f.name);
8213             
8214             valid = false;
8215
8216             if(!target && f.el.isVisible(true)){
8217                 target = f;
8218             }
8219            
8220         });
8221         
8222         if(this.errorMask && !valid){
8223             Roo.bootstrap.Form.popover.mask(this, target);
8224         }
8225         
8226         return valid;
8227     },
8228     
8229     /**
8230      * Returns true if any fields in this form have changed since their original load.
8231      * @return Boolean
8232      */
8233     isDirty : function(){
8234         var dirty = false;
8235         var items = this.getItems();
8236         items.each(function(f){
8237            if(f.isDirty()){
8238                dirty = true;
8239                return false;
8240            }
8241            return true;
8242         });
8243         return dirty;
8244     },
8245      /**
8246      * Performs a predefined action (submit or load) or custom actions you define on this form.
8247      * @param {String} actionName The name of the action type
8248      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8249      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8250      * accept other config options):
8251      * <pre>
8252 Property          Type             Description
8253 ----------------  ---------------  ----------------------------------------------------------------------------------
8254 url               String           The url for the action (defaults to the form's url)
8255 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8256 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8257 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8258                                    validate the form on the client (defaults to false)
8259      * </pre>
8260      * @return {BasicForm} this
8261      */
8262     doAction : function(action, options){
8263         if(typeof action == 'string'){
8264             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8265         }
8266         if(this.fireEvent('beforeaction', this, action) !== false){
8267             this.beforeAction(action);
8268             action.run.defer(100, action);
8269         }
8270         return this;
8271     },
8272
8273     // private
8274     beforeAction : function(action){
8275         var o = action.options;
8276         
8277         if(this.loadMask){
8278             
8279             if(this.maskBody){
8280                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8281             } else {
8282                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8283             }
8284         }
8285         // not really supported yet.. ??
8286
8287         //if(this.waitMsgTarget === true){
8288         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8289         //}else if(this.waitMsgTarget){
8290         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8291         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8292         //}else {
8293         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8294        // }
8295
8296     },
8297
8298     // private
8299     afterAction : function(action, success){
8300         this.activeAction = null;
8301         var o = action.options;
8302
8303         if(this.loadMask){
8304             
8305             if(this.maskBody){
8306                 Roo.get(document.body).unmask();
8307             } else {
8308                 this.el.unmask();
8309             }
8310         }
8311         
8312         //if(this.waitMsgTarget === true){
8313 //            this.el.unmask();
8314         //}else if(this.waitMsgTarget){
8315         //    this.waitMsgTarget.unmask();
8316         //}else{
8317         //    Roo.MessageBox.updateProgress(1);
8318         //    Roo.MessageBox.hide();
8319        // }
8320         //
8321         if(success){
8322             if(o.reset){
8323                 this.reset();
8324             }
8325             Roo.callback(o.success, o.scope, [this, action]);
8326             this.fireEvent('actioncomplete', this, action);
8327
8328         }else{
8329
8330             // failure condition..
8331             // we have a scenario where updates need confirming.
8332             // eg. if a locking scenario exists..
8333             // we look for { errors : { needs_confirm : true }} in the response.
8334             if (
8335                 (typeof(action.result) != 'undefined')  &&
8336                 (typeof(action.result.errors) != 'undefined')  &&
8337                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8338            ){
8339                 var _t = this;
8340                 Roo.log("not supported yet");
8341                  /*
8342
8343                 Roo.MessageBox.confirm(
8344                     "Change requires confirmation",
8345                     action.result.errorMsg,
8346                     function(r) {
8347                         if (r != 'yes') {
8348                             return;
8349                         }
8350                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8351                     }
8352
8353                 );
8354                 */
8355
8356
8357                 return;
8358             }
8359
8360             Roo.callback(o.failure, o.scope, [this, action]);
8361             // show an error message if no failed handler is set..
8362             if (!this.hasListener('actionfailed')) {
8363                 Roo.log("need to add dialog support");
8364                 /*
8365                 Roo.MessageBox.alert("Error",
8366                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8367                         action.result.errorMsg :
8368                         "Saving Failed, please check your entries or try again"
8369                 );
8370                 */
8371             }
8372
8373             this.fireEvent('actionfailed', this, action);
8374         }
8375
8376     },
8377     /**
8378      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8379      * @param {String} id The value to search for
8380      * @return Field
8381      */
8382     findField : function(id){
8383         var items = this.getItems();
8384         var field = items.get(id);
8385         if(!field){
8386              items.each(function(f){
8387                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8388                     field = f;
8389                     return false;
8390                 }
8391                 return true;
8392             });
8393         }
8394         return field || null;
8395     },
8396      /**
8397      * Mark fields in this form invalid in bulk.
8398      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8399      * @return {BasicForm} this
8400      */
8401     markInvalid : function(errors){
8402         if(errors instanceof Array){
8403             for(var i = 0, len = errors.length; i < len; i++){
8404                 var fieldError = errors[i];
8405                 var f = this.findField(fieldError.id);
8406                 if(f){
8407                     f.markInvalid(fieldError.msg);
8408                 }
8409             }
8410         }else{
8411             var field, id;
8412             for(id in errors){
8413                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8414                     field.markInvalid(errors[id]);
8415                 }
8416             }
8417         }
8418         //Roo.each(this.childForms || [], function (f) {
8419         //    f.markInvalid(errors);
8420         //});
8421
8422         return this;
8423     },
8424
8425     /**
8426      * Set values for fields in this form in bulk.
8427      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8428      * @return {BasicForm} this
8429      */
8430     setValues : function(values){
8431         if(values instanceof Array){ // array of objects
8432             for(var i = 0, len = values.length; i < len; i++){
8433                 var v = values[i];
8434                 var f = this.findField(v.id);
8435                 if(f){
8436                     f.setValue(v.value);
8437                     if(this.trackResetOnLoad){
8438                         f.originalValue = f.getValue();
8439                     }
8440                 }
8441             }
8442         }else{ // object hash
8443             var field, id;
8444             for(id in values){
8445                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8446
8447                     if (field.setFromData &&
8448                         field.valueField &&
8449                         field.displayField &&
8450                         // combos' with local stores can
8451                         // be queried via setValue()
8452                         // to set their value..
8453                         (field.store && !field.store.isLocal)
8454                         ) {
8455                         // it's a combo
8456                         var sd = { };
8457                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8458                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8459                         field.setFromData(sd);
8460
8461                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8462                         
8463                         field.setFromData(values);
8464                         
8465                     } else {
8466                         field.setValue(values[id]);
8467                     }
8468
8469
8470                     if(this.trackResetOnLoad){
8471                         field.originalValue = field.getValue();
8472                     }
8473                 }
8474             }
8475         }
8476
8477         //Roo.each(this.childForms || [], function (f) {
8478         //    f.setValues(values);
8479         //});
8480
8481         return this;
8482     },
8483
8484     /**
8485      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8486      * they are returned as an array.
8487      * @param {Boolean} asString
8488      * @return {Object}
8489      */
8490     getValues : function(asString){
8491         //if (this.childForms) {
8492             // copy values from the child forms
8493         //    Roo.each(this.childForms, function (f) {
8494         //        this.setValues(f.getValues());
8495         //    }, this);
8496         //}
8497
8498
8499
8500         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8501         if(asString === true){
8502             return fs;
8503         }
8504         return Roo.urlDecode(fs);
8505     },
8506
8507     /**
8508      * Returns the fields in this form as an object with key/value pairs.
8509      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8510      * @return {Object}
8511      */
8512     getFieldValues : function(with_hidden)
8513     {
8514         var items = this.getItems();
8515         var ret = {};
8516         items.each(function(f){
8517             
8518             if (!f.getName()) {
8519                 return;
8520             }
8521             
8522             var v = f.getValue();
8523             
8524             if (f.inputType =='radio') {
8525                 if (typeof(ret[f.getName()]) == 'undefined') {
8526                     ret[f.getName()] = ''; // empty..
8527                 }
8528
8529                 if (!f.el.dom.checked) {
8530                     return;
8531
8532                 }
8533                 v = f.el.dom.value;
8534
8535             }
8536             
8537             if(f.xtype == 'MoneyField'){
8538                 ret[f.currencyName] = f.getCurrency();
8539             }
8540
8541             // not sure if this supported any more..
8542             if ((typeof(v) == 'object') && f.getRawValue) {
8543                 v = f.getRawValue() ; // dates..
8544             }
8545             // combo boxes where name != hiddenName...
8546             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8547                 ret[f.name] = f.getRawValue();
8548             }
8549             ret[f.getName()] = v;
8550         });
8551
8552         return ret;
8553     },
8554
8555     /**
8556      * Clears all invalid messages in this form.
8557      * @return {BasicForm} this
8558      */
8559     clearInvalid : function(){
8560         var items = this.getItems();
8561
8562         items.each(function(f){
8563            f.clearInvalid();
8564         });
8565
8566         return this;
8567     },
8568
8569     /**
8570      * Resets this form.
8571      * @return {BasicForm} this
8572      */
8573     reset : function(){
8574         var items = this.getItems();
8575         items.each(function(f){
8576             f.reset();
8577         });
8578
8579         Roo.each(this.childForms || [], function (f) {
8580             f.reset();
8581         });
8582
8583
8584         return this;
8585     },
8586     
8587     getItems : function()
8588     {
8589         var r=new Roo.util.MixedCollection(false, function(o){
8590             return o.id || (o.id = Roo.id());
8591         });
8592         var iter = function(el) {
8593             if (el.inputEl) {
8594                 r.add(el);
8595             }
8596             if (!el.items) {
8597                 return;
8598             }
8599             Roo.each(el.items,function(e) {
8600                 iter(e);
8601             });
8602         };
8603
8604         iter(this);
8605         return r;
8606     },
8607     
8608     hideFields : function(items)
8609     {
8610         Roo.each(items, function(i){
8611             
8612             var f = this.findField(i);
8613             
8614             if(!f){
8615                 return;
8616             }
8617             
8618             f.hide();
8619             
8620         }, this);
8621     },
8622     
8623     showFields : function(items)
8624     {
8625         Roo.each(items, function(i){
8626             
8627             var f = this.findField(i);
8628             
8629             if(!f){
8630                 return;
8631             }
8632             
8633             f.show();
8634             
8635         }, this);
8636     }
8637
8638 });
8639
8640 Roo.apply(Roo.bootstrap.Form, {
8641     
8642     popover : {
8643         
8644         padding : 5,
8645         
8646         isApplied : false,
8647         
8648         isMasked : false,
8649         
8650         form : false,
8651         
8652         target : false,
8653         
8654         toolTip : false,
8655         
8656         intervalID : false,
8657         
8658         maskEl : false,
8659         
8660         apply : function()
8661         {
8662             if(this.isApplied){
8663                 return;
8664             }
8665             
8666             this.maskEl = {
8667                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8668                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8669                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8670                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8671             };
8672             
8673             this.maskEl.top.enableDisplayMode("block");
8674             this.maskEl.left.enableDisplayMode("block");
8675             this.maskEl.bottom.enableDisplayMode("block");
8676             this.maskEl.right.enableDisplayMode("block");
8677             
8678             this.toolTip = new Roo.bootstrap.Tooltip({
8679                 cls : 'roo-form-error-popover',
8680                 alignment : {
8681                     'left' : ['r-l', [-2,0], 'right'],
8682                     'right' : ['l-r', [2,0], 'left'],
8683                     'bottom' : ['tl-bl', [0,2], 'top'],
8684                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8685                 }
8686             });
8687             
8688             this.toolTip.render(Roo.get(document.body));
8689
8690             this.toolTip.el.enableDisplayMode("block");
8691             
8692             Roo.get(document.body).on('click', function(){
8693                 this.unmask();
8694             }, this);
8695             
8696             Roo.get(document.body).on('touchstart', function(){
8697                 this.unmask();
8698             }, this);
8699             
8700             this.isApplied = true
8701         },
8702         
8703         mask : function(form, target)
8704         {
8705             this.form = form;
8706             
8707             this.target = target;
8708             
8709             if(!this.form.errorMask || !target.el){
8710                 return;
8711             }
8712             
8713             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8714             
8715             Roo.log(scrollable);
8716             
8717             var ot = this.target.el.calcOffsetsTo(scrollable);
8718             
8719             var scrollTo = ot[1] - this.form.maskOffset;
8720             
8721             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8722             
8723             scrollable.scrollTo('top', scrollTo);
8724             
8725             var box = this.target.el.getBox();
8726             Roo.log(box);
8727             var zIndex = Roo.bootstrap.Modal.zIndex++;
8728
8729             
8730             this.maskEl.top.setStyle('position', 'absolute');
8731             this.maskEl.top.setStyle('z-index', zIndex);
8732             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8733             this.maskEl.top.setLeft(0);
8734             this.maskEl.top.setTop(0);
8735             this.maskEl.top.show();
8736             
8737             this.maskEl.left.setStyle('position', 'absolute');
8738             this.maskEl.left.setStyle('z-index', zIndex);
8739             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8740             this.maskEl.left.setLeft(0);
8741             this.maskEl.left.setTop(box.y - this.padding);
8742             this.maskEl.left.show();
8743
8744             this.maskEl.bottom.setStyle('position', 'absolute');
8745             this.maskEl.bottom.setStyle('z-index', zIndex);
8746             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8747             this.maskEl.bottom.setLeft(0);
8748             this.maskEl.bottom.setTop(box.bottom + this.padding);
8749             this.maskEl.bottom.show();
8750
8751             this.maskEl.right.setStyle('position', 'absolute');
8752             this.maskEl.right.setStyle('z-index', zIndex);
8753             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8754             this.maskEl.right.setLeft(box.right + this.padding);
8755             this.maskEl.right.setTop(box.y - this.padding);
8756             this.maskEl.right.show();
8757
8758             this.toolTip.bindEl = this.target.el;
8759
8760             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8761
8762             var tip = this.target.blankText;
8763
8764             if(this.target.getValue() !== '' ) {
8765                 
8766                 if (this.target.invalidText.length) {
8767                     tip = this.target.invalidText;
8768                 } else if (this.target.regexText.length){
8769                     tip = this.target.regexText;
8770                 }
8771             }
8772
8773             this.toolTip.show(tip);
8774
8775             this.intervalID = window.setInterval(function() {
8776                 Roo.bootstrap.Form.popover.unmask();
8777             }, 10000);
8778
8779             window.onwheel = function(){ return false;};
8780             
8781             (function(){ this.isMasked = true; }).defer(500, this);
8782             
8783         },
8784         
8785         unmask : function()
8786         {
8787             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8788                 return;
8789             }
8790             
8791             this.maskEl.top.setStyle('position', 'absolute');
8792             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8793             this.maskEl.top.hide();
8794
8795             this.maskEl.left.setStyle('position', 'absolute');
8796             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8797             this.maskEl.left.hide();
8798
8799             this.maskEl.bottom.setStyle('position', 'absolute');
8800             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8801             this.maskEl.bottom.hide();
8802
8803             this.maskEl.right.setStyle('position', 'absolute');
8804             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8805             this.maskEl.right.hide();
8806             
8807             this.toolTip.hide();
8808             
8809             this.toolTip.el.hide();
8810             
8811             window.onwheel = function(){ return true;};
8812             
8813             if(this.intervalID){
8814                 window.clearInterval(this.intervalID);
8815                 this.intervalID = false;
8816             }
8817             
8818             this.isMasked = false;
8819             
8820         }
8821         
8822     }
8823     
8824 });
8825
8826 /*
8827  * Based on:
8828  * Ext JS Library 1.1.1
8829  * Copyright(c) 2006-2007, Ext JS, LLC.
8830  *
8831  * Originally Released Under LGPL - original licence link has changed is not relivant.
8832  *
8833  * Fork - LGPL
8834  * <script type="text/javascript">
8835  */
8836 /**
8837  * @class Roo.form.VTypes
8838  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8839  * @singleton
8840  */
8841 Roo.form.VTypes = function(){
8842     // closure these in so they are only created once.
8843     var alpha = /^[a-zA-Z_]+$/;
8844     var alphanum = /^[a-zA-Z0-9_]+$/;
8845     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8846     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8847
8848     // All these messages and functions are configurable
8849     return {
8850         /**
8851          * The function used to validate email addresses
8852          * @param {String} value The email address
8853          */
8854         'email' : function(v){
8855             return email.test(v);
8856         },
8857         /**
8858          * The error text to display when the email validation function returns false
8859          * @type String
8860          */
8861         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8862         /**
8863          * The keystroke filter mask to be applied on email input
8864          * @type RegExp
8865          */
8866         'emailMask' : /[a-z0-9_\.\-@]/i,
8867
8868         /**
8869          * The function used to validate URLs
8870          * @param {String} value The URL
8871          */
8872         'url' : function(v){
8873             return url.test(v);
8874         },
8875         /**
8876          * The error text to display when the url validation function returns false
8877          * @type String
8878          */
8879         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8880         
8881         /**
8882          * The function used to validate alpha values
8883          * @param {String} value The value
8884          */
8885         'alpha' : function(v){
8886             return alpha.test(v);
8887         },
8888         /**
8889          * The error text to display when the alpha validation function returns false
8890          * @type String
8891          */
8892         'alphaText' : 'This field should only contain letters and _',
8893         /**
8894          * The keystroke filter mask to be applied on alpha input
8895          * @type RegExp
8896          */
8897         'alphaMask' : /[a-z_]/i,
8898
8899         /**
8900          * The function used to validate alphanumeric values
8901          * @param {String} value The value
8902          */
8903         'alphanum' : function(v){
8904             return alphanum.test(v);
8905         },
8906         /**
8907          * The error text to display when the alphanumeric validation function returns false
8908          * @type String
8909          */
8910         'alphanumText' : 'This field should only contain letters, numbers and _',
8911         /**
8912          * The keystroke filter mask to be applied on alphanumeric input
8913          * @type RegExp
8914          */
8915         'alphanumMask' : /[a-z0-9_]/i
8916     };
8917 }();/*
8918  * - LGPL
8919  *
8920  * Input
8921  * 
8922  */
8923
8924 /**
8925  * @class Roo.bootstrap.Input
8926  * @extends Roo.bootstrap.Component
8927  * Bootstrap Input class
8928  * @cfg {Boolean} disabled is it disabled
8929  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8930  * @cfg {String} name name of the input
8931  * @cfg {string} fieldLabel - the label associated
8932  * @cfg {string} placeholder - placeholder to put in text.
8933  * @cfg {string}  before - input group add on before
8934  * @cfg {string} after - input group add on after
8935  * @cfg {string} size - (lg|sm) or leave empty..
8936  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8937  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8938  * @cfg {Number} md colspan out of 12 for computer-sized screens
8939  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8940  * @cfg {string} value default value of the input
8941  * @cfg {Number} labelWidth set the width of label 
8942  * @cfg {Number} labellg set the width of label (1-12)
8943  * @cfg {Number} labelmd set the width of label (1-12)
8944  * @cfg {Number} labelsm set the width of label (1-12)
8945  * @cfg {Number} labelxs set the width of label (1-12)
8946  * @cfg {String} labelAlign (top|left)
8947  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8948  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8949  * @cfg {String} indicatorpos (left|right) default left
8950  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8951  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8952
8953  * @cfg {String} align (left|center|right) Default left
8954  * @cfg {Boolean} forceFeedback (true|false) Default false
8955  * 
8956  * @constructor
8957  * Create a new Input
8958  * @param {Object} config The config object
8959  */
8960
8961 Roo.bootstrap.Input = function(config){
8962     
8963     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8964     
8965     this.addEvents({
8966         /**
8967          * @event focus
8968          * Fires when this field receives input focus.
8969          * @param {Roo.form.Field} this
8970          */
8971         focus : true,
8972         /**
8973          * @event blur
8974          * Fires when this field loses input focus.
8975          * @param {Roo.form.Field} this
8976          */
8977         blur : true,
8978         /**
8979          * @event specialkey
8980          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8981          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8982          * @param {Roo.form.Field} this
8983          * @param {Roo.EventObject} e The event object
8984          */
8985         specialkey : true,
8986         /**
8987          * @event change
8988          * Fires just before the field blurs if the field value has changed.
8989          * @param {Roo.form.Field} this
8990          * @param {Mixed} newValue The new value
8991          * @param {Mixed} oldValue The original value
8992          */
8993         change : true,
8994         /**
8995          * @event invalid
8996          * Fires after the field has been marked as invalid.
8997          * @param {Roo.form.Field} this
8998          * @param {String} msg The validation message
8999          */
9000         invalid : true,
9001         /**
9002          * @event valid
9003          * Fires after the field has been validated with no errors.
9004          * @param {Roo.form.Field} this
9005          */
9006         valid : true,
9007          /**
9008          * @event keyup
9009          * Fires after the key up
9010          * @param {Roo.form.Field} this
9011          * @param {Roo.EventObject}  e The event Object
9012          */
9013         keyup : true
9014     });
9015 };
9016
9017 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
9018      /**
9019      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9020       automatic validation (defaults to "keyup").
9021      */
9022     validationEvent : "keyup",
9023      /**
9024      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9025      */
9026     validateOnBlur : true,
9027     /**
9028      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9029      */
9030     validationDelay : 250,
9031      /**
9032      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9033      */
9034     focusClass : "x-form-focus",  // not needed???
9035     
9036        
9037     /**
9038      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9039      */
9040     invalidClass : "has-warning",
9041     
9042     /**
9043      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9044      */
9045     validClass : "has-success",
9046     
9047     /**
9048      * @cfg {Boolean} hasFeedback (true|false) default true
9049      */
9050     hasFeedback : true,
9051     
9052     /**
9053      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9054      */
9055     invalidFeedbackClass : "glyphicon-warning-sign",
9056     
9057     /**
9058      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9059      */
9060     validFeedbackClass : "glyphicon-ok",
9061     
9062     /**
9063      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9064      */
9065     selectOnFocus : false,
9066     
9067      /**
9068      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9069      */
9070     maskRe : null,
9071        /**
9072      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9073      */
9074     vtype : null,
9075     
9076       /**
9077      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9078      */
9079     disableKeyFilter : false,
9080     
9081        /**
9082      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9083      */
9084     disabled : false,
9085      /**
9086      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9087      */
9088     allowBlank : true,
9089     /**
9090      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9091      */
9092     blankText : "Please complete this mandatory field",
9093     
9094      /**
9095      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9096      */
9097     minLength : 0,
9098     /**
9099      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9100      */
9101     maxLength : Number.MAX_VALUE,
9102     /**
9103      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9104      */
9105     minLengthText : "The minimum length for this field is {0}",
9106     /**
9107      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9108      */
9109     maxLengthText : "The maximum length for this field is {0}",
9110   
9111     
9112     /**
9113      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9114      * If available, this function will be called only after the basic validators all return true, and will be passed the
9115      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9116      */
9117     validator : null,
9118     /**
9119      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9120      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9121      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9122      */
9123     regex : null,
9124     /**
9125      * @cfg {String} regexText -- Depricated - use Invalid Text
9126      */
9127     regexText : "",
9128     
9129     /**
9130      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9131      */
9132     invalidText : "",
9133     
9134     
9135     
9136     autocomplete: false,
9137     
9138     
9139     fieldLabel : '',
9140     inputType : 'text',
9141     
9142     name : false,
9143     placeholder: false,
9144     before : false,
9145     after : false,
9146     size : false,
9147     hasFocus : false,
9148     preventMark: false,
9149     isFormField : true,
9150     value : '',
9151     labelWidth : 2,
9152     labelAlign : false,
9153     readOnly : false,
9154     align : false,
9155     formatedValue : false,
9156     forceFeedback : false,
9157     
9158     indicatorpos : 'left',
9159     
9160     labellg : 0,
9161     labelmd : 0,
9162     labelsm : 0,
9163     labelxs : 0,
9164     
9165     capture : '',
9166     accept : '',
9167     
9168     parentLabelAlign : function()
9169     {
9170         var parent = this;
9171         while (parent.parent()) {
9172             parent = parent.parent();
9173             if (typeof(parent.labelAlign) !='undefined') {
9174                 return parent.labelAlign;
9175             }
9176         }
9177         return 'left';
9178         
9179     },
9180     
9181     getAutoCreate : function()
9182     {
9183         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9184         
9185         var id = Roo.id();
9186         
9187         var cfg = {};
9188         
9189         if(this.inputType != 'hidden'){
9190             cfg.cls = 'form-group' //input-group
9191         }
9192         
9193         var input =  {
9194             tag: 'input',
9195             id : id,
9196             type : this.inputType,
9197             value : this.value,
9198             cls : 'form-control',
9199             placeholder : this.placeholder || '',
9200             autocomplete : this.autocomplete || 'new-password'
9201         };
9202         
9203         if(this.capture.length){
9204             input.capture = this.capture;
9205         }
9206         
9207         if(this.accept.length){
9208             input.accept = this.accept + "/*";
9209         }
9210         
9211         if(this.align){
9212             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9213         }
9214         
9215         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9216             input.maxLength = this.maxLength;
9217         }
9218         
9219         if (this.disabled) {
9220             input.disabled=true;
9221         }
9222         
9223         if (this.readOnly) {
9224             input.readonly=true;
9225         }
9226         
9227         if (this.name) {
9228             input.name = this.name;
9229         }
9230         
9231         if (this.size) {
9232             input.cls += ' input-' + this.size;
9233         }
9234         
9235         var settings=this;
9236         ['xs','sm','md','lg'].map(function(size){
9237             if (settings[size]) {
9238                 cfg.cls += ' col-' + size + '-' + settings[size];
9239             }
9240         });
9241         
9242         var inputblock = input;
9243         
9244         var feedback = {
9245             tag: 'span',
9246             cls: 'glyphicon form-control-feedback'
9247         };
9248             
9249         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9250             
9251             inputblock = {
9252                 cls : 'has-feedback',
9253                 cn :  [
9254                     input,
9255                     feedback
9256                 ] 
9257             };  
9258         }
9259         
9260         if (this.before || this.after) {
9261             
9262             inputblock = {
9263                 cls : 'input-group',
9264                 cn :  [] 
9265             };
9266             
9267             if (this.before && typeof(this.before) == 'string') {
9268                 
9269                 inputblock.cn.push({
9270                     tag :'span',
9271                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9272                     html : this.before
9273                 });
9274             }
9275             if (this.before && typeof(this.before) == 'object') {
9276                 this.before = Roo.factory(this.before);
9277                 
9278                 inputblock.cn.push({
9279                     tag :'span',
9280                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9281                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9282                 });
9283             }
9284             
9285             inputblock.cn.push(input);
9286             
9287             if (this.after && typeof(this.after) == 'string') {
9288                 inputblock.cn.push({
9289                     tag :'span',
9290                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9291                     html : this.after
9292                 });
9293             }
9294             if (this.after && typeof(this.after) == 'object') {
9295                 this.after = Roo.factory(this.after);
9296                 
9297                 inputblock.cn.push({
9298                     tag :'span',
9299                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9300                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9301                 });
9302             }
9303             
9304             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9305                 inputblock.cls += ' has-feedback';
9306                 inputblock.cn.push(feedback);
9307             }
9308         };
9309         var indicator = {
9310             tag : 'i',
9311             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9312             tooltip : 'This field is required'
9313         };
9314         if (Roo.bootstrap.version == 4) {
9315             indicator = {
9316                 tag : 'i',
9317                 style : 'display-none'
9318             };
9319         }
9320         if (align ==='left' && this.fieldLabel.length) {
9321             
9322             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9323             
9324             cfg.cn = [
9325                 indicator,
9326                 {
9327                     tag: 'label',
9328                     'for' :  id,
9329                     cls : 'control-label col-form-label',
9330                     html : this.fieldLabel
9331
9332                 },
9333                 {
9334                     cls : "", 
9335                     cn: [
9336                         inputblock
9337                     ]
9338                 }
9339             ];
9340             
9341             var labelCfg = cfg.cn[1];
9342             var contentCfg = cfg.cn[2];
9343             
9344             if(this.indicatorpos == 'right'){
9345                 cfg.cn = [
9346                     {
9347                         tag: 'label',
9348                         'for' :  id,
9349                         cls : 'control-label col-form-label',
9350                         cn : [
9351                             {
9352                                 tag : 'span',
9353                                 html : this.fieldLabel
9354                             },
9355                             indicator
9356                         ]
9357                     },
9358                     {
9359                         cls : "",
9360                         cn: [
9361                             inputblock
9362                         ]
9363                     }
9364
9365                 ];
9366                 
9367                 labelCfg = cfg.cn[0];
9368                 contentCfg = cfg.cn[1];
9369             
9370             }
9371             
9372             if(this.labelWidth > 12){
9373                 labelCfg.style = "width: " + this.labelWidth + 'px';
9374             }
9375             
9376             if(this.labelWidth < 13 && this.labelmd == 0){
9377                 this.labelmd = this.labelWidth;
9378             }
9379             
9380             if(this.labellg > 0){
9381                 labelCfg.cls += ' col-lg-' + this.labellg;
9382                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9383             }
9384             
9385             if(this.labelmd > 0){
9386                 labelCfg.cls += ' col-md-' + this.labelmd;
9387                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9388             }
9389             
9390             if(this.labelsm > 0){
9391                 labelCfg.cls += ' col-sm-' + this.labelsm;
9392                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9393             }
9394             
9395             if(this.labelxs > 0){
9396                 labelCfg.cls += ' col-xs-' + this.labelxs;
9397                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9398             }
9399             
9400             
9401         } else if ( this.fieldLabel.length) {
9402                 
9403             cfg.cn = [
9404                 {
9405                     tag : 'i',
9406                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9407                     tooltip : 'This field is required'
9408                 },
9409                 {
9410                     tag: 'label',
9411                    //cls : 'input-group-addon',
9412                     html : this.fieldLabel
9413
9414                 },
9415
9416                inputblock
9417
9418            ];
9419            
9420            if(this.indicatorpos == 'right'){
9421                 
9422                 cfg.cn = [
9423                     {
9424                         tag: 'label',
9425                        //cls : 'input-group-addon',
9426                         html : this.fieldLabel
9427
9428                     },
9429                     {
9430                         tag : 'i',
9431                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9432                         tooltip : 'This field is required'
9433                     },
9434
9435                    inputblock
9436
9437                ];
9438
9439             }
9440
9441         } else {
9442             
9443             cfg.cn = [
9444
9445                     inputblock
9446
9447             ];
9448                 
9449                 
9450         };
9451         
9452         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9453            cfg.cls += ' navbar-form';
9454         }
9455         
9456         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9457             // on BS4 we do this only if not form 
9458             cfg.cls += ' navbar-form';
9459             cfg.tag = 'li';
9460         }
9461         
9462         return cfg;
9463         
9464     },
9465     /**
9466      * return the real input element.
9467      */
9468     inputEl: function ()
9469     {
9470         return this.el.select('input.form-control',true).first();
9471     },
9472     
9473     tooltipEl : function()
9474     {
9475         return this.inputEl();
9476     },
9477     
9478     indicatorEl : function()
9479     {
9480         if (Roo.bootstrap.version == 4) {
9481             return false; // not enabled in v4 yet.
9482         }
9483         
9484         var indicator = this.el.select('i.roo-required-indicator',true).first();
9485         
9486         if(!indicator){
9487             return false;
9488         }
9489         
9490         return indicator;
9491         
9492     },
9493     
9494     setDisabled : function(v)
9495     {
9496         var i  = this.inputEl().dom;
9497         if (!v) {
9498             i.removeAttribute('disabled');
9499             return;
9500             
9501         }
9502         i.setAttribute('disabled','true');
9503     },
9504     initEvents : function()
9505     {
9506           
9507         this.inputEl().on("keydown" , this.fireKey,  this);
9508         this.inputEl().on("focus", this.onFocus,  this);
9509         this.inputEl().on("blur", this.onBlur,  this);
9510         
9511         this.inputEl().relayEvent('keyup', this);
9512         
9513         this.indicator = this.indicatorEl();
9514         
9515         if(this.indicator){
9516             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9517         }
9518  
9519         // reference to original value for reset
9520         this.originalValue = this.getValue();
9521         //Roo.form.TextField.superclass.initEvents.call(this);
9522         if(this.validationEvent == 'keyup'){
9523             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9524             this.inputEl().on('keyup', this.filterValidation, this);
9525         }
9526         else if(this.validationEvent !== false){
9527             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9528         }
9529         
9530         if(this.selectOnFocus){
9531             this.on("focus", this.preFocus, this);
9532             
9533         }
9534         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9535             this.inputEl().on("keypress", this.filterKeys, this);
9536         } else {
9537             this.inputEl().relayEvent('keypress', this);
9538         }
9539        /* if(this.grow){
9540             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9541             this.el.on("click", this.autoSize,  this);
9542         }
9543         */
9544         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9545             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9546         }
9547         
9548         if (typeof(this.before) == 'object') {
9549             this.before.render(this.el.select('.roo-input-before',true).first());
9550         }
9551         if (typeof(this.after) == 'object') {
9552             this.after.render(this.el.select('.roo-input-after',true).first());
9553         }
9554         
9555         this.inputEl().on('change', this.onChange, this);
9556         
9557     },
9558     filterValidation : function(e){
9559         if(!e.isNavKeyPress()){
9560             this.validationTask.delay(this.validationDelay);
9561         }
9562     },
9563      /**
9564      * Validates the field value
9565      * @return {Boolean} True if the value is valid, else false
9566      */
9567     validate : function(){
9568         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9569         if(this.disabled || this.validateValue(this.getRawValue())){
9570             this.markValid();
9571             return true;
9572         }
9573         
9574         this.markInvalid();
9575         return false;
9576     },
9577     
9578     
9579     /**
9580      * Validates a value according to the field's validation rules and marks the field as invalid
9581      * if the validation fails
9582      * @param {Mixed} value The value to validate
9583      * @return {Boolean} True if the value is valid, else false
9584      */
9585     validateValue : function(value)
9586     {
9587         if(this.getVisibilityEl().hasClass('hidden')){
9588             return true;
9589         }
9590         
9591         if(value.length < 1)  { // if it's blank
9592             if(this.allowBlank){
9593                 return true;
9594             }
9595             return false;
9596         }
9597         
9598         if(value.length < this.minLength){
9599             return false;
9600         }
9601         if(value.length > this.maxLength){
9602             return false;
9603         }
9604         if(this.vtype){
9605             var vt = Roo.form.VTypes;
9606             if(!vt[this.vtype](value, this)){
9607                 return false;
9608             }
9609         }
9610         if(typeof this.validator == "function"){
9611             var msg = this.validator(value);
9612             if(msg !== true){
9613                 return false;
9614             }
9615             if (typeof(msg) == 'string') {
9616                 this.invalidText = msg;
9617             }
9618         }
9619         
9620         if(this.regex && !this.regex.test(value)){
9621             return false;
9622         }
9623         
9624         return true;
9625     },
9626     
9627      // private
9628     fireKey : function(e){
9629         //Roo.log('field ' + e.getKey());
9630         if(e.isNavKeyPress()){
9631             this.fireEvent("specialkey", this, e);
9632         }
9633     },
9634     focus : function (selectText){
9635         if(this.rendered){
9636             this.inputEl().focus();
9637             if(selectText === true){
9638                 this.inputEl().dom.select();
9639             }
9640         }
9641         return this;
9642     } ,
9643     
9644     onFocus : function(){
9645         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9646            // this.el.addClass(this.focusClass);
9647         }
9648         if(!this.hasFocus){
9649             this.hasFocus = true;
9650             this.startValue = this.getValue();
9651             this.fireEvent("focus", this);
9652         }
9653     },
9654     
9655     beforeBlur : Roo.emptyFn,
9656
9657     
9658     // private
9659     onBlur : function(){
9660         this.beforeBlur();
9661         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9662             //this.el.removeClass(this.focusClass);
9663         }
9664         this.hasFocus = false;
9665         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9666             this.validate();
9667         }
9668         var v = this.getValue();
9669         if(String(v) !== String(this.startValue)){
9670             this.fireEvent('change', this, v, this.startValue);
9671         }
9672         this.fireEvent("blur", this);
9673     },
9674     
9675     onChange : function(e)
9676     {
9677         var v = this.getValue();
9678         if(String(v) !== String(this.startValue)){
9679             this.fireEvent('change', this, v, this.startValue);
9680         }
9681         
9682     },
9683     
9684     /**
9685      * Resets the current field value to the originally loaded value and clears any validation messages
9686      */
9687     reset : function(){
9688         this.setValue(this.originalValue);
9689         this.validate();
9690     },
9691      /**
9692      * Returns the name of the field
9693      * @return {Mixed} name The name field
9694      */
9695     getName: function(){
9696         return this.name;
9697     },
9698      /**
9699      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9700      * @return {Mixed} value The field value
9701      */
9702     getValue : function(){
9703         
9704         var v = this.inputEl().getValue();
9705         
9706         return v;
9707     },
9708     /**
9709      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9710      * @return {Mixed} value The field value
9711      */
9712     getRawValue : function(){
9713         var v = this.inputEl().getValue();
9714         
9715         return v;
9716     },
9717     
9718     /**
9719      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9720      * @param {Mixed} value The value to set
9721      */
9722     setRawValue : function(v){
9723         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9724     },
9725     
9726     selectText : function(start, end){
9727         var v = this.getRawValue();
9728         if(v.length > 0){
9729             start = start === undefined ? 0 : start;
9730             end = end === undefined ? v.length : end;
9731             var d = this.inputEl().dom;
9732             if(d.setSelectionRange){
9733                 d.setSelectionRange(start, end);
9734             }else if(d.createTextRange){
9735                 var range = d.createTextRange();
9736                 range.moveStart("character", start);
9737                 range.moveEnd("character", v.length-end);
9738                 range.select();
9739             }
9740         }
9741     },
9742     
9743     /**
9744      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9745      * @param {Mixed} value The value to set
9746      */
9747     setValue : function(v){
9748         this.value = v;
9749         if(this.rendered){
9750             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9751             this.validate();
9752         }
9753     },
9754     
9755     /*
9756     processValue : function(value){
9757         if(this.stripCharsRe){
9758             var newValue = value.replace(this.stripCharsRe, '');
9759             if(newValue !== value){
9760                 this.setRawValue(newValue);
9761                 return newValue;
9762             }
9763         }
9764         return value;
9765     },
9766   */
9767     preFocus : function(){
9768         
9769         if(this.selectOnFocus){
9770             this.inputEl().dom.select();
9771         }
9772     },
9773     filterKeys : function(e){
9774         var k = e.getKey();
9775         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9776             return;
9777         }
9778         var c = e.getCharCode(), cc = String.fromCharCode(c);
9779         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9780             return;
9781         }
9782         if(!this.maskRe.test(cc)){
9783             e.stopEvent();
9784         }
9785     },
9786      /**
9787      * Clear any invalid styles/messages for this field
9788      */
9789     clearInvalid : function(){
9790         
9791         if(!this.el || this.preventMark){ // not rendered
9792             return;
9793         }
9794         
9795         
9796         this.el.removeClass([this.invalidClass, 'is-invalid']);
9797         
9798         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9799             
9800             var feedback = this.el.select('.form-control-feedback', true).first();
9801             
9802             if(feedback){
9803                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9804             }
9805             
9806         }
9807         
9808         if(this.indicator){
9809             this.indicator.removeClass('visible');
9810             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9811         }
9812         
9813         this.fireEvent('valid', this);
9814     },
9815     
9816      /**
9817      * Mark this field as valid
9818      */
9819     markValid : function()
9820     {
9821         if(!this.el  || this.preventMark){ // not rendered...
9822             return;
9823         }
9824         
9825         this.el.removeClass([this.invalidClass, this.validClass]);
9826         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9827
9828         var feedback = this.el.select('.form-control-feedback', true).first();
9829             
9830         if(feedback){
9831             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9832         }
9833         
9834         if(this.indicator){
9835             this.indicator.removeClass('visible');
9836             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9837         }
9838         
9839         if(this.disabled){
9840             return;
9841         }
9842         
9843         if(this.allowBlank && !this.getRawValue().length){
9844             return;
9845         }
9846         if (Roo.bootstrap.version == 3) {
9847             this.el.addClass(this.validClass);
9848         } else {
9849             this.inputEl().addClass('is-valid');
9850         }
9851
9852         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9853             
9854             var feedback = this.el.select('.form-control-feedback', true).first();
9855             
9856             if(feedback){
9857                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9858                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9859             }
9860             
9861         }
9862         
9863         this.fireEvent('valid', this);
9864     },
9865     
9866      /**
9867      * Mark this field as invalid
9868      * @param {String} msg The validation message
9869      */
9870     markInvalid : function(msg)
9871     {
9872         if(!this.el  || this.preventMark){ // not rendered
9873             return;
9874         }
9875         
9876         this.el.removeClass([this.invalidClass, this.validClass]);
9877         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9878         
9879         var feedback = this.el.select('.form-control-feedback', true).first();
9880             
9881         if(feedback){
9882             this.el.select('.form-control-feedback', true).first().removeClass(
9883                     [this.invalidFeedbackClass, this.validFeedbackClass]);
9884         }
9885
9886         if(this.disabled){
9887             return;
9888         }
9889         
9890         if(this.allowBlank && !this.getRawValue().length){
9891             return;
9892         }
9893         
9894         if(this.indicator){
9895             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9896             this.indicator.addClass('visible');
9897         }
9898         if (Roo.bootstrap.version == 3) {
9899             this.el.addClass(this.invalidClass);
9900         } else {
9901             this.inputEl().addClass('is-invalid');
9902         }
9903         
9904         
9905         
9906         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9907             
9908             var feedback = this.el.select('.form-control-feedback', true).first();
9909             
9910             if(feedback){
9911                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9912                 
9913                 if(this.getValue().length || this.forceFeedback){
9914                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9915                 }
9916                 
9917             }
9918             
9919         }
9920         
9921         this.fireEvent('invalid', this, msg);
9922     },
9923     // private
9924     SafariOnKeyDown : function(event)
9925     {
9926         // this is a workaround for a password hang bug on chrome/ webkit.
9927         if (this.inputEl().dom.type != 'password') {
9928             return;
9929         }
9930         
9931         var isSelectAll = false;
9932         
9933         if(this.inputEl().dom.selectionEnd > 0){
9934             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9935         }
9936         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9937             event.preventDefault();
9938             this.setValue('');
9939             return;
9940         }
9941         
9942         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9943             
9944             event.preventDefault();
9945             // this is very hacky as keydown always get's upper case.
9946             //
9947             var cc = String.fromCharCode(event.getCharCode());
9948             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9949             
9950         }
9951     },
9952     adjustWidth : function(tag, w){
9953         tag = tag.toLowerCase();
9954         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9955             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9956                 if(tag == 'input'){
9957                     return w + 2;
9958                 }
9959                 if(tag == 'textarea'){
9960                     return w-2;
9961                 }
9962             }else if(Roo.isOpera){
9963                 if(tag == 'input'){
9964                     return w + 2;
9965                 }
9966                 if(tag == 'textarea'){
9967                     return w-2;
9968                 }
9969             }
9970         }
9971         return w;
9972     },
9973     
9974     setFieldLabel : function(v)
9975     {
9976         if(!this.rendered){
9977             return;
9978         }
9979         
9980         if(this.indicatorEl()){
9981             var ar = this.el.select('label > span',true);
9982             
9983             if (ar.elements.length) {
9984                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9985                 this.fieldLabel = v;
9986                 return;
9987             }
9988             
9989             var br = this.el.select('label',true);
9990             
9991             if(br.elements.length) {
9992                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9993                 this.fieldLabel = v;
9994                 return;
9995             }
9996             
9997             Roo.log('Cannot Found any of label > span || label in input');
9998             return;
9999         }
10000         
10001         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10002         this.fieldLabel = v;
10003         
10004         
10005     }
10006 });
10007
10008  
10009 /*
10010  * - LGPL
10011  *
10012  * Input
10013  * 
10014  */
10015
10016 /**
10017  * @class Roo.bootstrap.TextArea
10018  * @extends Roo.bootstrap.Input
10019  * Bootstrap TextArea class
10020  * @cfg {Number} cols Specifies the visible width of a text area
10021  * @cfg {Number} rows Specifies the visible number of lines in a text area
10022  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10023  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10024  * @cfg {string} html text
10025  * 
10026  * @constructor
10027  * Create a new TextArea
10028  * @param {Object} config The config object
10029  */
10030
10031 Roo.bootstrap.TextArea = function(config){
10032     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10033    
10034 };
10035
10036 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
10037      
10038     cols : false,
10039     rows : 5,
10040     readOnly : false,
10041     warp : 'soft',
10042     resize : false,
10043     value: false,
10044     html: false,
10045     
10046     getAutoCreate : function(){
10047         
10048         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10049         
10050         var id = Roo.id();
10051         
10052         var cfg = {};
10053         
10054         if(this.inputType != 'hidden'){
10055             cfg.cls = 'form-group' //input-group
10056         }
10057         
10058         var input =  {
10059             tag: 'textarea',
10060             id : id,
10061             warp : this.warp,
10062             rows : this.rows,
10063             value : this.value || '',
10064             html: this.html || '',
10065             cls : 'form-control',
10066             placeholder : this.placeholder || '' 
10067             
10068         };
10069         
10070         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10071             input.maxLength = this.maxLength;
10072         }
10073         
10074         if(this.resize){
10075             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10076         }
10077         
10078         if(this.cols){
10079             input.cols = this.cols;
10080         }
10081         
10082         if (this.readOnly) {
10083             input.readonly = true;
10084         }
10085         
10086         if (this.name) {
10087             input.name = this.name;
10088         }
10089         
10090         if (this.size) {
10091             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10092         }
10093         
10094         var settings=this;
10095         ['xs','sm','md','lg'].map(function(size){
10096             if (settings[size]) {
10097                 cfg.cls += ' col-' + size + '-' + settings[size];
10098             }
10099         });
10100         
10101         var inputblock = input;
10102         
10103         if(this.hasFeedback && !this.allowBlank){
10104             
10105             var feedback = {
10106                 tag: 'span',
10107                 cls: 'glyphicon form-control-feedback'
10108             };
10109
10110             inputblock = {
10111                 cls : 'has-feedback',
10112                 cn :  [
10113                     input,
10114                     feedback
10115                 ] 
10116             };  
10117         }
10118         
10119         
10120         if (this.before || this.after) {
10121             
10122             inputblock = {
10123                 cls : 'input-group',
10124                 cn :  [] 
10125             };
10126             if (this.before) {
10127                 inputblock.cn.push({
10128                     tag :'span',
10129                     cls : 'input-group-addon',
10130                     html : this.before
10131                 });
10132             }
10133             
10134             inputblock.cn.push(input);
10135             
10136             if(this.hasFeedback && !this.allowBlank){
10137                 inputblock.cls += ' has-feedback';
10138                 inputblock.cn.push(feedback);
10139             }
10140             
10141             if (this.after) {
10142                 inputblock.cn.push({
10143                     tag :'span',
10144                     cls : 'input-group-addon',
10145                     html : this.after
10146                 });
10147             }
10148             
10149         }
10150         
10151         if (align ==='left' && this.fieldLabel.length) {
10152             cfg.cn = [
10153                 {
10154                     tag: 'label',
10155                     'for' :  id,
10156                     cls : 'control-label',
10157                     html : this.fieldLabel
10158                 },
10159                 {
10160                     cls : "",
10161                     cn: [
10162                         inputblock
10163                     ]
10164                 }
10165
10166             ];
10167             
10168             if(this.labelWidth > 12){
10169                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10170             }
10171
10172             if(this.labelWidth < 13 && this.labelmd == 0){
10173                 this.labelmd = this.labelWidth;
10174             }
10175
10176             if(this.labellg > 0){
10177                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10178                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10179             }
10180
10181             if(this.labelmd > 0){
10182                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10183                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10184             }
10185
10186             if(this.labelsm > 0){
10187                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10188                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10189             }
10190
10191             if(this.labelxs > 0){
10192                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10193                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10194             }
10195             
10196         } else if ( this.fieldLabel.length) {
10197             cfg.cn = [
10198
10199                {
10200                    tag: 'label',
10201                    //cls : 'input-group-addon',
10202                    html : this.fieldLabel
10203
10204                },
10205
10206                inputblock
10207
10208            ];
10209
10210         } else {
10211
10212             cfg.cn = [
10213
10214                 inputblock
10215
10216             ];
10217                 
10218         }
10219         
10220         if (this.disabled) {
10221             input.disabled=true;
10222         }
10223         
10224         return cfg;
10225         
10226     },
10227     /**
10228      * return the real textarea element.
10229      */
10230     inputEl: function ()
10231     {
10232         return this.el.select('textarea.form-control',true).first();
10233     },
10234     
10235     /**
10236      * Clear any invalid styles/messages for this field
10237      */
10238     clearInvalid : function()
10239     {
10240         
10241         if(!this.el || this.preventMark){ // not rendered
10242             return;
10243         }
10244         
10245         var label = this.el.select('label', true).first();
10246         var icon = this.el.select('i.fa-star', true).first();
10247         
10248         if(label && icon){
10249             icon.remove();
10250         }
10251         this.el.removeClass( this.validClass);
10252         this.inputEl().removeClass('is-invalid');
10253          
10254         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10255             
10256             var feedback = this.el.select('.form-control-feedback', true).first();
10257             
10258             if(feedback){
10259                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10260             }
10261             
10262         }
10263         
10264         this.fireEvent('valid', this);
10265     },
10266     
10267      /**
10268      * Mark this field as valid
10269      */
10270     markValid : function()
10271     {
10272         if(!this.el  || this.preventMark){ // not rendered
10273             return;
10274         }
10275         
10276         this.el.removeClass([this.invalidClass, this.validClass]);
10277         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10278         
10279         var feedback = this.el.select('.form-control-feedback', true).first();
10280             
10281         if(feedback){
10282             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10283         }
10284
10285         if(this.disabled || this.allowBlank){
10286             return;
10287         }
10288         
10289         var label = this.el.select('label', true).first();
10290         var icon = this.el.select('i.fa-star', true).first();
10291         
10292         if(label && icon){
10293             icon.remove();
10294         }
10295         if (Roo.bootstrap.version == 3) {
10296             this.el.addClass(this.validClass);
10297         } else {
10298             this.inputEl().addClass('is-valid');
10299         }
10300         
10301         
10302         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10303             
10304             var feedback = this.el.select('.form-control-feedback', true).first();
10305             
10306             if(feedback){
10307                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10308                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10309             }
10310             
10311         }
10312         
10313         this.fireEvent('valid', this);
10314     },
10315     
10316      /**
10317      * Mark this field as invalid
10318      * @param {String} msg The validation message
10319      */
10320     markInvalid : function(msg)
10321     {
10322         if(!this.el  || this.preventMark){ // not rendered
10323             return;
10324         }
10325         
10326         this.el.removeClass([this.invalidClass, this.validClass]);
10327         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10328         
10329         var feedback = this.el.select('.form-control-feedback', true).first();
10330             
10331         if(feedback){
10332             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10333         }
10334
10335         if(this.disabled || this.allowBlank){
10336             return;
10337         }
10338         
10339         var label = this.el.select('label', true).first();
10340         var icon = this.el.select('i.fa-star', true).first();
10341         
10342         if(!this.getValue().length && label && !icon){
10343             this.el.createChild({
10344                 tag : 'i',
10345                 cls : 'text-danger fa fa-lg fa-star',
10346                 tooltip : 'This field is required',
10347                 style : 'margin-right:5px;'
10348             }, label, true);
10349         }
10350         
10351         if (Roo.bootstrap.version == 3) {
10352             this.el.addClass(this.invalidClass);
10353         } else {
10354             this.inputEl().addClass('is-invalid');
10355         }
10356         
10357         // fixme ... this may be depricated need to test..
10358         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10359             
10360             var feedback = this.el.select('.form-control-feedback', true).first();
10361             
10362             if(feedback){
10363                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10364                 
10365                 if(this.getValue().length || this.forceFeedback){
10366                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10367                 }
10368                 
10369             }
10370             
10371         }
10372         
10373         this.fireEvent('invalid', this, msg);
10374     }
10375 });
10376
10377  
10378 /*
10379  * - LGPL
10380  *
10381  * trigger field - base class for combo..
10382  * 
10383  */
10384  
10385 /**
10386  * @class Roo.bootstrap.TriggerField
10387  * @extends Roo.bootstrap.Input
10388  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10389  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10390  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10391  * for which you can provide a custom implementation.  For example:
10392  * <pre><code>
10393 var trigger = new Roo.bootstrap.TriggerField();
10394 trigger.onTriggerClick = myTriggerFn;
10395 trigger.applyTo('my-field');
10396 </code></pre>
10397  *
10398  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10399  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10400  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10401  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10402  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10403
10404  * @constructor
10405  * Create a new TriggerField.
10406  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10407  * to the base TextField)
10408  */
10409 Roo.bootstrap.TriggerField = function(config){
10410     this.mimicing = false;
10411     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10412 };
10413
10414 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10415     /**
10416      * @cfg {String} triggerClass A CSS class to apply to the trigger
10417      */
10418      /**
10419      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10420      */
10421     hideTrigger:false,
10422
10423     /**
10424      * @cfg {Boolean} removable (true|false) special filter default false
10425      */
10426     removable : false,
10427     
10428     /** @cfg {Boolean} grow @hide */
10429     /** @cfg {Number} growMin @hide */
10430     /** @cfg {Number} growMax @hide */
10431
10432     /**
10433      * @hide 
10434      * @method
10435      */
10436     autoSize: Roo.emptyFn,
10437     // private
10438     monitorTab : true,
10439     // private
10440     deferHeight : true,
10441
10442     
10443     actionMode : 'wrap',
10444     
10445     caret : false,
10446     
10447     
10448     getAutoCreate : function(){
10449        
10450         var align = this.labelAlign || this.parentLabelAlign();
10451         
10452         var id = Roo.id();
10453         
10454         var cfg = {
10455             cls: 'form-group' //input-group
10456         };
10457         
10458         
10459         var input =  {
10460             tag: 'input',
10461             id : id,
10462             type : this.inputType,
10463             cls : 'form-control',
10464             autocomplete: 'new-password',
10465             placeholder : this.placeholder || '' 
10466             
10467         };
10468         if (this.name) {
10469             input.name = this.name;
10470         }
10471         if (this.size) {
10472             input.cls += ' input-' + this.size;
10473         }
10474         
10475         if (this.disabled) {
10476             input.disabled=true;
10477         }
10478         
10479         var inputblock = input;
10480         
10481         if(this.hasFeedback && !this.allowBlank){
10482             
10483             var feedback = {
10484                 tag: 'span',
10485                 cls: 'glyphicon form-control-feedback'
10486             };
10487             
10488             if(this.removable && !this.editable && !this.tickable){
10489                 inputblock = {
10490                     cls : 'has-feedback',
10491                     cn :  [
10492                         inputblock,
10493                         {
10494                             tag: 'button',
10495                             html : 'x',
10496                             cls : 'roo-combo-removable-btn close'
10497                         },
10498                         feedback
10499                     ] 
10500                 };
10501             } else {
10502                 inputblock = {
10503                     cls : 'has-feedback',
10504                     cn :  [
10505                         inputblock,
10506                         feedback
10507                     ] 
10508                 };
10509             }
10510
10511         } else {
10512             if(this.removable && !this.editable && !this.tickable){
10513                 inputblock = {
10514                     cls : 'roo-removable',
10515                     cn :  [
10516                         inputblock,
10517                         {
10518                             tag: 'button',
10519                             html : 'x',
10520                             cls : 'roo-combo-removable-btn close'
10521                         }
10522                     ] 
10523                 };
10524             }
10525         }
10526         
10527         if (this.before || this.after) {
10528             
10529             inputblock = {
10530                 cls : 'input-group',
10531                 cn :  [] 
10532             };
10533             if (this.before) {
10534                 inputblock.cn.push({
10535                     tag :'span',
10536                     cls : 'input-group-addon input-group-prepend input-group-text',
10537                     html : this.before
10538                 });
10539             }
10540             
10541             inputblock.cn.push(input);
10542             
10543             if(this.hasFeedback && !this.allowBlank){
10544                 inputblock.cls += ' has-feedback';
10545                 inputblock.cn.push(feedback);
10546             }
10547             
10548             if (this.after) {
10549                 inputblock.cn.push({
10550                     tag :'span',
10551                     cls : 'input-group-addon input-group-append input-group-text',
10552                     html : this.after
10553                 });
10554             }
10555             
10556         };
10557         
10558       
10559         
10560         var ibwrap = inputblock;
10561         
10562         if(this.multiple){
10563             ibwrap = {
10564                 tag: 'ul',
10565                 cls: 'roo-select2-choices',
10566                 cn:[
10567                     {
10568                         tag: 'li',
10569                         cls: 'roo-select2-search-field',
10570                         cn: [
10571
10572                             inputblock
10573                         ]
10574                     }
10575                 ]
10576             };
10577                 
10578         }
10579         
10580         var combobox = {
10581             cls: 'roo-select2-container input-group',
10582             cn: [
10583                  {
10584                     tag: 'input',
10585                     type : 'hidden',
10586                     cls: 'form-hidden-field'
10587                 },
10588                 ibwrap
10589             ]
10590         };
10591         
10592         if(!this.multiple && this.showToggleBtn){
10593             
10594             var caret = {
10595                         tag: 'span',
10596                         cls: 'caret'
10597              };
10598             if (this.caret != false) {
10599                 caret = {
10600                      tag: 'i',
10601                      cls: 'fa fa-' + this.caret
10602                 };
10603                 
10604             }
10605             
10606             combobox.cn.push({
10607                 tag :'span',
10608                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10609                 cn : [
10610                     caret,
10611                     {
10612                         tag: 'span',
10613                         cls: 'combobox-clear',
10614                         cn  : [
10615                             {
10616                                 tag : 'i',
10617                                 cls: 'icon-remove'
10618                             }
10619                         ]
10620                     }
10621                 ]
10622
10623             })
10624         }
10625         
10626         if(this.multiple){
10627             combobox.cls += ' roo-select2-container-multi';
10628         }
10629          var indicator = {
10630             tag : 'i',
10631             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10632             tooltip : 'This field is required'
10633         };
10634         if (Roo.bootstrap.version == 4) {
10635             indicator = {
10636                 tag : 'i',
10637                 style : 'display:none'
10638             };
10639         }
10640         
10641         
10642         if (align ==='left' && this.fieldLabel.length) {
10643             
10644             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10645
10646             cfg.cn = [
10647                 indicator,
10648                 {
10649                     tag: 'label',
10650                     'for' :  id,
10651                     cls : 'control-label',
10652                     html : this.fieldLabel
10653
10654                 },
10655                 {
10656                     cls : "", 
10657                     cn: [
10658                         combobox
10659                     ]
10660                 }
10661
10662             ];
10663             
10664             var labelCfg = cfg.cn[1];
10665             var contentCfg = cfg.cn[2];
10666             
10667             if(this.indicatorpos == 'right'){
10668                 cfg.cn = [
10669                     {
10670                         tag: 'label',
10671                         'for' :  id,
10672                         cls : 'control-label',
10673                         cn : [
10674                             {
10675                                 tag : 'span',
10676                                 html : this.fieldLabel
10677                             },
10678                             indicator
10679                         ]
10680                     },
10681                     {
10682                         cls : "", 
10683                         cn: [
10684                             combobox
10685                         ]
10686                     }
10687
10688                 ];
10689                 
10690                 labelCfg = cfg.cn[0];
10691                 contentCfg = cfg.cn[1];
10692             }
10693             
10694             if(this.labelWidth > 12){
10695                 labelCfg.style = "width: " + this.labelWidth + 'px';
10696             }
10697             
10698             if(this.labelWidth < 13 && this.labelmd == 0){
10699                 this.labelmd = this.labelWidth;
10700             }
10701             
10702             if(this.labellg > 0){
10703                 labelCfg.cls += ' col-lg-' + this.labellg;
10704                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10705             }
10706             
10707             if(this.labelmd > 0){
10708                 labelCfg.cls += ' col-md-' + this.labelmd;
10709                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10710             }
10711             
10712             if(this.labelsm > 0){
10713                 labelCfg.cls += ' col-sm-' + this.labelsm;
10714                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10715             }
10716             
10717             if(this.labelxs > 0){
10718                 labelCfg.cls += ' col-xs-' + this.labelxs;
10719                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10720             }
10721             
10722         } else if ( this.fieldLabel.length) {
10723 //                Roo.log(" label");
10724             cfg.cn = [
10725                 indicator,
10726                {
10727                    tag: 'label',
10728                    //cls : 'input-group-addon',
10729                    html : this.fieldLabel
10730
10731                },
10732
10733                combobox
10734
10735             ];
10736             
10737             if(this.indicatorpos == 'right'){
10738                 
10739                 cfg.cn = [
10740                     {
10741                        tag: 'label',
10742                        cn : [
10743                            {
10744                                tag : 'span',
10745                                html : this.fieldLabel
10746                            },
10747                            indicator
10748                        ]
10749
10750                     },
10751                     combobox
10752
10753                 ];
10754
10755             }
10756
10757         } else {
10758             
10759 //                Roo.log(" no label && no align");
10760                 cfg = combobox
10761                      
10762                 
10763         }
10764         
10765         var settings=this;
10766         ['xs','sm','md','lg'].map(function(size){
10767             if (settings[size]) {
10768                 cfg.cls += ' col-' + size + '-' + settings[size];
10769             }
10770         });
10771         
10772         return cfg;
10773         
10774     },
10775     
10776     
10777     
10778     // private
10779     onResize : function(w, h){
10780 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10781 //        if(typeof w == 'number'){
10782 //            var x = w - this.trigger.getWidth();
10783 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10784 //            this.trigger.setStyle('left', x+'px');
10785 //        }
10786     },
10787
10788     // private
10789     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10790
10791     // private
10792     getResizeEl : function(){
10793         return this.inputEl();
10794     },
10795
10796     // private
10797     getPositionEl : function(){
10798         return this.inputEl();
10799     },
10800
10801     // private
10802     alignErrorIcon : function(){
10803         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10804     },
10805
10806     // private
10807     initEvents : function(){
10808         
10809         this.createList();
10810         
10811         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10812         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10813         if(!this.multiple && this.showToggleBtn){
10814             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10815             if(this.hideTrigger){
10816                 this.trigger.setDisplayed(false);
10817             }
10818             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10819         }
10820         
10821         if(this.multiple){
10822             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10823         }
10824         
10825         if(this.removable && !this.editable && !this.tickable){
10826             var close = this.closeTriggerEl();
10827             
10828             if(close){
10829                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10830                 close.on('click', this.removeBtnClick, this, close);
10831             }
10832         }
10833         
10834         //this.trigger.addClassOnOver('x-form-trigger-over');
10835         //this.trigger.addClassOnClick('x-form-trigger-click');
10836         
10837         //if(!this.width){
10838         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10839         //}
10840     },
10841     
10842     closeTriggerEl : function()
10843     {
10844         var close = this.el.select('.roo-combo-removable-btn', true).first();
10845         return close ? close : false;
10846     },
10847     
10848     removeBtnClick : function(e, h, el)
10849     {
10850         e.preventDefault();
10851         
10852         if(this.fireEvent("remove", this) !== false){
10853             this.reset();
10854             this.fireEvent("afterremove", this)
10855         }
10856     },
10857     
10858     createList : function()
10859     {
10860         this.list = Roo.get(document.body).createChild({
10861             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10862             cls: 'typeahead typeahead-long dropdown-menu',
10863             style: 'display:none'
10864         });
10865         
10866         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10867         
10868     },
10869
10870     // private
10871     initTrigger : function(){
10872        
10873     },
10874
10875     // private
10876     onDestroy : function(){
10877         if(this.trigger){
10878             this.trigger.removeAllListeners();
10879           //  this.trigger.remove();
10880         }
10881         //if(this.wrap){
10882         //    this.wrap.remove();
10883         //}
10884         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10885     },
10886
10887     // private
10888     onFocus : function(){
10889         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10890         /*
10891         if(!this.mimicing){
10892             this.wrap.addClass('x-trigger-wrap-focus');
10893             this.mimicing = true;
10894             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10895             if(this.monitorTab){
10896                 this.el.on("keydown", this.checkTab, this);
10897             }
10898         }
10899         */
10900     },
10901
10902     // private
10903     checkTab : function(e){
10904         if(e.getKey() == e.TAB){
10905             this.triggerBlur();
10906         }
10907     },
10908
10909     // private
10910     onBlur : function(){
10911         // do nothing
10912     },
10913
10914     // private
10915     mimicBlur : function(e, t){
10916         /*
10917         if(!this.wrap.contains(t) && this.validateBlur()){
10918             this.triggerBlur();
10919         }
10920         */
10921     },
10922
10923     // private
10924     triggerBlur : function(){
10925         this.mimicing = false;
10926         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10927         if(this.monitorTab){
10928             this.el.un("keydown", this.checkTab, this);
10929         }
10930         //this.wrap.removeClass('x-trigger-wrap-focus');
10931         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10932     },
10933
10934     // private
10935     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10936     validateBlur : function(e, t){
10937         return true;
10938     },
10939
10940     // private
10941     onDisable : function(){
10942         this.inputEl().dom.disabled = true;
10943         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10944         //if(this.wrap){
10945         //    this.wrap.addClass('x-item-disabled');
10946         //}
10947     },
10948
10949     // private
10950     onEnable : function(){
10951         this.inputEl().dom.disabled = false;
10952         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10953         //if(this.wrap){
10954         //    this.el.removeClass('x-item-disabled');
10955         //}
10956     },
10957
10958     // private
10959     onShow : function(){
10960         var ae = this.getActionEl();
10961         
10962         if(ae){
10963             ae.dom.style.display = '';
10964             ae.dom.style.visibility = 'visible';
10965         }
10966     },
10967
10968     // private
10969     
10970     onHide : function(){
10971         var ae = this.getActionEl();
10972         ae.dom.style.display = 'none';
10973     },
10974
10975     /**
10976      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10977      * by an implementing function.
10978      * @method
10979      * @param {EventObject} e
10980      */
10981     onTriggerClick : Roo.emptyFn
10982 });
10983  /*
10984  * Based on:
10985  * Ext JS Library 1.1.1
10986  * Copyright(c) 2006-2007, Ext JS, LLC.
10987  *
10988  * Originally Released Under LGPL - original licence link has changed is not relivant.
10989  *
10990  * Fork - LGPL
10991  * <script type="text/javascript">
10992  */
10993
10994
10995 /**
10996  * @class Roo.data.SortTypes
10997  * @singleton
10998  * Defines the default sorting (casting?) comparison functions used when sorting data.
10999  */
11000 Roo.data.SortTypes = {
11001     /**
11002      * Default sort that does nothing
11003      * @param {Mixed} s The value being converted
11004      * @return {Mixed} The comparison value
11005      */
11006     none : function(s){
11007         return s;
11008     },
11009     
11010     /**
11011      * The regular expression used to strip tags
11012      * @type {RegExp}
11013      * @property
11014      */
11015     stripTagsRE : /<\/?[^>]+>/gi,
11016     
11017     /**
11018      * Strips all HTML tags to sort on text only
11019      * @param {Mixed} s The value being converted
11020      * @return {String} The comparison value
11021      */
11022     asText : function(s){
11023         return String(s).replace(this.stripTagsRE, "");
11024     },
11025     
11026     /**
11027      * Strips all HTML tags to sort on text only - Case insensitive
11028      * @param {Mixed} s The value being converted
11029      * @return {String} The comparison value
11030      */
11031     asUCText : function(s){
11032         return String(s).toUpperCase().replace(this.stripTagsRE, "");
11033     },
11034     
11035     /**
11036      * Case insensitive string
11037      * @param {Mixed} s The value being converted
11038      * @return {String} The comparison value
11039      */
11040     asUCString : function(s) {
11041         return String(s).toUpperCase();
11042     },
11043     
11044     /**
11045      * Date sorting
11046      * @param {Mixed} s The value being converted
11047      * @return {Number} The comparison value
11048      */
11049     asDate : function(s) {
11050         if(!s){
11051             return 0;
11052         }
11053         if(s instanceof Date){
11054             return s.getTime();
11055         }
11056         return Date.parse(String(s));
11057     },
11058     
11059     /**
11060      * Float sorting
11061      * @param {Mixed} s The value being converted
11062      * @return {Float} The comparison value
11063      */
11064     asFloat : function(s) {
11065         var val = parseFloat(String(s).replace(/,/g, ""));
11066         if(isNaN(val)) {
11067             val = 0;
11068         }
11069         return val;
11070     },
11071     
11072     /**
11073      * Integer sorting
11074      * @param {Mixed} s The value being converted
11075      * @return {Number} The comparison value
11076      */
11077     asInt : function(s) {
11078         var val = parseInt(String(s).replace(/,/g, ""));
11079         if(isNaN(val)) {
11080             val = 0;
11081         }
11082         return val;
11083     }
11084 };/*
11085  * Based on:
11086  * Ext JS Library 1.1.1
11087  * Copyright(c) 2006-2007, Ext JS, LLC.
11088  *
11089  * Originally Released Under LGPL - original licence link has changed is not relivant.
11090  *
11091  * Fork - LGPL
11092  * <script type="text/javascript">
11093  */
11094
11095 /**
11096 * @class Roo.data.Record
11097  * Instances of this class encapsulate both record <em>definition</em> information, and record
11098  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11099  * to access Records cached in an {@link Roo.data.Store} object.<br>
11100  * <p>
11101  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11102  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11103  * objects.<br>
11104  * <p>
11105  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11106  * @constructor
11107  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11108  * {@link #create}. The parameters are the same.
11109  * @param {Array} data An associative Array of data values keyed by the field name.
11110  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11111  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11112  * not specified an integer id is generated.
11113  */
11114 Roo.data.Record = function(data, id){
11115     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11116     this.data = data;
11117 };
11118
11119 /**
11120  * Generate a constructor for a specific record layout.
11121  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11122  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11123  * Each field definition object may contain the following properties: <ul>
11124  * <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,
11125  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11126  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11127  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11128  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11129  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11130  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11131  * this may be omitted.</p></li>
11132  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11133  * <ul><li>auto (Default, implies no conversion)</li>
11134  * <li>string</li>
11135  * <li>int</li>
11136  * <li>float</li>
11137  * <li>boolean</li>
11138  * <li>date</li></ul></p></li>
11139  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11140  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11141  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11142  * by the Reader into an object that will be stored in the Record. It is passed the
11143  * following parameters:<ul>
11144  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11145  * </ul></p></li>
11146  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11147  * </ul>
11148  * <br>usage:<br><pre><code>
11149 var TopicRecord = Roo.data.Record.create(
11150     {name: 'title', mapping: 'topic_title'},
11151     {name: 'author', mapping: 'username'},
11152     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11153     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11154     {name: 'lastPoster', mapping: 'user2'},
11155     {name: 'excerpt', mapping: 'post_text'}
11156 );
11157
11158 var myNewRecord = new TopicRecord({
11159     title: 'Do my job please',
11160     author: 'noobie',
11161     totalPosts: 1,
11162     lastPost: new Date(),
11163     lastPoster: 'Animal',
11164     excerpt: 'No way dude!'
11165 });
11166 myStore.add(myNewRecord);
11167 </code></pre>
11168  * @method create
11169  * @static
11170  */
11171 Roo.data.Record.create = function(o){
11172     var f = function(){
11173         f.superclass.constructor.apply(this, arguments);
11174     };
11175     Roo.extend(f, Roo.data.Record);
11176     var p = f.prototype;
11177     p.fields = new Roo.util.MixedCollection(false, function(field){
11178         return field.name;
11179     });
11180     for(var i = 0, len = o.length; i < len; i++){
11181         p.fields.add(new Roo.data.Field(o[i]));
11182     }
11183     f.getField = function(name){
11184         return p.fields.get(name);  
11185     };
11186     return f;
11187 };
11188
11189 Roo.data.Record.AUTO_ID = 1000;
11190 Roo.data.Record.EDIT = 'edit';
11191 Roo.data.Record.REJECT = 'reject';
11192 Roo.data.Record.COMMIT = 'commit';
11193
11194 Roo.data.Record.prototype = {
11195     /**
11196      * Readonly flag - true if this record has been modified.
11197      * @type Boolean
11198      */
11199     dirty : false,
11200     editing : false,
11201     error: null,
11202     modified: null,
11203
11204     // private
11205     join : function(store){
11206         this.store = store;
11207     },
11208
11209     /**
11210      * Set the named field to the specified value.
11211      * @param {String} name The name of the field to set.
11212      * @param {Object} value The value to set the field to.
11213      */
11214     set : function(name, value){
11215         if(this.data[name] == value){
11216             return;
11217         }
11218         this.dirty = true;
11219         if(!this.modified){
11220             this.modified = {};
11221         }
11222         if(typeof this.modified[name] == 'undefined'){
11223             this.modified[name] = this.data[name];
11224         }
11225         this.data[name] = value;
11226         if(!this.editing && this.store){
11227             this.store.afterEdit(this);
11228         }       
11229     },
11230
11231     /**
11232      * Get the value of the named field.
11233      * @param {String} name The name of the field to get the value of.
11234      * @return {Object} The value of the field.
11235      */
11236     get : function(name){
11237         return this.data[name]; 
11238     },
11239
11240     // private
11241     beginEdit : function(){
11242         this.editing = true;
11243         this.modified = {}; 
11244     },
11245
11246     // private
11247     cancelEdit : function(){
11248         this.editing = false;
11249         delete this.modified;
11250     },
11251
11252     // private
11253     endEdit : function(){
11254         this.editing = false;
11255         if(this.dirty && this.store){
11256             this.store.afterEdit(this);
11257         }
11258     },
11259
11260     /**
11261      * Usually called by the {@link Roo.data.Store} which owns the Record.
11262      * Rejects all changes made to the Record since either creation, or the last commit operation.
11263      * Modified fields are reverted to their original values.
11264      * <p>
11265      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11266      * of reject operations.
11267      */
11268     reject : function(){
11269         var m = this.modified;
11270         for(var n in m){
11271             if(typeof m[n] != "function"){
11272                 this.data[n] = m[n];
11273             }
11274         }
11275         this.dirty = false;
11276         delete this.modified;
11277         this.editing = false;
11278         if(this.store){
11279             this.store.afterReject(this);
11280         }
11281     },
11282
11283     /**
11284      * Usually called by the {@link Roo.data.Store} which owns the Record.
11285      * Commits all changes made to the Record since either creation, or the last commit operation.
11286      * <p>
11287      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11288      * of commit operations.
11289      */
11290     commit : function(){
11291         this.dirty = false;
11292         delete this.modified;
11293         this.editing = false;
11294         if(this.store){
11295             this.store.afterCommit(this);
11296         }
11297     },
11298
11299     // private
11300     hasError : function(){
11301         return this.error != null;
11302     },
11303
11304     // private
11305     clearError : function(){
11306         this.error = null;
11307     },
11308
11309     /**
11310      * Creates a copy of this record.
11311      * @param {String} id (optional) A new record id if you don't want to use this record's id
11312      * @return {Record}
11313      */
11314     copy : function(newId) {
11315         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11316     }
11317 };/*
11318  * Based on:
11319  * Ext JS Library 1.1.1
11320  * Copyright(c) 2006-2007, Ext JS, LLC.
11321  *
11322  * Originally Released Under LGPL - original licence link has changed is not relivant.
11323  *
11324  * Fork - LGPL
11325  * <script type="text/javascript">
11326  */
11327
11328
11329
11330 /**
11331  * @class Roo.data.Store
11332  * @extends Roo.util.Observable
11333  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11334  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11335  * <p>
11336  * 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
11337  * has no knowledge of the format of the data returned by the Proxy.<br>
11338  * <p>
11339  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11340  * instances from the data object. These records are cached and made available through accessor functions.
11341  * @constructor
11342  * Creates a new Store.
11343  * @param {Object} config A config object containing the objects needed for the Store to access data,
11344  * and read the data into Records.
11345  */
11346 Roo.data.Store = function(config){
11347     this.data = new Roo.util.MixedCollection(false);
11348     this.data.getKey = function(o){
11349         return o.id;
11350     };
11351     this.baseParams = {};
11352     // private
11353     this.paramNames = {
11354         "start" : "start",
11355         "limit" : "limit",
11356         "sort" : "sort",
11357         "dir" : "dir",
11358         "multisort" : "_multisort"
11359     };
11360
11361     if(config && config.data){
11362         this.inlineData = config.data;
11363         delete config.data;
11364     }
11365
11366     Roo.apply(this, config);
11367     
11368     if(this.reader){ // reader passed
11369         this.reader = Roo.factory(this.reader, Roo.data);
11370         this.reader.xmodule = this.xmodule || false;
11371         if(!this.recordType){
11372             this.recordType = this.reader.recordType;
11373         }
11374         if(this.reader.onMetaChange){
11375             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11376         }
11377     }
11378
11379     if(this.recordType){
11380         this.fields = this.recordType.prototype.fields;
11381     }
11382     this.modified = [];
11383
11384     this.addEvents({
11385         /**
11386          * @event datachanged
11387          * Fires when the data cache has changed, and a widget which is using this Store
11388          * as a Record cache should refresh its view.
11389          * @param {Store} this
11390          */
11391         datachanged : true,
11392         /**
11393          * @event metachange
11394          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11395          * @param {Store} this
11396          * @param {Object} meta The JSON metadata
11397          */
11398         metachange : true,
11399         /**
11400          * @event add
11401          * Fires when Records have been added to the Store
11402          * @param {Store} this
11403          * @param {Roo.data.Record[]} records The array of Records added
11404          * @param {Number} index The index at which the record(s) were added
11405          */
11406         add : true,
11407         /**
11408          * @event remove
11409          * Fires when a Record has been removed from the Store
11410          * @param {Store} this
11411          * @param {Roo.data.Record} record The Record that was removed
11412          * @param {Number} index The index at which the record was removed
11413          */
11414         remove : true,
11415         /**
11416          * @event update
11417          * Fires when a Record has been updated
11418          * @param {Store} this
11419          * @param {Roo.data.Record} record The Record that was updated
11420          * @param {String} operation The update operation being performed.  Value may be one of:
11421          * <pre><code>
11422  Roo.data.Record.EDIT
11423  Roo.data.Record.REJECT
11424  Roo.data.Record.COMMIT
11425          * </code></pre>
11426          */
11427         update : true,
11428         /**
11429          * @event clear
11430          * Fires when the data cache has been cleared.
11431          * @param {Store} this
11432          */
11433         clear : true,
11434         /**
11435          * @event beforeload
11436          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11437          * the load action will be canceled.
11438          * @param {Store} this
11439          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11440          */
11441         beforeload : true,
11442         /**
11443          * @event beforeloadadd
11444          * Fires after a new set of Records has been loaded.
11445          * @param {Store} this
11446          * @param {Roo.data.Record[]} records The Records that were loaded
11447          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11448          */
11449         beforeloadadd : true,
11450         /**
11451          * @event load
11452          * Fires after a new set of Records has been loaded, before they are added to the store.
11453          * @param {Store} this
11454          * @param {Roo.data.Record[]} records The Records that were loaded
11455          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11456          * @params {Object} return from reader
11457          */
11458         load : true,
11459         /**
11460          * @event loadexception
11461          * Fires if an exception occurs in the Proxy during loading.
11462          * Called with the signature of the Proxy's "loadexception" event.
11463          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11464          * 
11465          * @param {Proxy} 
11466          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11467          * @param {Object} load options 
11468          * @param {Object} jsonData from your request (normally this contains the Exception)
11469          */
11470         loadexception : true
11471     });
11472     
11473     if(this.proxy){
11474         this.proxy = Roo.factory(this.proxy, Roo.data);
11475         this.proxy.xmodule = this.xmodule || false;
11476         this.relayEvents(this.proxy,  ["loadexception"]);
11477     }
11478     this.sortToggle = {};
11479     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11480
11481     Roo.data.Store.superclass.constructor.call(this);
11482
11483     if(this.inlineData){
11484         this.loadData(this.inlineData);
11485         delete this.inlineData;
11486     }
11487 };
11488
11489 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11490      /**
11491     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11492     * without a remote query - used by combo/forms at present.
11493     */
11494     
11495     /**
11496     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11497     */
11498     /**
11499     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11500     */
11501     /**
11502     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11503     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11504     */
11505     /**
11506     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11507     * on any HTTP request
11508     */
11509     /**
11510     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11511     */
11512     /**
11513     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11514     */
11515     multiSort: false,
11516     /**
11517     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11518     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11519     */
11520     remoteSort : false,
11521
11522     /**
11523     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11524      * loaded or when a record is removed. (defaults to false).
11525     */
11526     pruneModifiedRecords : false,
11527
11528     // private
11529     lastOptions : null,
11530
11531     /**
11532      * Add Records to the Store and fires the add event.
11533      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11534      */
11535     add : function(records){
11536         records = [].concat(records);
11537         for(var i = 0, len = records.length; i < len; i++){
11538             records[i].join(this);
11539         }
11540         var index = this.data.length;
11541         this.data.addAll(records);
11542         this.fireEvent("add", this, records, index);
11543     },
11544
11545     /**
11546      * Remove a Record from the Store and fires the remove event.
11547      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11548      */
11549     remove : function(record){
11550         var index = this.data.indexOf(record);
11551         this.data.removeAt(index);
11552  
11553         if(this.pruneModifiedRecords){
11554             this.modified.remove(record);
11555         }
11556         this.fireEvent("remove", this, record, index);
11557     },
11558
11559     /**
11560      * Remove all Records from the Store and fires the clear event.
11561      */
11562     removeAll : function(){
11563         this.data.clear();
11564         if(this.pruneModifiedRecords){
11565             this.modified = [];
11566         }
11567         this.fireEvent("clear", this);
11568     },
11569
11570     /**
11571      * Inserts Records to the Store at the given index and fires the add event.
11572      * @param {Number} index The start index at which to insert the passed Records.
11573      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11574      */
11575     insert : function(index, records){
11576         records = [].concat(records);
11577         for(var i = 0, len = records.length; i < len; i++){
11578             this.data.insert(index, records[i]);
11579             records[i].join(this);
11580         }
11581         this.fireEvent("add", this, records, index);
11582     },
11583
11584     /**
11585      * Get the index within the cache of the passed Record.
11586      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11587      * @return {Number} The index of the passed Record. Returns -1 if not found.
11588      */
11589     indexOf : function(record){
11590         return this.data.indexOf(record);
11591     },
11592
11593     /**
11594      * Get the index within the cache of the Record with the passed id.
11595      * @param {String} id The id of the Record to find.
11596      * @return {Number} The index of the Record. Returns -1 if not found.
11597      */
11598     indexOfId : function(id){
11599         return this.data.indexOfKey(id);
11600     },
11601
11602     /**
11603      * Get the Record with the specified id.
11604      * @param {String} id The id of the Record to find.
11605      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11606      */
11607     getById : function(id){
11608         return this.data.key(id);
11609     },
11610
11611     /**
11612      * Get the Record at the specified index.
11613      * @param {Number} index The index of the Record to find.
11614      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11615      */
11616     getAt : function(index){
11617         return this.data.itemAt(index);
11618     },
11619
11620     /**
11621      * Returns a range of Records between specified indices.
11622      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11623      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11624      * @return {Roo.data.Record[]} An array of Records
11625      */
11626     getRange : function(start, end){
11627         return this.data.getRange(start, end);
11628     },
11629
11630     // private
11631     storeOptions : function(o){
11632         o = Roo.apply({}, o);
11633         delete o.callback;
11634         delete o.scope;
11635         this.lastOptions = o;
11636     },
11637
11638     /**
11639      * Loads the Record cache from the configured Proxy using the configured Reader.
11640      * <p>
11641      * If using remote paging, then the first load call must specify the <em>start</em>
11642      * and <em>limit</em> properties in the options.params property to establish the initial
11643      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11644      * <p>
11645      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11646      * and this call will return before the new data has been loaded. Perform any post-processing
11647      * in a callback function, or in a "load" event handler.</strong>
11648      * <p>
11649      * @param {Object} options An object containing properties which control loading options:<ul>
11650      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11651      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11652      * passed the following arguments:<ul>
11653      * <li>r : Roo.data.Record[]</li>
11654      * <li>options: Options object from the load call</li>
11655      * <li>success: Boolean success indicator</li></ul></li>
11656      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11657      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11658      * </ul>
11659      */
11660     load : function(options){
11661         options = options || {};
11662         if(this.fireEvent("beforeload", this, options) !== false){
11663             this.storeOptions(options);
11664             var p = Roo.apply(options.params || {}, this.baseParams);
11665             // if meta was not loaded from remote source.. try requesting it.
11666             if (!this.reader.metaFromRemote) {
11667                 p._requestMeta = 1;
11668             }
11669             if(this.sortInfo && this.remoteSort){
11670                 var pn = this.paramNames;
11671                 p[pn["sort"]] = this.sortInfo.field;
11672                 p[pn["dir"]] = this.sortInfo.direction;
11673             }
11674             if (this.multiSort) {
11675                 var pn = this.paramNames;
11676                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11677             }
11678             
11679             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11680         }
11681     },
11682
11683     /**
11684      * Reloads the Record cache from the configured Proxy using the configured Reader and
11685      * the options from the last load operation performed.
11686      * @param {Object} options (optional) An object containing properties which may override the options
11687      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11688      * the most recently used options are reused).
11689      */
11690     reload : function(options){
11691         this.load(Roo.applyIf(options||{}, this.lastOptions));
11692     },
11693
11694     // private
11695     // Called as a callback by the Reader during a load operation.
11696     loadRecords : function(o, options, success){
11697         if(!o || success === false){
11698             if(success !== false){
11699                 this.fireEvent("load", this, [], options, o);
11700             }
11701             if(options.callback){
11702                 options.callback.call(options.scope || this, [], options, false);
11703             }
11704             return;
11705         }
11706         // if data returned failure - throw an exception.
11707         if (o.success === false) {
11708             // show a message if no listener is registered.
11709             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11710                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11711             }
11712             // loadmask wil be hooked into this..
11713             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11714             return;
11715         }
11716         var r = o.records, t = o.totalRecords || r.length;
11717         
11718         this.fireEvent("beforeloadadd", this, r, options, o);
11719         
11720         if(!options || options.add !== true){
11721             if(this.pruneModifiedRecords){
11722                 this.modified = [];
11723             }
11724             for(var i = 0, len = r.length; i < len; i++){
11725                 r[i].join(this);
11726             }
11727             if(this.snapshot){
11728                 this.data = this.snapshot;
11729                 delete this.snapshot;
11730             }
11731             this.data.clear();
11732             this.data.addAll(r);
11733             this.totalLength = t;
11734             this.applySort();
11735             this.fireEvent("datachanged", this);
11736         }else{
11737             this.totalLength = Math.max(t, this.data.length+r.length);
11738             this.add(r);
11739         }
11740         
11741         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11742                 
11743             var e = new Roo.data.Record({});
11744
11745             e.set(this.parent.displayField, this.parent.emptyTitle);
11746             e.set(this.parent.valueField, '');
11747
11748             this.insert(0, e);
11749         }
11750             
11751         this.fireEvent("load", this, r, options, o);
11752         if(options.callback){
11753             options.callback.call(options.scope || this, r, options, true);
11754         }
11755     },
11756
11757
11758     /**
11759      * Loads data from a passed data block. A Reader which understands the format of the data
11760      * must have been configured in the constructor.
11761      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11762      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11763      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11764      */
11765     loadData : function(o, append){
11766         var r = this.reader.readRecords(o);
11767         this.loadRecords(r, {add: append}, true);
11768     },
11769
11770     /**
11771      * Gets the number of cached records.
11772      * <p>
11773      * <em>If using paging, this may not be the total size of the dataset. If the data object
11774      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11775      * the data set size</em>
11776      */
11777     getCount : function(){
11778         return this.data.length || 0;
11779     },
11780
11781     /**
11782      * Gets the total number of records in the dataset as returned by the server.
11783      * <p>
11784      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11785      * the dataset size</em>
11786      */
11787     getTotalCount : function(){
11788         return this.totalLength || 0;
11789     },
11790
11791     /**
11792      * Returns the sort state of the Store as an object with two properties:
11793      * <pre><code>
11794  field {String} The name of the field by which the Records are sorted
11795  direction {String} The sort order, "ASC" or "DESC"
11796      * </code></pre>
11797      */
11798     getSortState : function(){
11799         return this.sortInfo;
11800     },
11801
11802     // private
11803     applySort : function(){
11804         if(this.sortInfo && !this.remoteSort){
11805             var s = this.sortInfo, f = s.field;
11806             var st = this.fields.get(f).sortType;
11807             var fn = function(r1, r2){
11808                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11809                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11810             };
11811             this.data.sort(s.direction, fn);
11812             if(this.snapshot && this.snapshot != this.data){
11813                 this.snapshot.sort(s.direction, fn);
11814             }
11815         }
11816     },
11817
11818     /**
11819      * Sets the default sort column and order to be used by the next load operation.
11820      * @param {String} fieldName The name of the field to sort by.
11821      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11822      */
11823     setDefaultSort : function(field, dir){
11824         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11825     },
11826
11827     /**
11828      * Sort the Records.
11829      * If remote sorting is used, the sort is performed on the server, and the cache is
11830      * reloaded. If local sorting is used, the cache is sorted internally.
11831      * @param {String} fieldName The name of the field to sort by.
11832      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11833      */
11834     sort : function(fieldName, dir){
11835         var f = this.fields.get(fieldName);
11836         if(!dir){
11837             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11838             
11839             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11840                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11841             }else{
11842                 dir = f.sortDir;
11843             }
11844         }
11845         this.sortToggle[f.name] = dir;
11846         this.sortInfo = {field: f.name, direction: dir};
11847         if(!this.remoteSort){
11848             this.applySort();
11849             this.fireEvent("datachanged", this);
11850         }else{
11851             this.load(this.lastOptions);
11852         }
11853     },
11854
11855     /**
11856      * Calls the specified function for each of the Records in the cache.
11857      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11858      * Returning <em>false</em> aborts and exits the iteration.
11859      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11860      */
11861     each : function(fn, scope){
11862         this.data.each(fn, scope);
11863     },
11864
11865     /**
11866      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11867      * (e.g., during paging).
11868      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11869      */
11870     getModifiedRecords : function(){
11871         return this.modified;
11872     },
11873
11874     // private
11875     createFilterFn : function(property, value, anyMatch){
11876         if(!value.exec){ // not a regex
11877             value = String(value);
11878             if(value.length == 0){
11879                 return false;
11880             }
11881             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11882         }
11883         return function(r){
11884             return value.test(r.data[property]);
11885         };
11886     },
11887
11888     /**
11889      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11890      * @param {String} property A field on your records
11891      * @param {Number} start The record index to start at (defaults to 0)
11892      * @param {Number} end The last record index to include (defaults to length - 1)
11893      * @return {Number} The sum
11894      */
11895     sum : function(property, start, end){
11896         var rs = this.data.items, v = 0;
11897         start = start || 0;
11898         end = (end || end === 0) ? end : rs.length-1;
11899
11900         for(var i = start; i <= end; i++){
11901             v += (rs[i].data[property] || 0);
11902         }
11903         return v;
11904     },
11905
11906     /**
11907      * Filter the records by a specified property.
11908      * @param {String} field A field on your records
11909      * @param {String/RegExp} value Either a string that the field
11910      * should start with or a RegExp to test against the field
11911      * @param {Boolean} anyMatch True to match any part not just the beginning
11912      */
11913     filter : function(property, value, anyMatch){
11914         var fn = this.createFilterFn(property, value, anyMatch);
11915         return fn ? this.filterBy(fn) : this.clearFilter();
11916     },
11917
11918     /**
11919      * Filter by a function. The specified function will be called with each
11920      * record in this data source. If the function returns true the record is included,
11921      * otherwise it is filtered.
11922      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11923      * @param {Object} scope (optional) The scope of the function (defaults to this)
11924      */
11925     filterBy : function(fn, scope){
11926         this.snapshot = this.snapshot || this.data;
11927         this.data = this.queryBy(fn, scope||this);
11928         this.fireEvent("datachanged", this);
11929     },
11930
11931     /**
11932      * Query the records by a specified property.
11933      * @param {String} field A field on your records
11934      * @param {String/RegExp} value Either a string that the field
11935      * should start with or a RegExp to test against the field
11936      * @param {Boolean} anyMatch True to match any part not just the beginning
11937      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11938      */
11939     query : function(property, value, anyMatch){
11940         var fn = this.createFilterFn(property, value, anyMatch);
11941         return fn ? this.queryBy(fn) : this.data.clone();
11942     },
11943
11944     /**
11945      * Query by a function. The specified function will be called with each
11946      * record in this data source. If the function returns true the record is included
11947      * in the results.
11948      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11949      * @param {Object} scope (optional) The scope of the function (defaults to this)
11950       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11951      **/
11952     queryBy : function(fn, scope){
11953         var data = this.snapshot || this.data;
11954         return data.filterBy(fn, scope||this);
11955     },
11956
11957     /**
11958      * Collects unique values for a particular dataIndex from this store.
11959      * @param {String} dataIndex The property to collect
11960      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11961      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11962      * @return {Array} An array of the unique values
11963      **/
11964     collect : function(dataIndex, allowNull, bypassFilter){
11965         var d = (bypassFilter === true && this.snapshot) ?
11966                 this.snapshot.items : this.data.items;
11967         var v, sv, r = [], l = {};
11968         for(var i = 0, len = d.length; i < len; i++){
11969             v = d[i].data[dataIndex];
11970             sv = String(v);
11971             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11972                 l[sv] = true;
11973                 r[r.length] = v;
11974             }
11975         }
11976         return r;
11977     },
11978
11979     /**
11980      * Revert to a view of the Record cache with no filtering applied.
11981      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11982      */
11983     clearFilter : function(suppressEvent){
11984         if(this.snapshot && this.snapshot != this.data){
11985             this.data = this.snapshot;
11986             delete this.snapshot;
11987             if(suppressEvent !== true){
11988                 this.fireEvent("datachanged", this);
11989             }
11990         }
11991     },
11992
11993     // private
11994     afterEdit : function(record){
11995         if(this.modified.indexOf(record) == -1){
11996             this.modified.push(record);
11997         }
11998         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11999     },
12000     
12001     // private
12002     afterReject : function(record){
12003         this.modified.remove(record);
12004         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12005     },
12006
12007     // private
12008     afterCommit : function(record){
12009         this.modified.remove(record);
12010         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12011     },
12012
12013     /**
12014      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12015      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12016      */
12017     commitChanges : function(){
12018         var m = this.modified.slice(0);
12019         this.modified = [];
12020         for(var i = 0, len = m.length; i < len; i++){
12021             m[i].commit();
12022         }
12023     },
12024
12025     /**
12026      * Cancel outstanding changes on all changed records.
12027      */
12028     rejectChanges : function(){
12029         var m = this.modified.slice(0);
12030         this.modified = [];
12031         for(var i = 0, len = m.length; i < len; i++){
12032             m[i].reject();
12033         }
12034     },
12035
12036     onMetaChange : function(meta, rtype, o){
12037         this.recordType = rtype;
12038         this.fields = rtype.prototype.fields;
12039         delete this.snapshot;
12040         this.sortInfo = meta.sortInfo || this.sortInfo;
12041         this.modified = [];
12042         this.fireEvent('metachange', this, this.reader.meta);
12043     },
12044     
12045     moveIndex : function(data, type)
12046     {
12047         var index = this.indexOf(data);
12048         
12049         var newIndex = index + type;
12050         
12051         this.remove(data);
12052         
12053         this.insert(newIndex, data);
12054         
12055     }
12056 });/*
12057  * Based on:
12058  * Ext JS Library 1.1.1
12059  * Copyright(c) 2006-2007, Ext JS, LLC.
12060  *
12061  * Originally Released Under LGPL - original licence link has changed is not relivant.
12062  *
12063  * Fork - LGPL
12064  * <script type="text/javascript">
12065  */
12066
12067 /**
12068  * @class Roo.data.SimpleStore
12069  * @extends Roo.data.Store
12070  * Small helper class to make creating Stores from Array data easier.
12071  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12072  * @cfg {Array} fields An array of field definition objects, or field name strings.
12073  * @cfg {Array} data The multi-dimensional array of data
12074  * @constructor
12075  * @param {Object} config
12076  */
12077 Roo.data.SimpleStore = function(config){
12078     Roo.data.SimpleStore.superclass.constructor.call(this, {
12079         isLocal : true,
12080         reader: new Roo.data.ArrayReader({
12081                 id: config.id
12082             },
12083             Roo.data.Record.create(config.fields)
12084         ),
12085         proxy : new Roo.data.MemoryProxy(config.data)
12086     });
12087     this.load();
12088 };
12089 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12090  * Based on:
12091  * Ext JS Library 1.1.1
12092  * Copyright(c) 2006-2007, Ext JS, LLC.
12093  *
12094  * Originally Released Under LGPL - original licence link has changed is not relivant.
12095  *
12096  * Fork - LGPL
12097  * <script type="text/javascript">
12098  */
12099
12100 /**
12101 /**
12102  * @extends Roo.data.Store
12103  * @class Roo.data.JsonStore
12104  * Small helper class to make creating Stores for JSON data easier. <br/>
12105 <pre><code>
12106 var store = new Roo.data.JsonStore({
12107     url: 'get-images.php',
12108     root: 'images',
12109     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12110 });
12111 </code></pre>
12112  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12113  * JsonReader and HttpProxy (unless inline data is provided).</b>
12114  * @cfg {Array} fields An array of field definition objects, or field name strings.
12115  * @constructor
12116  * @param {Object} config
12117  */
12118 Roo.data.JsonStore = function(c){
12119     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12120         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12121         reader: new Roo.data.JsonReader(c, c.fields)
12122     }));
12123 };
12124 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12125  * Based on:
12126  * Ext JS Library 1.1.1
12127  * Copyright(c) 2006-2007, Ext JS, LLC.
12128  *
12129  * Originally Released Under LGPL - original licence link has changed is not relivant.
12130  *
12131  * Fork - LGPL
12132  * <script type="text/javascript">
12133  */
12134
12135  
12136 Roo.data.Field = function(config){
12137     if(typeof config == "string"){
12138         config = {name: config};
12139     }
12140     Roo.apply(this, config);
12141     
12142     if(!this.type){
12143         this.type = "auto";
12144     }
12145     
12146     var st = Roo.data.SortTypes;
12147     // named sortTypes are supported, here we look them up
12148     if(typeof this.sortType == "string"){
12149         this.sortType = st[this.sortType];
12150     }
12151     
12152     // set default sortType for strings and dates
12153     if(!this.sortType){
12154         switch(this.type){
12155             case "string":
12156                 this.sortType = st.asUCString;
12157                 break;
12158             case "date":
12159                 this.sortType = st.asDate;
12160                 break;
12161             default:
12162                 this.sortType = st.none;
12163         }
12164     }
12165
12166     // define once
12167     var stripRe = /[\$,%]/g;
12168
12169     // prebuilt conversion function for this field, instead of
12170     // switching every time we're reading a value
12171     if(!this.convert){
12172         var cv, dateFormat = this.dateFormat;
12173         switch(this.type){
12174             case "":
12175             case "auto":
12176             case undefined:
12177                 cv = function(v){ return v; };
12178                 break;
12179             case "string":
12180                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12181                 break;
12182             case "int":
12183                 cv = function(v){
12184                     return v !== undefined && v !== null && v !== '' ?
12185                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12186                     };
12187                 break;
12188             case "float":
12189                 cv = function(v){
12190                     return v !== undefined && v !== null && v !== '' ?
12191                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12192                     };
12193                 break;
12194             case "bool":
12195             case "boolean":
12196                 cv = function(v){ return v === true || v === "true" || v == 1; };
12197                 break;
12198             case "date":
12199                 cv = function(v){
12200                     if(!v){
12201                         return '';
12202                     }
12203                     if(v instanceof Date){
12204                         return v;
12205                     }
12206                     if(dateFormat){
12207                         if(dateFormat == "timestamp"){
12208                             return new Date(v*1000);
12209                         }
12210                         return Date.parseDate(v, dateFormat);
12211                     }
12212                     var parsed = Date.parse(v);
12213                     return parsed ? new Date(parsed) : null;
12214                 };
12215              break;
12216             
12217         }
12218         this.convert = cv;
12219     }
12220 };
12221
12222 Roo.data.Field.prototype = {
12223     dateFormat: null,
12224     defaultValue: "",
12225     mapping: null,
12226     sortType : null,
12227     sortDir : "ASC"
12228 };/*
12229  * Based on:
12230  * Ext JS Library 1.1.1
12231  * Copyright(c) 2006-2007, Ext JS, LLC.
12232  *
12233  * Originally Released Under LGPL - original licence link has changed is not relivant.
12234  *
12235  * Fork - LGPL
12236  * <script type="text/javascript">
12237  */
12238  
12239 // Base class for reading structured data from a data source.  This class is intended to be
12240 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12241
12242 /**
12243  * @class Roo.data.DataReader
12244  * Base class for reading structured data from a data source.  This class is intended to be
12245  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12246  */
12247
12248 Roo.data.DataReader = function(meta, recordType){
12249     
12250     this.meta = meta;
12251     
12252     this.recordType = recordType instanceof Array ? 
12253         Roo.data.Record.create(recordType) : recordType;
12254 };
12255
12256 Roo.data.DataReader.prototype = {
12257      /**
12258      * Create an empty record
12259      * @param {Object} data (optional) - overlay some values
12260      * @return {Roo.data.Record} record created.
12261      */
12262     newRow :  function(d) {
12263         var da =  {};
12264         this.recordType.prototype.fields.each(function(c) {
12265             switch( c.type) {
12266                 case 'int' : da[c.name] = 0; break;
12267                 case 'date' : da[c.name] = new Date(); break;
12268                 case 'float' : da[c.name] = 0.0; break;
12269                 case 'boolean' : da[c.name] = false; break;
12270                 default : da[c.name] = ""; break;
12271             }
12272             
12273         });
12274         return new this.recordType(Roo.apply(da, d));
12275     }
12276     
12277 };/*
12278  * Based on:
12279  * Ext JS Library 1.1.1
12280  * Copyright(c) 2006-2007, Ext JS, LLC.
12281  *
12282  * Originally Released Under LGPL - original licence link has changed is not relivant.
12283  *
12284  * Fork - LGPL
12285  * <script type="text/javascript">
12286  */
12287
12288 /**
12289  * @class Roo.data.DataProxy
12290  * @extends Roo.data.Observable
12291  * This class is an abstract base class for implementations which provide retrieval of
12292  * unformatted data objects.<br>
12293  * <p>
12294  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12295  * (of the appropriate type which knows how to parse the data object) to provide a block of
12296  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12297  * <p>
12298  * Custom implementations must implement the load method as described in
12299  * {@link Roo.data.HttpProxy#load}.
12300  */
12301 Roo.data.DataProxy = function(){
12302     this.addEvents({
12303         /**
12304          * @event beforeload
12305          * Fires before a network request is made to retrieve a data object.
12306          * @param {Object} This DataProxy object.
12307          * @param {Object} params The params parameter to the load function.
12308          */
12309         beforeload : true,
12310         /**
12311          * @event load
12312          * Fires before the load method's callback is called.
12313          * @param {Object} This DataProxy object.
12314          * @param {Object} o The data object.
12315          * @param {Object} arg The callback argument object passed to the load function.
12316          */
12317         load : true,
12318         /**
12319          * @event loadexception
12320          * Fires if an Exception occurs during data retrieval.
12321          * @param {Object} This DataProxy object.
12322          * @param {Object} o The data object.
12323          * @param {Object} arg The callback argument object passed to the load function.
12324          * @param {Object} e The Exception.
12325          */
12326         loadexception : true
12327     });
12328     Roo.data.DataProxy.superclass.constructor.call(this);
12329 };
12330
12331 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12332
12333     /**
12334      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12335      */
12336 /*
12337  * Based on:
12338  * Ext JS Library 1.1.1
12339  * Copyright(c) 2006-2007, Ext JS, LLC.
12340  *
12341  * Originally Released Under LGPL - original licence link has changed is not relivant.
12342  *
12343  * Fork - LGPL
12344  * <script type="text/javascript">
12345  */
12346 /**
12347  * @class Roo.data.MemoryProxy
12348  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12349  * to the Reader when its load method is called.
12350  * @constructor
12351  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12352  */
12353 Roo.data.MemoryProxy = function(data){
12354     if (data.data) {
12355         data = data.data;
12356     }
12357     Roo.data.MemoryProxy.superclass.constructor.call(this);
12358     this.data = data;
12359 };
12360
12361 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12362     
12363     /**
12364      * Load data from the requested source (in this case an in-memory
12365      * data object passed to the constructor), read the data object into
12366      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12367      * process that block using the passed callback.
12368      * @param {Object} params This parameter is not used by the MemoryProxy class.
12369      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12370      * object into a block of Roo.data.Records.
12371      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12372      * The function must be passed <ul>
12373      * <li>The Record block object</li>
12374      * <li>The "arg" argument from the load function</li>
12375      * <li>A boolean success indicator</li>
12376      * </ul>
12377      * @param {Object} scope The scope in which to call the callback
12378      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12379      */
12380     load : function(params, reader, callback, scope, arg){
12381         params = params || {};
12382         var result;
12383         try {
12384             result = reader.readRecords(this.data);
12385         }catch(e){
12386             this.fireEvent("loadexception", this, arg, null, e);
12387             callback.call(scope, null, arg, false);
12388             return;
12389         }
12390         callback.call(scope, result, arg, true);
12391     },
12392     
12393     // private
12394     update : function(params, records){
12395         
12396     }
12397 });/*
12398  * Based on:
12399  * Ext JS Library 1.1.1
12400  * Copyright(c) 2006-2007, Ext JS, LLC.
12401  *
12402  * Originally Released Under LGPL - original licence link has changed is not relivant.
12403  *
12404  * Fork - LGPL
12405  * <script type="text/javascript">
12406  */
12407 /**
12408  * @class Roo.data.HttpProxy
12409  * @extends Roo.data.DataProxy
12410  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12411  * configured to reference a certain URL.<br><br>
12412  * <p>
12413  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12414  * from which the running page was served.<br><br>
12415  * <p>
12416  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12417  * <p>
12418  * Be aware that to enable the browser to parse an XML document, the server must set
12419  * the Content-Type header in the HTTP response to "text/xml".
12420  * @constructor
12421  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12422  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12423  * will be used to make the request.
12424  */
12425 Roo.data.HttpProxy = function(conn){
12426     Roo.data.HttpProxy.superclass.constructor.call(this);
12427     // is conn a conn config or a real conn?
12428     this.conn = conn;
12429     this.useAjax = !conn || !conn.events;
12430   
12431 };
12432
12433 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12434     // thse are take from connection...
12435     
12436     /**
12437      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12438      */
12439     /**
12440      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12441      * extra parameters to each request made by this object. (defaults to undefined)
12442      */
12443     /**
12444      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12445      *  to each request made by this object. (defaults to undefined)
12446      */
12447     /**
12448      * @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)
12449      */
12450     /**
12451      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12452      */
12453      /**
12454      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12455      * @type Boolean
12456      */
12457   
12458
12459     /**
12460      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12461      * @type Boolean
12462      */
12463     /**
12464      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12465      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12466      * a finer-grained basis than the DataProxy events.
12467      */
12468     getConnection : function(){
12469         return this.useAjax ? Roo.Ajax : this.conn;
12470     },
12471
12472     /**
12473      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12474      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12475      * process that block using the passed callback.
12476      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12477      * for the request to the remote server.
12478      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12479      * object into a block of Roo.data.Records.
12480      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12481      * The function must be passed <ul>
12482      * <li>The Record block object</li>
12483      * <li>The "arg" argument from the load function</li>
12484      * <li>A boolean success indicator</li>
12485      * </ul>
12486      * @param {Object} scope The scope in which to call the callback
12487      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12488      */
12489     load : function(params, reader, callback, scope, arg){
12490         if(this.fireEvent("beforeload", this, params) !== false){
12491             var  o = {
12492                 params : params || {},
12493                 request: {
12494                     callback : callback,
12495                     scope : scope,
12496                     arg : arg
12497                 },
12498                 reader: reader,
12499                 callback : this.loadResponse,
12500                 scope: this
12501             };
12502             if(this.useAjax){
12503                 Roo.applyIf(o, this.conn);
12504                 if(this.activeRequest){
12505                     Roo.Ajax.abort(this.activeRequest);
12506                 }
12507                 this.activeRequest = Roo.Ajax.request(o);
12508             }else{
12509                 this.conn.request(o);
12510             }
12511         }else{
12512             callback.call(scope||this, null, arg, false);
12513         }
12514     },
12515
12516     // private
12517     loadResponse : function(o, success, response){
12518         delete this.activeRequest;
12519         if(!success){
12520             this.fireEvent("loadexception", this, o, response);
12521             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12522             return;
12523         }
12524         var result;
12525         try {
12526             result = o.reader.read(response);
12527         }catch(e){
12528             this.fireEvent("loadexception", this, o, response, e);
12529             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12530             return;
12531         }
12532         
12533         this.fireEvent("load", this, o, o.request.arg);
12534         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12535     },
12536
12537     // private
12538     update : function(dataSet){
12539
12540     },
12541
12542     // private
12543     updateResponse : function(dataSet){
12544
12545     }
12546 });/*
12547  * Based on:
12548  * Ext JS Library 1.1.1
12549  * Copyright(c) 2006-2007, Ext JS, LLC.
12550  *
12551  * Originally Released Under LGPL - original licence link has changed is not relivant.
12552  *
12553  * Fork - LGPL
12554  * <script type="text/javascript">
12555  */
12556
12557 /**
12558  * @class Roo.data.ScriptTagProxy
12559  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12560  * other than the originating domain of the running page.<br><br>
12561  * <p>
12562  * <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
12563  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12564  * <p>
12565  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12566  * source code that is used as the source inside a &lt;script> tag.<br><br>
12567  * <p>
12568  * In order for the browser to process the returned data, the server must wrap the data object
12569  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12570  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12571  * depending on whether the callback name was passed:
12572  * <p>
12573  * <pre><code>
12574 boolean scriptTag = false;
12575 String cb = request.getParameter("callback");
12576 if (cb != null) {
12577     scriptTag = true;
12578     response.setContentType("text/javascript");
12579 } else {
12580     response.setContentType("application/x-json");
12581 }
12582 Writer out = response.getWriter();
12583 if (scriptTag) {
12584     out.write(cb + "(");
12585 }
12586 out.print(dataBlock.toJsonString());
12587 if (scriptTag) {
12588     out.write(");");
12589 }
12590 </pre></code>
12591  *
12592  * @constructor
12593  * @param {Object} config A configuration object.
12594  */
12595 Roo.data.ScriptTagProxy = function(config){
12596     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12597     Roo.apply(this, config);
12598     this.head = document.getElementsByTagName("head")[0];
12599 };
12600
12601 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12602
12603 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12604     /**
12605      * @cfg {String} url The URL from which to request the data object.
12606      */
12607     /**
12608      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12609      */
12610     timeout : 30000,
12611     /**
12612      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12613      * the server the name of the callback function set up by the load call to process the returned data object.
12614      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12615      * javascript output which calls this named function passing the data object as its only parameter.
12616      */
12617     callbackParam : "callback",
12618     /**
12619      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12620      * name to the request.
12621      */
12622     nocache : true,
12623
12624     /**
12625      * Load data from the configured URL, read the data object into
12626      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12627      * process that block using the passed callback.
12628      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12629      * for the request to the remote server.
12630      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12631      * object into a block of Roo.data.Records.
12632      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12633      * The function must be passed <ul>
12634      * <li>The Record block object</li>
12635      * <li>The "arg" argument from the load function</li>
12636      * <li>A boolean success indicator</li>
12637      * </ul>
12638      * @param {Object} scope The scope in which to call the callback
12639      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12640      */
12641     load : function(params, reader, callback, scope, arg){
12642         if(this.fireEvent("beforeload", this, params) !== false){
12643
12644             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12645
12646             var url = this.url;
12647             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12648             if(this.nocache){
12649                 url += "&_dc=" + (new Date().getTime());
12650             }
12651             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12652             var trans = {
12653                 id : transId,
12654                 cb : "stcCallback"+transId,
12655                 scriptId : "stcScript"+transId,
12656                 params : params,
12657                 arg : arg,
12658                 url : url,
12659                 callback : callback,
12660                 scope : scope,
12661                 reader : reader
12662             };
12663             var conn = this;
12664
12665             window[trans.cb] = function(o){
12666                 conn.handleResponse(o, trans);
12667             };
12668
12669             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12670
12671             if(this.autoAbort !== false){
12672                 this.abort();
12673             }
12674
12675             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12676
12677             var script = document.createElement("script");
12678             script.setAttribute("src", url);
12679             script.setAttribute("type", "text/javascript");
12680             script.setAttribute("id", trans.scriptId);
12681             this.head.appendChild(script);
12682
12683             this.trans = trans;
12684         }else{
12685             callback.call(scope||this, null, arg, false);
12686         }
12687     },
12688
12689     // private
12690     isLoading : function(){
12691         return this.trans ? true : false;
12692     },
12693
12694     /**
12695      * Abort the current server request.
12696      */
12697     abort : function(){
12698         if(this.isLoading()){
12699             this.destroyTrans(this.trans);
12700         }
12701     },
12702
12703     // private
12704     destroyTrans : function(trans, isLoaded){
12705         this.head.removeChild(document.getElementById(trans.scriptId));
12706         clearTimeout(trans.timeoutId);
12707         if(isLoaded){
12708             window[trans.cb] = undefined;
12709             try{
12710                 delete window[trans.cb];
12711             }catch(e){}
12712         }else{
12713             // if hasn't been loaded, wait for load to remove it to prevent script error
12714             window[trans.cb] = function(){
12715                 window[trans.cb] = undefined;
12716                 try{
12717                     delete window[trans.cb];
12718                 }catch(e){}
12719             };
12720         }
12721     },
12722
12723     // private
12724     handleResponse : function(o, trans){
12725         this.trans = false;
12726         this.destroyTrans(trans, true);
12727         var result;
12728         try {
12729             result = trans.reader.readRecords(o);
12730         }catch(e){
12731             this.fireEvent("loadexception", this, o, trans.arg, e);
12732             trans.callback.call(trans.scope||window, null, trans.arg, false);
12733             return;
12734         }
12735         this.fireEvent("load", this, o, trans.arg);
12736         trans.callback.call(trans.scope||window, result, trans.arg, true);
12737     },
12738
12739     // private
12740     handleFailure : function(trans){
12741         this.trans = false;
12742         this.destroyTrans(trans, false);
12743         this.fireEvent("loadexception", this, null, trans.arg);
12744         trans.callback.call(trans.scope||window, null, trans.arg, false);
12745     }
12746 });/*
12747  * Based on:
12748  * Ext JS Library 1.1.1
12749  * Copyright(c) 2006-2007, Ext JS, LLC.
12750  *
12751  * Originally Released Under LGPL - original licence link has changed is not relivant.
12752  *
12753  * Fork - LGPL
12754  * <script type="text/javascript">
12755  */
12756
12757 /**
12758  * @class Roo.data.JsonReader
12759  * @extends Roo.data.DataReader
12760  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12761  * based on mappings in a provided Roo.data.Record constructor.
12762  * 
12763  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12764  * in the reply previously. 
12765  * 
12766  * <p>
12767  * Example code:
12768  * <pre><code>
12769 var RecordDef = Roo.data.Record.create([
12770     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12771     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12772 ]);
12773 var myReader = new Roo.data.JsonReader({
12774     totalProperty: "results",    // The property which contains the total dataset size (optional)
12775     root: "rows",                // The property which contains an Array of row objects
12776     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12777 }, RecordDef);
12778 </code></pre>
12779  * <p>
12780  * This would consume a JSON file like this:
12781  * <pre><code>
12782 { 'results': 2, 'rows': [
12783     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12784     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12785 }
12786 </code></pre>
12787  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12788  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12789  * paged from the remote server.
12790  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12791  * @cfg {String} root name of the property which contains the Array of row objects.
12792  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12793  * @cfg {Array} fields Array of field definition objects
12794  * @constructor
12795  * Create a new JsonReader
12796  * @param {Object} meta Metadata configuration options
12797  * @param {Object} recordType Either an Array of field definition objects,
12798  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12799  */
12800 Roo.data.JsonReader = function(meta, recordType){
12801     
12802     meta = meta || {};
12803     // set some defaults:
12804     Roo.applyIf(meta, {
12805         totalProperty: 'total',
12806         successProperty : 'success',
12807         root : 'data',
12808         id : 'id'
12809     });
12810     
12811     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12812 };
12813 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12814     
12815     /**
12816      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12817      * Used by Store query builder to append _requestMeta to params.
12818      * 
12819      */
12820     metaFromRemote : false,
12821     /**
12822      * This method is only used by a DataProxy which has retrieved data from a remote server.
12823      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12824      * @return {Object} data A data block which is used by an Roo.data.Store object as
12825      * a cache of Roo.data.Records.
12826      */
12827     read : function(response){
12828         var json = response.responseText;
12829        
12830         var o = /* eval:var:o */ eval("("+json+")");
12831         if(!o) {
12832             throw {message: "JsonReader.read: Json object not found"};
12833         }
12834         
12835         if(o.metaData){
12836             
12837             delete this.ef;
12838             this.metaFromRemote = true;
12839             this.meta = o.metaData;
12840             this.recordType = Roo.data.Record.create(o.metaData.fields);
12841             this.onMetaChange(this.meta, this.recordType, o);
12842         }
12843         return this.readRecords(o);
12844     },
12845
12846     // private function a store will implement
12847     onMetaChange : function(meta, recordType, o){
12848
12849     },
12850
12851     /**
12852          * @ignore
12853          */
12854     simpleAccess: function(obj, subsc) {
12855         return obj[subsc];
12856     },
12857
12858         /**
12859          * @ignore
12860          */
12861     getJsonAccessor: function(){
12862         var re = /[\[\.]/;
12863         return function(expr) {
12864             try {
12865                 return(re.test(expr))
12866                     ? new Function("obj", "return obj." + expr)
12867                     : function(obj){
12868                         return obj[expr];
12869                     };
12870             } catch(e){}
12871             return Roo.emptyFn;
12872         };
12873     }(),
12874
12875     /**
12876      * Create a data block containing Roo.data.Records from an XML document.
12877      * @param {Object} o An object which contains an Array of row objects in the property specified
12878      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12879      * which contains the total size of the dataset.
12880      * @return {Object} data A data block which is used by an Roo.data.Store object as
12881      * a cache of Roo.data.Records.
12882      */
12883     readRecords : function(o){
12884         /**
12885          * After any data loads, the raw JSON data is available for further custom processing.
12886          * @type Object
12887          */
12888         this.o = o;
12889         var s = this.meta, Record = this.recordType,
12890             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12891
12892 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12893         if (!this.ef) {
12894             if(s.totalProperty) {
12895                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12896                 }
12897                 if(s.successProperty) {
12898                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12899                 }
12900                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12901                 if (s.id) {
12902                         var g = this.getJsonAccessor(s.id);
12903                         this.getId = function(rec) {
12904                                 var r = g(rec);  
12905                                 return (r === undefined || r === "") ? null : r;
12906                         };
12907                 } else {
12908                         this.getId = function(){return null;};
12909                 }
12910             this.ef = [];
12911             for(var jj = 0; jj < fl; jj++){
12912                 f = fi[jj];
12913                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12914                 this.ef[jj] = this.getJsonAccessor(map);
12915             }
12916         }
12917
12918         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12919         if(s.totalProperty){
12920             var vt = parseInt(this.getTotal(o), 10);
12921             if(!isNaN(vt)){
12922                 totalRecords = vt;
12923             }
12924         }
12925         if(s.successProperty){
12926             var vs = this.getSuccess(o);
12927             if(vs === false || vs === 'false'){
12928                 success = false;
12929             }
12930         }
12931         var records = [];
12932         for(var i = 0; i < c; i++){
12933                 var n = root[i];
12934             var values = {};
12935             var id = this.getId(n);
12936             for(var j = 0; j < fl; j++){
12937                 f = fi[j];
12938             var v = this.ef[j](n);
12939             if (!f.convert) {
12940                 Roo.log('missing convert for ' + f.name);
12941                 Roo.log(f);
12942                 continue;
12943             }
12944             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12945             }
12946             var record = new Record(values, id);
12947             record.json = n;
12948             records[i] = record;
12949         }
12950         return {
12951             raw : o,
12952             success : success,
12953             records : records,
12954             totalRecords : totalRecords
12955         };
12956     }
12957 });/*
12958  * Based on:
12959  * Ext JS Library 1.1.1
12960  * Copyright(c) 2006-2007, Ext JS, LLC.
12961  *
12962  * Originally Released Under LGPL - original licence link has changed is not relivant.
12963  *
12964  * Fork - LGPL
12965  * <script type="text/javascript">
12966  */
12967
12968 /**
12969  * @class Roo.data.ArrayReader
12970  * @extends Roo.data.DataReader
12971  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12972  * Each element of that Array represents a row of data fields. The
12973  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12974  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12975  * <p>
12976  * Example code:.
12977  * <pre><code>
12978 var RecordDef = Roo.data.Record.create([
12979     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12980     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12981 ]);
12982 var myReader = new Roo.data.ArrayReader({
12983     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12984 }, RecordDef);
12985 </code></pre>
12986  * <p>
12987  * This would consume an Array like this:
12988  * <pre><code>
12989 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12990   </code></pre>
12991  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12992  * @constructor
12993  * Create a new JsonReader
12994  * @param {Object} meta Metadata configuration options.
12995  * @param {Object} recordType Either an Array of field definition objects
12996  * as specified to {@link Roo.data.Record#create},
12997  * or an {@link Roo.data.Record} object
12998  * created using {@link Roo.data.Record#create}.
12999  */
13000 Roo.data.ArrayReader = function(meta, recordType){
13001     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
13002 };
13003
13004 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13005     /**
13006      * Create a data block containing Roo.data.Records from an XML document.
13007      * @param {Object} o An Array of row objects which represents the dataset.
13008      * @return {Object} data A data block which is used by an Roo.data.Store object as
13009      * a cache of Roo.data.Records.
13010      */
13011     readRecords : function(o){
13012         var sid = this.meta ? this.meta.id : null;
13013         var recordType = this.recordType, fields = recordType.prototype.fields;
13014         var records = [];
13015         var root = o;
13016             for(var i = 0; i < root.length; i++){
13017                     var n = root[i];
13018                 var values = {};
13019                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13020                 for(var j = 0, jlen = fields.length; j < jlen; j++){
13021                 var f = fields.items[j];
13022                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13023                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13024                 v = f.convert(v);
13025                 values[f.name] = v;
13026             }
13027                 var record = new recordType(values, id);
13028                 record.json = n;
13029                 records[records.length] = record;
13030             }
13031             return {
13032                 records : records,
13033                 totalRecords : records.length
13034             };
13035     }
13036 });/*
13037  * - LGPL
13038  * * 
13039  */
13040
13041 /**
13042  * @class Roo.bootstrap.ComboBox
13043  * @extends Roo.bootstrap.TriggerField
13044  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13045  * @cfg {Boolean} append (true|false) default false
13046  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13047  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13048  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13049  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13050  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13051  * @cfg {Boolean} animate default true
13052  * @cfg {Boolean} emptyResultText only for touch device
13053  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13054  * @cfg {String} emptyTitle default ''
13055  * @constructor
13056  * Create a new ComboBox.
13057  * @param {Object} config Configuration options
13058  */
13059 Roo.bootstrap.ComboBox = function(config){
13060     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13061     this.addEvents({
13062         /**
13063          * @event expand
13064          * Fires when the dropdown list is expanded
13065         * @param {Roo.bootstrap.ComboBox} combo This combo box
13066         */
13067         'expand' : true,
13068         /**
13069          * @event collapse
13070          * Fires when the dropdown list is collapsed
13071         * @param {Roo.bootstrap.ComboBox} combo This combo box
13072         */
13073         'collapse' : true,
13074         /**
13075          * @event beforeselect
13076          * Fires before a list item is selected. Return false to cancel the selection.
13077         * @param {Roo.bootstrap.ComboBox} combo This combo box
13078         * @param {Roo.data.Record} record The data record returned from the underlying store
13079         * @param {Number} index The index of the selected item in the dropdown list
13080         */
13081         'beforeselect' : true,
13082         /**
13083          * @event select
13084          * Fires when a list item is selected
13085         * @param {Roo.bootstrap.ComboBox} combo This combo box
13086         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13087         * @param {Number} index The index of the selected item in the dropdown list
13088         */
13089         'select' : true,
13090         /**
13091          * @event beforequery
13092          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13093          * The event object passed has these properties:
13094         * @param {Roo.bootstrap.ComboBox} combo This combo box
13095         * @param {String} query The query
13096         * @param {Boolean} forceAll true to force "all" query
13097         * @param {Boolean} cancel true to cancel the query
13098         * @param {Object} e The query event object
13099         */
13100         'beforequery': true,
13101          /**
13102          * @event add
13103          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13104         * @param {Roo.bootstrap.ComboBox} combo This combo box
13105         */
13106         'add' : true,
13107         /**
13108          * @event edit
13109          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13110         * @param {Roo.bootstrap.ComboBox} combo This combo box
13111         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13112         */
13113         'edit' : true,
13114         /**
13115          * @event remove
13116          * Fires when the remove value from the combobox array
13117         * @param {Roo.bootstrap.ComboBox} combo This combo box
13118         */
13119         'remove' : true,
13120         /**
13121          * @event afterremove
13122          * Fires when the remove value from the combobox array
13123         * @param {Roo.bootstrap.ComboBox} combo This combo box
13124         */
13125         'afterremove' : true,
13126         /**
13127          * @event specialfilter
13128          * Fires when specialfilter
13129             * @param {Roo.bootstrap.ComboBox} combo This combo box
13130             */
13131         'specialfilter' : true,
13132         /**
13133          * @event tick
13134          * Fires when tick the element
13135             * @param {Roo.bootstrap.ComboBox} combo This combo box
13136             */
13137         'tick' : true,
13138         /**
13139          * @event touchviewdisplay
13140          * Fires when touch view require special display (default is using displayField)
13141             * @param {Roo.bootstrap.ComboBox} combo This combo box
13142             * @param {Object} cfg set html .
13143             */
13144         'touchviewdisplay' : true
13145         
13146     });
13147     
13148     this.item = [];
13149     this.tickItems = [];
13150     
13151     this.selectedIndex = -1;
13152     if(this.mode == 'local'){
13153         if(config.queryDelay === undefined){
13154             this.queryDelay = 10;
13155         }
13156         if(config.minChars === undefined){
13157             this.minChars = 0;
13158         }
13159     }
13160 };
13161
13162 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13163      
13164     /**
13165      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13166      * rendering into an Roo.Editor, defaults to false)
13167      */
13168     /**
13169      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13170      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13171      */
13172     /**
13173      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13174      */
13175     /**
13176      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13177      * the dropdown list (defaults to undefined, with no header element)
13178      */
13179
13180      /**
13181      * @cfg {String/Roo.Template} tpl The template to use to render the output
13182      */
13183      
13184      /**
13185      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13186      */
13187     listWidth: undefined,
13188     /**
13189      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13190      * mode = 'remote' or 'text' if mode = 'local')
13191      */
13192     displayField: undefined,
13193     
13194     /**
13195      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13196      * mode = 'remote' or 'value' if mode = 'local'). 
13197      * Note: use of a valueField requires the user make a selection
13198      * in order for a value to be mapped.
13199      */
13200     valueField: undefined,
13201     /**
13202      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13203      */
13204     modalTitle : '',
13205     
13206     /**
13207      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13208      * field's data value (defaults to the underlying DOM element's name)
13209      */
13210     hiddenName: undefined,
13211     /**
13212      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13213      */
13214     listClass: '',
13215     /**
13216      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13217      */
13218     selectedClass: 'active',
13219     
13220     /**
13221      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13222      */
13223     shadow:'sides',
13224     /**
13225      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13226      * anchor positions (defaults to 'tl-bl')
13227      */
13228     listAlign: 'tl-bl?',
13229     /**
13230      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13231      */
13232     maxHeight: 300,
13233     /**
13234      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13235      * query specified by the allQuery config option (defaults to 'query')
13236      */
13237     triggerAction: 'query',
13238     /**
13239      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13240      * (defaults to 4, does not apply if editable = false)
13241      */
13242     minChars : 4,
13243     /**
13244      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13245      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13246      */
13247     typeAhead: false,
13248     /**
13249      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13250      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13251      */
13252     queryDelay: 500,
13253     /**
13254      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13255      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13256      */
13257     pageSize: 0,
13258     /**
13259      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13260      * when editable = true (defaults to false)
13261      */
13262     selectOnFocus:false,
13263     /**
13264      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13265      */
13266     queryParam: 'query',
13267     /**
13268      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13269      * when mode = 'remote' (defaults to 'Loading...')
13270      */
13271     loadingText: 'Loading...',
13272     /**
13273      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13274      */
13275     resizable: false,
13276     /**
13277      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13278      */
13279     handleHeight : 8,
13280     /**
13281      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13282      * traditional select (defaults to true)
13283      */
13284     editable: true,
13285     /**
13286      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13287      */
13288     allQuery: '',
13289     /**
13290      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13291      */
13292     mode: 'remote',
13293     /**
13294      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13295      * listWidth has a higher value)
13296      */
13297     minListWidth : 70,
13298     /**
13299      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13300      * allow the user to set arbitrary text into the field (defaults to false)
13301      */
13302     forceSelection:false,
13303     /**
13304      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13305      * if typeAhead = true (defaults to 250)
13306      */
13307     typeAheadDelay : 250,
13308     /**
13309      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13310      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13311      */
13312     valueNotFoundText : undefined,
13313     /**
13314      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13315      */
13316     blockFocus : false,
13317     
13318     /**
13319      * @cfg {Boolean} disableClear Disable showing of clear button.
13320      */
13321     disableClear : false,
13322     /**
13323      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13324      */
13325     alwaysQuery : false,
13326     
13327     /**
13328      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13329      */
13330     multiple : false,
13331     
13332     /**
13333      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13334      */
13335     invalidClass : "has-warning",
13336     
13337     /**
13338      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13339      */
13340     validClass : "has-success",
13341     
13342     /**
13343      * @cfg {Boolean} specialFilter (true|false) special filter default false
13344      */
13345     specialFilter : false,
13346     
13347     /**
13348      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13349      */
13350     mobileTouchView : true,
13351     
13352     /**
13353      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13354      */
13355     useNativeIOS : false,
13356     
13357     /**
13358      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13359      */
13360     mobile_restrict_height : false,
13361     
13362     ios_options : false,
13363     
13364     //private
13365     addicon : false,
13366     editicon: false,
13367     
13368     page: 0,
13369     hasQuery: false,
13370     append: false,
13371     loadNext: false,
13372     autoFocus : true,
13373     tickable : false,
13374     btnPosition : 'right',
13375     triggerList : true,
13376     showToggleBtn : true,
13377     animate : true,
13378     emptyResultText: 'Empty',
13379     triggerText : 'Select',
13380     emptyTitle : '',
13381     
13382     // element that contains real text value.. (when hidden is used..)
13383     
13384     getAutoCreate : function()
13385     {   
13386         var cfg = false;
13387         //render
13388         /*
13389          * Render classic select for iso
13390          */
13391         
13392         if(Roo.isIOS && this.useNativeIOS){
13393             cfg = this.getAutoCreateNativeIOS();
13394             return cfg;
13395         }
13396         
13397         /*
13398          * Touch Devices
13399          */
13400         
13401         if(Roo.isTouch && this.mobileTouchView){
13402             cfg = this.getAutoCreateTouchView();
13403             return cfg;;
13404         }
13405         
13406         /*
13407          *  Normal ComboBox
13408          */
13409         if(!this.tickable){
13410             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13411             return cfg;
13412         }
13413         
13414         /*
13415          *  ComboBox with tickable selections
13416          */
13417              
13418         var align = this.labelAlign || this.parentLabelAlign();
13419         
13420         cfg = {
13421             cls : 'form-group roo-combobox-tickable' //input-group
13422         };
13423         
13424         var btn_text_select = '';
13425         var btn_text_done = '';
13426         var btn_text_cancel = '';
13427         
13428         if (this.btn_text_show) {
13429             btn_text_select = 'Select';
13430             btn_text_done = 'Done';
13431             btn_text_cancel = 'Cancel'; 
13432         }
13433         
13434         var buttons = {
13435             tag : 'div',
13436             cls : 'tickable-buttons',
13437             cn : [
13438                 {
13439                     tag : 'button',
13440                     type : 'button',
13441                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13442                     //html : this.triggerText
13443                     html: btn_text_select
13444                 },
13445                 {
13446                     tag : 'button',
13447                     type : 'button',
13448                     name : 'ok',
13449                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13450                     //html : 'Done'
13451                     html: btn_text_done
13452                 },
13453                 {
13454                     tag : 'button',
13455                     type : 'button',
13456                     name : 'cancel',
13457                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13458                     //html : 'Cancel'
13459                     html: btn_text_cancel
13460                 }
13461             ]
13462         };
13463         
13464         if(this.editable){
13465             buttons.cn.unshift({
13466                 tag: 'input',
13467                 cls: 'roo-select2-search-field-input'
13468             });
13469         }
13470         
13471         var _this = this;
13472         
13473         Roo.each(buttons.cn, function(c){
13474             if (_this.size) {
13475                 c.cls += ' btn-' + _this.size;
13476             }
13477
13478             if (_this.disabled) {
13479                 c.disabled = true;
13480             }
13481         });
13482         
13483         var box = {
13484             tag: 'div',
13485             style : 'display: contents',
13486             cn: [
13487                 {
13488                     tag: 'input',
13489                     type : 'hidden',
13490                     cls: 'form-hidden-field'
13491                 },
13492                 {
13493                     tag: 'ul',
13494                     cls: 'roo-select2-choices',
13495                     cn:[
13496                         {
13497                             tag: 'li',
13498                             cls: 'roo-select2-search-field',
13499                             cn: [
13500                                 buttons
13501                             ]
13502                         }
13503                     ]
13504                 }
13505             ]
13506         };
13507         
13508         var combobox = {
13509             cls: 'roo-select2-container input-group roo-select2-container-multi',
13510             cn: [
13511                 
13512                 box
13513 //                {
13514 //                    tag: 'ul',
13515 //                    cls: 'typeahead typeahead-long dropdown-menu',
13516 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13517 //                }
13518             ]
13519         };
13520         
13521         if(this.hasFeedback && !this.allowBlank){
13522             
13523             var feedback = {
13524                 tag: 'span',
13525                 cls: 'glyphicon form-control-feedback'
13526             };
13527
13528             combobox.cn.push(feedback);
13529         }
13530         
13531         var indicator = {
13532             tag : 'i',
13533             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13534             tooltip : 'This field is required'
13535         };
13536         if (Roo.bootstrap.version == 4) {
13537             indicator = {
13538                 tag : 'i',
13539                 style : 'display:none'
13540             };
13541         }
13542         if (align ==='left' && this.fieldLabel.length) {
13543             
13544             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13545             
13546             cfg.cn = [
13547                 indicator,
13548                 {
13549                     tag: 'label',
13550                     'for' :  id,
13551                     cls : 'control-label col-form-label',
13552                     html : this.fieldLabel
13553
13554                 },
13555                 {
13556                     cls : "", 
13557                     cn: [
13558                         combobox
13559                     ]
13560                 }
13561
13562             ];
13563             
13564             var labelCfg = cfg.cn[1];
13565             var contentCfg = cfg.cn[2];
13566             
13567
13568             if(this.indicatorpos == 'right'){
13569                 
13570                 cfg.cn = [
13571                     {
13572                         tag: 'label',
13573                         'for' :  id,
13574                         cls : 'control-label col-form-label',
13575                         cn : [
13576                             {
13577                                 tag : 'span',
13578                                 html : this.fieldLabel
13579                             },
13580                             indicator
13581                         ]
13582                     },
13583                     {
13584                         cls : "",
13585                         cn: [
13586                             combobox
13587                         ]
13588                     }
13589
13590                 ];
13591                 
13592                 
13593                 
13594                 labelCfg = cfg.cn[0];
13595                 contentCfg = cfg.cn[1];
13596             
13597             }
13598             
13599             if(this.labelWidth > 12){
13600                 labelCfg.style = "width: " + this.labelWidth + 'px';
13601             }
13602             
13603             if(this.labelWidth < 13 && this.labelmd == 0){
13604                 this.labelmd = this.labelWidth;
13605             }
13606             
13607             if(this.labellg > 0){
13608                 labelCfg.cls += ' col-lg-' + this.labellg;
13609                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13610             }
13611             
13612             if(this.labelmd > 0){
13613                 labelCfg.cls += ' col-md-' + this.labelmd;
13614                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13615             }
13616             
13617             if(this.labelsm > 0){
13618                 labelCfg.cls += ' col-sm-' + this.labelsm;
13619                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13620             }
13621             
13622             if(this.labelxs > 0){
13623                 labelCfg.cls += ' col-xs-' + this.labelxs;
13624                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13625             }
13626                 
13627                 
13628         } else if ( this.fieldLabel.length) {
13629 //                Roo.log(" label");
13630                  cfg.cn = [
13631                    indicator,
13632                     {
13633                         tag: 'label',
13634                         //cls : 'input-group-addon',
13635                         html : this.fieldLabel
13636                     },
13637                     combobox
13638                 ];
13639                 
13640                 if(this.indicatorpos == 'right'){
13641                     cfg.cn = [
13642                         {
13643                             tag: 'label',
13644                             //cls : 'input-group-addon',
13645                             html : this.fieldLabel
13646                         },
13647                         indicator,
13648                         combobox
13649                     ];
13650                     
13651                 }
13652
13653         } else {
13654             
13655 //                Roo.log(" no label && no align");
13656                 cfg = combobox
13657                      
13658                 
13659         }
13660          
13661         var settings=this;
13662         ['xs','sm','md','lg'].map(function(size){
13663             if (settings[size]) {
13664                 cfg.cls += ' col-' + size + '-' + settings[size];
13665             }
13666         });
13667         
13668         return cfg;
13669         
13670     },
13671     
13672     _initEventsCalled : false,
13673     
13674     // private
13675     initEvents: function()
13676     {   
13677         if (this._initEventsCalled) { // as we call render... prevent looping...
13678             return;
13679         }
13680         this._initEventsCalled = true;
13681         
13682         if (!this.store) {
13683             throw "can not find store for combo";
13684         }
13685         
13686         this.indicator = this.indicatorEl();
13687         
13688         this.store = Roo.factory(this.store, Roo.data);
13689         this.store.parent = this;
13690         
13691         // if we are building from html. then this element is so complex, that we can not really
13692         // use the rendered HTML.
13693         // so we have to trash and replace the previous code.
13694         if (Roo.XComponent.build_from_html) {
13695             // remove this element....
13696             var e = this.el.dom, k=0;
13697             while (e ) { e = e.previousSibling;  ++k;}
13698
13699             this.el.remove();
13700             
13701             this.el=false;
13702             this.rendered = false;
13703             
13704             this.render(this.parent().getChildContainer(true), k);
13705         }
13706         
13707         if(Roo.isIOS && this.useNativeIOS){
13708             this.initIOSView();
13709             return;
13710         }
13711         
13712         /*
13713          * Touch Devices
13714          */
13715         
13716         if(Roo.isTouch && this.mobileTouchView){
13717             this.initTouchView();
13718             return;
13719         }
13720         
13721         if(this.tickable){
13722             this.initTickableEvents();
13723             return;
13724         }
13725         
13726         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13727         
13728         if(this.hiddenName){
13729             
13730             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13731             
13732             this.hiddenField.dom.value =
13733                 this.hiddenValue !== undefined ? this.hiddenValue :
13734                 this.value !== undefined ? this.value : '';
13735
13736             // prevent input submission
13737             this.el.dom.removeAttribute('name');
13738             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13739              
13740              
13741         }
13742         //if(Roo.isGecko){
13743         //    this.el.dom.setAttribute('autocomplete', 'off');
13744         //}
13745         
13746         var cls = 'x-combo-list';
13747         
13748         //this.list = new Roo.Layer({
13749         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13750         //});
13751         
13752         var _this = this;
13753         
13754         (function(){
13755             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13756             _this.list.setWidth(lw);
13757         }).defer(100);
13758         
13759         this.list.on('mouseover', this.onViewOver, this);
13760         this.list.on('mousemove', this.onViewMove, this);
13761         this.list.on('scroll', this.onViewScroll, this);
13762         
13763         /*
13764         this.list.swallowEvent('mousewheel');
13765         this.assetHeight = 0;
13766
13767         if(this.title){
13768             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13769             this.assetHeight += this.header.getHeight();
13770         }
13771
13772         this.innerList = this.list.createChild({cls:cls+'-inner'});
13773         this.innerList.on('mouseover', this.onViewOver, this);
13774         this.innerList.on('mousemove', this.onViewMove, this);
13775         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13776         
13777         if(this.allowBlank && !this.pageSize && !this.disableClear){
13778             this.footer = this.list.createChild({cls:cls+'-ft'});
13779             this.pageTb = new Roo.Toolbar(this.footer);
13780            
13781         }
13782         if(this.pageSize){
13783             this.footer = this.list.createChild({cls:cls+'-ft'});
13784             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13785                     {pageSize: this.pageSize});
13786             
13787         }
13788         
13789         if (this.pageTb && this.allowBlank && !this.disableClear) {
13790             var _this = this;
13791             this.pageTb.add(new Roo.Toolbar.Fill(), {
13792                 cls: 'x-btn-icon x-btn-clear',
13793                 text: '&#160;',
13794                 handler: function()
13795                 {
13796                     _this.collapse();
13797                     _this.clearValue();
13798                     _this.onSelect(false, -1);
13799                 }
13800             });
13801         }
13802         if (this.footer) {
13803             this.assetHeight += this.footer.getHeight();
13804         }
13805         */
13806             
13807         if(!this.tpl){
13808             this.tpl = Roo.bootstrap.version == 4 ?
13809                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13810                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13811         }
13812
13813         this.view = new Roo.View(this.list, this.tpl, {
13814             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13815         });
13816         //this.view.wrapEl.setDisplayed(false);
13817         this.view.on('click', this.onViewClick, this);
13818         
13819         
13820         this.store.on('beforeload', this.onBeforeLoad, this);
13821         this.store.on('load', this.onLoad, this);
13822         this.store.on('loadexception', this.onLoadException, this);
13823         /*
13824         if(this.resizable){
13825             this.resizer = new Roo.Resizable(this.list,  {
13826                pinned:true, handles:'se'
13827             });
13828             this.resizer.on('resize', function(r, w, h){
13829                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13830                 this.listWidth = w;
13831                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13832                 this.restrictHeight();
13833             }, this);
13834             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13835         }
13836         */
13837         if(!this.editable){
13838             this.editable = true;
13839             this.setEditable(false);
13840         }
13841         
13842         /*
13843         
13844         if (typeof(this.events.add.listeners) != 'undefined') {
13845             
13846             this.addicon = this.wrap.createChild(
13847                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13848        
13849             this.addicon.on('click', function(e) {
13850                 this.fireEvent('add', this);
13851             }, this);
13852         }
13853         if (typeof(this.events.edit.listeners) != 'undefined') {
13854             
13855             this.editicon = this.wrap.createChild(
13856                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13857             if (this.addicon) {
13858                 this.editicon.setStyle('margin-left', '40px');
13859             }
13860             this.editicon.on('click', function(e) {
13861                 
13862                 // we fire even  if inothing is selected..
13863                 this.fireEvent('edit', this, this.lastData );
13864                 
13865             }, this);
13866         }
13867         */
13868         
13869         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13870             "up" : function(e){
13871                 this.inKeyMode = true;
13872                 this.selectPrev();
13873             },
13874
13875             "down" : function(e){
13876                 if(!this.isExpanded()){
13877                     this.onTriggerClick();
13878                 }else{
13879                     this.inKeyMode = true;
13880                     this.selectNext();
13881                 }
13882             },
13883
13884             "enter" : function(e){
13885 //                this.onViewClick();
13886                 //return true;
13887                 this.collapse();
13888                 
13889                 if(this.fireEvent("specialkey", this, e)){
13890                     this.onViewClick(false);
13891                 }
13892                 
13893                 return true;
13894             },
13895
13896             "esc" : function(e){
13897                 this.collapse();
13898             },
13899
13900             "tab" : function(e){
13901                 this.collapse();
13902                 
13903                 if(this.fireEvent("specialkey", this, e)){
13904                     this.onViewClick(false);
13905                 }
13906                 
13907                 return true;
13908             },
13909
13910             scope : this,
13911
13912             doRelay : function(foo, bar, hname){
13913                 if(hname == 'down' || this.scope.isExpanded()){
13914                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13915                 }
13916                 return true;
13917             },
13918
13919             forceKeyDown: true
13920         });
13921         
13922         
13923         this.queryDelay = Math.max(this.queryDelay || 10,
13924                 this.mode == 'local' ? 10 : 250);
13925         
13926         
13927         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13928         
13929         if(this.typeAhead){
13930             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13931         }
13932         if(this.editable !== false){
13933             this.inputEl().on("keyup", this.onKeyUp, this);
13934         }
13935         if(this.forceSelection){
13936             this.inputEl().on('blur', this.doForce, this);
13937         }
13938         
13939         if(this.multiple){
13940             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13941             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13942         }
13943     },
13944     
13945     initTickableEvents: function()
13946     {   
13947         this.createList();
13948         
13949         if(this.hiddenName){
13950             
13951             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13952             
13953             this.hiddenField.dom.value =
13954                 this.hiddenValue !== undefined ? this.hiddenValue :
13955                 this.value !== undefined ? this.value : '';
13956
13957             // prevent input submission
13958             this.el.dom.removeAttribute('name');
13959             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13960              
13961              
13962         }
13963         
13964 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13965         
13966         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13967         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13968         if(this.triggerList){
13969             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13970         }
13971          
13972         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13973         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13974         
13975         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13976         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13977         
13978         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13979         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13980         
13981         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13982         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13983         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13984         
13985         this.okBtn.hide();
13986         this.cancelBtn.hide();
13987         
13988         var _this = this;
13989         
13990         (function(){
13991             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13992             _this.list.setWidth(lw);
13993         }).defer(100);
13994         
13995         this.list.on('mouseover', this.onViewOver, this);
13996         this.list.on('mousemove', this.onViewMove, this);
13997         
13998         this.list.on('scroll', this.onViewScroll, this);
13999         
14000         if(!this.tpl){
14001             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
14002                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14003         }
14004
14005         this.view = new Roo.View(this.list, this.tpl, {
14006             singleSelect:true,
14007             tickable:true,
14008             parent:this,
14009             store: this.store,
14010             selectedClass: this.selectedClass
14011         });
14012         
14013         //this.view.wrapEl.setDisplayed(false);
14014         this.view.on('click', this.onViewClick, this);
14015         
14016         
14017         
14018         this.store.on('beforeload', this.onBeforeLoad, this);
14019         this.store.on('load', this.onLoad, this);
14020         this.store.on('loadexception', this.onLoadException, this);
14021         
14022         if(this.editable){
14023             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14024                 "up" : function(e){
14025                     this.inKeyMode = true;
14026                     this.selectPrev();
14027                 },
14028
14029                 "down" : function(e){
14030                     this.inKeyMode = true;
14031                     this.selectNext();
14032                 },
14033
14034                 "enter" : function(e){
14035                     if(this.fireEvent("specialkey", this, e)){
14036                         this.onViewClick(false);
14037                     }
14038                     
14039                     return true;
14040                 },
14041
14042                 "esc" : function(e){
14043                     this.onTickableFooterButtonClick(e, false, false);
14044                 },
14045
14046                 "tab" : function(e){
14047                     this.fireEvent("specialkey", this, e);
14048                     
14049                     this.onTickableFooterButtonClick(e, false, false);
14050                     
14051                     return true;
14052                 },
14053
14054                 scope : this,
14055
14056                 doRelay : function(e, fn, key){
14057                     if(this.scope.isExpanded()){
14058                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14059                     }
14060                     return true;
14061                 },
14062
14063                 forceKeyDown: true
14064             });
14065         }
14066         
14067         this.queryDelay = Math.max(this.queryDelay || 10,
14068                 this.mode == 'local' ? 10 : 250);
14069         
14070         
14071         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14072         
14073         if(this.typeAhead){
14074             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14075         }
14076         
14077         if(this.editable !== false){
14078             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14079         }
14080         
14081         this.indicator = this.indicatorEl();
14082         
14083         if(this.indicator){
14084             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14085             this.indicator.hide();
14086         }
14087         
14088     },
14089
14090     onDestroy : function(){
14091         if(this.view){
14092             this.view.setStore(null);
14093             this.view.el.removeAllListeners();
14094             this.view.el.remove();
14095             this.view.purgeListeners();
14096         }
14097         if(this.list){
14098             this.list.dom.innerHTML  = '';
14099         }
14100         
14101         if(this.store){
14102             this.store.un('beforeload', this.onBeforeLoad, this);
14103             this.store.un('load', this.onLoad, this);
14104             this.store.un('loadexception', this.onLoadException, this);
14105         }
14106         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14107     },
14108
14109     // private
14110     fireKey : function(e){
14111         if(e.isNavKeyPress() && !this.list.isVisible()){
14112             this.fireEvent("specialkey", this, e);
14113         }
14114     },
14115
14116     // private
14117     onResize: function(w, h){
14118 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14119 //        
14120 //        if(typeof w != 'number'){
14121 //            // we do not handle it!?!?
14122 //            return;
14123 //        }
14124 //        var tw = this.trigger.getWidth();
14125 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14126 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14127 //        var x = w - tw;
14128 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14129 //            
14130 //        //this.trigger.setStyle('left', x+'px');
14131 //        
14132 //        if(this.list && this.listWidth === undefined){
14133 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14134 //            this.list.setWidth(lw);
14135 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14136 //        }
14137         
14138     
14139         
14140     },
14141
14142     /**
14143      * Allow or prevent the user from directly editing the field text.  If false is passed,
14144      * the user will only be able to select from the items defined in the dropdown list.  This method
14145      * is the runtime equivalent of setting the 'editable' config option at config time.
14146      * @param {Boolean} value True to allow the user to directly edit the field text
14147      */
14148     setEditable : function(value){
14149         if(value == this.editable){
14150             return;
14151         }
14152         this.editable = value;
14153         if(!value){
14154             this.inputEl().dom.setAttribute('readOnly', true);
14155             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14156             this.inputEl().addClass('x-combo-noedit');
14157         }else{
14158             this.inputEl().dom.setAttribute('readOnly', false);
14159             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14160             this.inputEl().removeClass('x-combo-noedit');
14161         }
14162     },
14163
14164     // private
14165     
14166     onBeforeLoad : function(combo,opts){
14167         if(!this.hasFocus){
14168             return;
14169         }
14170          if (!opts.add) {
14171             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14172          }
14173         this.restrictHeight();
14174         this.selectedIndex = -1;
14175     },
14176
14177     // private
14178     onLoad : function(){
14179         
14180         this.hasQuery = false;
14181         
14182         if(!this.hasFocus){
14183             return;
14184         }
14185         
14186         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14187             this.loading.hide();
14188         }
14189         
14190         if(this.store.getCount() > 0){
14191             
14192             this.expand();
14193             this.restrictHeight();
14194             if(this.lastQuery == this.allQuery){
14195                 if(this.editable && !this.tickable){
14196                     this.inputEl().dom.select();
14197                 }
14198                 
14199                 if(
14200                     !this.selectByValue(this.value, true) &&
14201                     this.autoFocus && 
14202                     (
14203                         !this.store.lastOptions ||
14204                         typeof(this.store.lastOptions.add) == 'undefined' || 
14205                         this.store.lastOptions.add != true
14206                     )
14207                 ){
14208                     this.select(0, true);
14209                 }
14210             }else{
14211                 if(this.autoFocus){
14212                     this.selectNext();
14213                 }
14214                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14215                     this.taTask.delay(this.typeAheadDelay);
14216                 }
14217             }
14218         }else{
14219             this.onEmptyResults();
14220         }
14221         
14222         //this.el.focus();
14223     },
14224     // private
14225     onLoadException : function()
14226     {
14227         this.hasQuery = false;
14228         
14229         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14230             this.loading.hide();
14231         }
14232         
14233         if(this.tickable && this.editable){
14234             return;
14235         }
14236         
14237         this.collapse();
14238         // only causes errors at present
14239         //Roo.log(this.store.reader.jsonData);
14240         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14241             // fixme
14242             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14243         //}
14244         
14245         
14246     },
14247     // private
14248     onTypeAhead : function(){
14249         if(this.store.getCount() > 0){
14250             var r = this.store.getAt(0);
14251             var newValue = r.data[this.displayField];
14252             var len = newValue.length;
14253             var selStart = this.getRawValue().length;
14254             
14255             if(selStart != len){
14256                 this.setRawValue(newValue);
14257                 this.selectText(selStart, newValue.length);
14258             }
14259         }
14260     },
14261
14262     // private
14263     onSelect : function(record, index){
14264         
14265         if(this.fireEvent('beforeselect', this, record, index) !== false){
14266         
14267             this.setFromData(index > -1 ? record.data : false);
14268             
14269             this.collapse();
14270             this.fireEvent('select', this, record, index);
14271         }
14272     },
14273
14274     /**
14275      * Returns the currently selected field value or empty string if no value is set.
14276      * @return {String} value The selected value
14277      */
14278     getValue : function()
14279     {
14280         if(Roo.isIOS && this.useNativeIOS){
14281             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14282         }
14283         
14284         if(this.multiple){
14285             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14286         }
14287         
14288         if(this.valueField){
14289             return typeof this.value != 'undefined' ? this.value : '';
14290         }else{
14291             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14292         }
14293     },
14294     
14295     getRawValue : function()
14296     {
14297         if(Roo.isIOS && this.useNativeIOS){
14298             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14299         }
14300         
14301         var v = this.inputEl().getValue();
14302         
14303         return v;
14304     },
14305
14306     /**
14307      * Clears any text/value currently set in the field
14308      */
14309     clearValue : function(){
14310         
14311         if(this.hiddenField){
14312             this.hiddenField.dom.value = '';
14313         }
14314         this.value = '';
14315         this.setRawValue('');
14316         this.lastSelectionText = '';
14317         this.lastData = false;
14318         
14319         var close = this.closeTriggerEl();
14320         
14321         if(close){
14322             close.hide();
14323         }
14324         
14325         this.validate();
14326         
14327     },
14328
14329     /**
14330      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14331      * will be displayed in the field.  If the value does not match the data value of an existing item,
14332      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14333      * Otherwise the field will be blank (although the value will still be set).
14334      * @param {String} value The value to match
14335      */
14336     setValue : function(v)
14337     {
14338         if(Roo.isIOS && this.useNativeIOS){
14339             this.setIOSValue(v);
14340             return;
14341         }
14342         
14343         if(this.multiple){
14344             this.syncValue();
14345             return;
14346         }
14347         
14348         var text = v;
14349         if(this.valueField){
14350             var r = this.findRecord(this.valueField, v);
14351             if(r){
14352                 text = r.data[this.displayField];
14353             }else if(this.valueNotFoundText !== undefined){
14354                 text = this.valueNotFoundText;
14355             }
14356         }
14357         this.lastSelectionText = text;
14358         if(this.hiddenField){
14359             this.hiddenField.dom.value = v;
14360         }
14361         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14362         this.value = v;
14363         
14364         var close = this.closeTriggerEl();
14365         
14366         if(close){
14367             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14368         }
14369         
14370         this.validate();
14371     },
14372     /**
14373      * @property {Object} the last set data for the element
14374      */
14375     
14376     lastData : false,
14377     /**
14378      * Sets the value of the field based on a object which is related to the record format for the store.
14379      * @param {Object} value the value to set as. or false on reset?
14380      */
14381     setFromData : function(o){
14382         
14383         if(this.multiple){
14384             this.addItem(o);
14385             return;
14386         }
14387             
14388         var dv = ''; // display value
14389         var vv = ''; // value value..
14390         this.lastData = o;
14391         if (this.displayField) {
14392             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14393         } else {
14394             // this is an error condition!!!
14395             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14396         }
14397         
14398         if(this.valueField){
14399             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14400         }
14401         
14402         var close = this.closeTriggerEl();
14403         
14404         if(close){
14405             if(dv.length || vv * 1 > 0){
14406                 close.show() ;
14407                 this.blockFocus=true;
14408             } else {
14409                 close.hide();
14410             }             
14411         }
14412         
14413         if(this.hiddenField){
14414             this.hiddenField.dom.value = vv;
14415             
14416             this.lastSelectionText = dv;
14417             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14418             this.value = vv;
14419             return;
14420         }
14421         // no hidden field.. - we store the value in 'value', but still display
14422         // display field!!!!
14423         this.lastSelectionText = dv;
14424         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14425         this.value = vv;
14426         
14427         
14428         
14429     },
14430     // private
14431     reset : function(){
14432         // overridden so that last data is reset..
14433         
14434         if(this.multiple){
14435             this.clearItem();
14436             return;
14437         }
14438         
14439         this.setValue(this.originalValue);
14440         //this.clearInvalid();
14441         this.lastData = false;
14442         if (this.view) {
14443             this.view.clearSelections();
14444         }
14445         
14446         this.validate();
14447     },
14448     // private
14449     findRecord : function(prop, value){
14450         var record;
14451         if(this.store.getCount() > 0){
14452             this.store.each(function(r){
14453                 if(r.data[prop] == value){
14454                     record = r;
14455                     return false;
14456                 }
14457                 return true;
14458             });
14459         }
14460         return record;
14461     },
14462     
14463     getName: function()
14464     {
14465         // returns hidden if it's set..
14466         if (!this.rendered) {return ''};
14467         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14468         
14469     },
14470     // private
14471     onViewMove : function(e, t){
14472         this.inKeyMode = false;
14473     },
14474
14475     // private
14476     onViewOver : function(e, t){
14477         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14478             return;
14479         }
14480         var item = this.view.findItemFromChild(t);
14481         
14482         if(item){
14483             var index = this.view.indexOf(item);
14484             this.select(index, false);
14485         }
14486     },
14487
14488     // private
14489     onViewClick : function(view, doFocus, el, e)
14490     {
14491         var index = this.view.getSelectedIndexes()[0];
14492         
14493         var r = this.store.getAt(index);
14494         
14495         if(this.tickable){
14496             
14497             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14498                 return;
14499             }
14500             
14501             var rm = false;
14502             var _this = this;
14503             
14504             Roo.each(this.tickItems, function(v,k){
14505                 
14506                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14507                     Roo.log(v);
14508                     _this.tickItems.splice(k, 1);
14509                     
14510                     if(typeof(e) == 'undefined' && view == false){
14511                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14512                     }
14513                     
14514                     rm = true;
14515                     return;
14516                 }
14517             });
14518             
14519             if(rm){
14520                 return;
14521             }
14522             
14523             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14524                 this.tickItems.push(r.data);
14525             }
14526             
14527             if(typeof(e) == 'undefined' && view == false){
14528                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14529             }
14530                     
14531             return;
14532         }
14533         
14534         if(r){
14535             this.onSelect(r, index);
14536         }
14537         if(doFocus !== false && !this.blockFocus){
14538             this.inputEl().focus();
14539         }
14540     },
14541
14542     // private
14543     restrictHeight : function(){
14544         //this.innerList.dom.style.height = '';
14545         //var inner = this.innerList.dom;
14546         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14547         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14548         //this.list.beginUpdate();
14549         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14550         this.list.alignTo(this.inputEl(), this.listAlign);
14551         this.list.alignTo(this.inputEl(), this.listAlign);
14552         //this.list.endUpdate();
14553     },
14554
14555     // private
14556     onEmptyResults : function(){
14557         
14558         if(this.tickable && this.editable){
14559             this.hasFocus = false;
14560             this.restrictHeight();
14561             return;
14562         }
14563         
14564         this.collapse();
14565     },
14566
14567     /**
14568      * Returns true if the dropdown list is expanded, else false.
14569      */
14570     isExpanded : function(){
14571         return this.list.isVisible();
14572     },
14573
14574     /**
14575      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14576      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14577      * @param {String} value The data value of the item to select
14578      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14579      * selected item if it is not currently in view (defaults to true)
14580      * @return {Boolean} True if the value matched an item in the list, else false
14581      */
14582     selectByValue : function(v, scrollIntoView){
14583         if(v !== undefined && v !== null){
14584             var r = this.findRecord(this.valueField || this.displayField, v);
14585             if(r){
14586                 this.select(this.store.indexOf(r), scrollIntoView);
14587                 return true;
14588             }
14589         }
14590         return false;
14591     },
14592
14593     /**
14594      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14595      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14596      * @param {Number} index The zero-based index of the list item to select
14597      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14598      * selected item if it is not currently in view (defaults to true)
14599      */
14600     select : function(index, scrollIntoView){
14601         this.selectedIndex = index;
14602         this.view.select(index);
14603         if(scrollIntoView !== false){
14604             var el = this.view.getNode(index);
14605             /*
14606              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14607              */
14608             if(el){
14609                 this.list.scrollChildIntoView(el, false);
14610             }
14611         }
14612     },
14613
14614     // private
14615     selectNext : function(){
14616         var ct = this.store.getCount();
14617         if(ct > 0){
14618             if(this.selectedIndex == -1){
14619                 this.select(0);
14620             }else if(this.selectedIndex < ct-1){
14621                 this.select(this.selectedIndex+1);
14622             }
14623         }
14624     },
14625
14626     // private
14627     selectPrev : function(){
14628         var ct = this.store.getCount();
14629         if(ct > 0){
14630             if(this.selectedIndex == -1){
14631                 this.select(0);
14632             }else if(this.selectedIndex != 0){
14633                 this.select(this.selectedIndex-1);
14634             }
14635         }
14636     },
14637
14638     // private
14639     onKeyUp : function(e){
14640         if(this.editable !== false && !e.isSpecialKey()){
14641             this.lastKey = e.getKey();
14642             this.dqTask.delay(this.queryDelay);
14643         }
14644     },
14645
14646     // private
14647     validateBlur : function(){
14648         return !this.list || !this.list.isVisible();   
14649     },
14650
14651     // private
14652     initQuery : function(){
14653         
14654         var v = this.getRawValue();
14655         
14656         if(this.tickable && this.editable){
14657             v = this.tickableInputEl().getValue();
14658         }
14659         
14660         this.doQuery(v);
14661     },
14662
14663     // private
14664     doForce : function(){
14665         if(this.inputEl().dom.value.length > 0){
14666             this.inputEl().dom.value =
14667                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14668              
14669         }
14670     },
14671
14672     /**
14673      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14674      * query allowing the query action to be canceled if needed.
14675      * @param {String} query The SQL query to execute
14676      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14677      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14678      * saved in the current store (defaults to false)
14679      */
14680     doQuery : function(q, forceAll){
14681         
14682         if(q === undefined || q === null){
14683             q = '';
14684         }
14685         var qe = {
14686             query: q,
14687             forceAll: forceAll,
14688             combo: this,
14689             cancel:false
14690         };
14691         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14692             return false;
14693         }
14694         q = qe.query;
14695         
14696         forceAll = qe.forceAll;
14697         if(forceAll === true || (q.length >= this.minChars)){
14698             
14699             this.hasQuery = true;
14700             
14701             if(this.lastQuery != q || this.alwaysQuery){
14702                 this.lastQuery = q;
14703                 if(this.mode == 'local'){
14704                     this.selectedIndex = -1;
14705                     if(forceAll){
14706                         this.store.clearFilter();
14707                     }else{
14708                         
14709                         if(this.specialFilter){
14710                             this.fireEvent('specialfilter', this);
14711                             this.onLoad();
14712                             return;
14713                         }
14714                         
14715                         this.store.filter(this.displayField, q);
14716                     }
14717                     
14718                     this.store.fireEvent("datachanged", this.store);
14719                     
14720                     this.onLoad();
14721                     
14722                     
14723                 }else{
14724                     
14725                     this.store.baseParams[this.queryParam] = q;
14726                     
14727                     var options = {params : this.getParams(q)};
14728                     
14729                     if(this.loadNext){
14730                         options.add = true;
14731                         options.params.start = this.page * this.pageSize;
14732                     }
14733                     
14734                     this.store.load(options);
14735                     
14736                     /*
14737                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14738                      *  we should expand the list on onLoad
14739                      *  so command out it
14740                      */
14741 //                    this.expand();
14742                 }
14743             }else{
14744                 this.selectedIndex = -1;
14745                 this.onLoad();   
14746             }
14747         }
14748         
14749         this.loadNext = false;
14750     },
14751     
14752     // private
14753     getParams : function(q){
14754         var p = {};
14755         //p[this.queryParam] = q;
14756         
14757         if(this.pageSize){
14758             p.start = 0;
14759             p.limit = this.pageSize;
14760         }
14761         return p;
14762     },
14763
14764     /**
14765      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14766      */
14767     collapse : function(){
14768         if(!this.isExpanded()){
14769             return;
14770         }
14771         
14772         this.list.hide();
14773         
14774         this.hasFocus = false;
14775         
14776         if(this.tickable){
14777             this.okBtn.hide();
14778             this.cancelBtn.hide();
14779             this.trigger.show();
14780             
14781             if(this.editable){
14782                 this.tickableInputEl().dom.value = '';
14783                 this.tickableInputEl().blur();
14784             }
14785             
14786         }
14787         
14788         Roo.get(document).un('mousedown', this.collapseIf, this);
14789         Roo.get(document).un('mousewheel', this.collapseIf, this);
14790         if (!this.editable) {
14791             Roo.get(document).un('keydown', this.listKeyPress, this);
14792         }
14793         this.fireEvent('collapse', this);
14794         
14795         this.validate();
14796     },
14797
14798     // private
14799     collapseIf : function(e){
14800         var in_combo  = e.within(this.el);
14801         var in_list =  e.within(this.list);
14802         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14803         
14804         if (in_combo || in_list || is_list) {
14805             //e.stopPropagation();
14806             return;
14807         }
14808         
14809         if(this.tickable){
14810             this.onTickableFooterButtonClick(e, false, false);
14811         }
14812
14813         this.collapse();
14814         
14815     },
14816
14817     /**
14818      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14819      */
14820     expand : function(){
14821        
14822         if(this.isExpanded() || !this.hasFocus){
14823             return;
14824         }
14825         
14826         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14827         this.list.setWidth(lw);
14828         
14829         Roo.log('expand');
14830         
14831         this.list.show();
14832         
14833         this.restrictHeight();
14834         
14835         if(this.tickable){
14836             
14837             this.tickItems = Roo.apply([], this.item);
14838             
14839             this.okBtn.show();
14840             this.cancelBtn.show();
14841             this.trigger.hide();
14842             
14843             if(this.editable){
14844                 this.tickableInputEl().focus();
14845             }
14846             
14847         }
14848         
14849         Roo.get(document).on('mousedown', this.collapseIf, this);
14850         Roo.get(document).on('mousewheel', this.collapseIf, this);
14851         if (!this.editable) {
14852             Roo.get(document).on('keydown', this.listKeyPress, this);
14853         }
14854         
14855         this.fireEvent('expand', this);
14856     },
14857
14858     // private
14859     // Implements the default empty TriggerField.onTriggerClick function
14860     onTriggerClick : function(e)
14861     {
14862         Roo.log('trigger click');
14863         
14864         if(this.disabled || !this.triggerList){
14865             return;
14866         }
14867         
14868         this.page = 0;
14869         this.loadNext = false;
14870         
14871         if(this.isExpanded()){
14872             this.collapse();
14873             if (!this.blockFocus) {
14874                 this.inputEl().focus();
14875             }
14876             
14877         }else {
14878             this.hasFocus = true;
14879             if(this.triggerAction == 'all') {
14880                 this.doQuery(this.allQuery, true);
14881             } else {
14882                 this.doQuery(this.getRawValue());
14883             }
14884             if (!this.blockFocus) {
14885                 this.inputEl().focus();
14886             }
14887         }
14888     },
14889     
14890     onTickableTriggerClick : function(e)
14891     {
14892         if(this.disabled){
14893             return;
14894         }
14895         
14896         this.page = 0;
14897         this.loadNext = false;
14898         this.hasFocus = true;
14899         
14900         if(this.triggerAction == 'all') {
14901             this.doQuery(this.allQuery, true);
14902         } else {
14903             this.doQuery(this.getRawValue());
14904         }
14905     },
14906     
14907     onSearchFieldClick : function(e)
14908     {
14909         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14910             this.onTickableFooterButtonClick(e, false, false);
14911             return;
14912         }
14913         
14914         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14915             return;
14916         }
14917         
14918         this.page = 0;
14919         this.loadNext = false;
14920         this.hasFocus = true;
14921         
14922         if(this.triggerAction == 'all') {
14923             this.doQuery(this.allQuery, true);
14924         } else {
14925             this.doQuery(this.getRawValue());
14926         }
14927     },
14928     
14929     listKeyPress : function(e)
14930     {
14931         //Roo.log('listkeypress');
14932         // scroll to first matching element based on key pres..
14933         if (e.isSpecialKey()) {
14934             return false;
14935         }
14936         var k = String.fromCharCode(e.getKey()).toUpperCase();
14937         //Roo.log(k);
14938         var match  = false;
14939         var csel = this.view.getSelectedNodes();
14940         var cselitem = false;
14941         if (csel.length) {
14942             var ix = this.view.indexOf(csel[0]);
14943             cselitem  = this.store.getAt(ix);
14944             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14945                 cselitem = false;
14946             }
14947             
14948         }
14949         
14950         this.store.each(function(v) { 
14951             if (cselitem) {
14952                 // start at existing selection.
14953                 if (cselitem.id == v.id) {
14954                     cselitem = false;
14955                 }
14956                 return true;
14957             }
14958                 
14959             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14960                 match = this.store.indexOf(v);
14961                 return false;
14962             }
14963             return true;
14964         }, this);
14965         
14966         if (match === false) {
14967             return true; // no more action?
14968         }
14969         // scroll to?
14970         this.view.select(match);
14971         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14972         sn.scrollIntoView(sn.dom.parentNode, false);
14973     },
14974     
14975     onViewScroll : function(e, t){
14976         
14977         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){
14978             return;
14979         }
14980         
14981         this.hasQuery = true;
14982         
14983         this.loading = this.list.select('.loading', true).first();
14984         
14985         if(this.loading === null){
14986             this.list.createChild({
14987                 tag: 'div',
14988                 cls: 'loading roo-select2-more-results roo-select2-active',
14989                 html: 'Loading more results...'
14990             });
14991             
14992             this.loading = this.list.select('.loading', true).first();
14993             
14994             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14995             
14996             this.loading.hide();
14997         }
14998         
14999         this.loading.show();
15000         
15001         var _combo = this;
15002         
15003         this.page++;
15004         this.loadNext = true;
15005         
15006         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15007         
15008         return;
15009     },
15010     
15011     addItem : function(o)
15012     {   
15013         var dv = ''; // display value
15014         
15015         if (this.displayField) {
15016             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15017         } else {
15018             // this is an error condition!!!
15019             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15020         }
15021         
15022         if(!dv.length){
15023             return;
15024         }
15025         
15026         var choice = this.choices.createChild({
15027             tag: 'li',
15028             cls: 'roo-select2-search-choice',
15029             cn: [
15030                 {
15031                     tag: 'div',
15032                     html: dv
15033                 },
15034                 {
15035                     tag: 'a',
15036                     href: '#',
15037                     cls: 'roo-select2-search-choice-close fa fa-times',
15038                     tabindex: '-1'
15039                 }
15040             ]
15041             
15042         }, this.searchField);
15043         
15044         var close = choice.select('a.roo-select2-search-choice-close', true).first();
15045         
15046         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15047         
15048         this.item.push(o);
15049         
15050         this.lastData = o;
15051         
15052         this.syncValue();
15053         
15054         this.inputEl().dom.value = '';
15055         
15056         this.validate();
15057     },
15058     
15059     onRemoveItem : function(e, _self, o)
15060     {
15061         e.preventDefault();
15062         
15063         this.lastItem = Roo.apply([], this.item);
15064         
15065         var index = this.item.indexOf(o.data) * 1;
15066         
15067         if( index < 0){
15068             Roo.log('not this item?!');
15069             return;
15070         }
15071         
15072         this.item.splice(index, 1);
15073         o.item.remove();
15074         
15075         this.syncValue();
15076         
15077         this.fireEvent('remove', this, e);
15078         
15079         this.validate();
15080         
15081     },
15082     
15083     syncValue : function()
15084     {
15085         if(!this.item.length){
15086             this.clearValue();
15087             return;
15088         }
15089             
15090         var value = [];
15091         var _this = this;
15092         Roo.each(this.item, function(i){
15093             if(_this.valueField){
15094                 value.push(i[_this.valueField]);
15095                 return;
15096             }
15097
15098             value.push(i);
15099         });
15100
15101         this.value = value.join(',');
15102
15103         if(this.hiddenField){
15104             this.hiddenField.dom.value = this.value;
15105         }
15106         
15107         this.store.fireEvent("datachanged", this.store);
15108         
15109         this.validate();
15110     },
15111     
15112     clearItem : function()
15113     {
15114         if(!this.multiple){
15115             return;
15116         }
15117         
15118         this.item = [];
15119         
15120         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15121            c.remove();
15122         });
15123         
15124         this.syncValue();
15125         
15126         this.validate();
15127         
15128         if(this.tickable && !Roo.isTouch){
15129             this.view.refresh();
15130         }
15131     },
15132     
15133     inputEl: function ()
15134     {
15135         if(Roo.isIOS && this.useNativeIOS){
15136             return this.el.select('select.roo-ios-select', true).first();
15137         }
15138         
15139         if(Roo.isTouch && this.mobileTouchView){
15140             return this.el.select('input.form-control',true).first();
15141         }
15142         
15143         if(this.tickable){
15144             return this.searchField;
15145         }
15146         
15147         return this.el.select('input.form-control',true).first();
15148     },
15149     
15150     onTickableFooterButtonClick : function(e, btn, el)
15151     {
15152         e.preventDefault();
15153         
15154         this.lastItem = Roo.apply([], this.item);
15155         
15156         if(btn && btn.name == 'cancel'){
15157             this.tickItems = Roo.apply([], this.item);
15158             this.collapse();
15159             return;
15160         }
15161         
15162         this.clearItem();
15163         
15164         var _this = this;
15165         
15166         Roo.each(this.tickItems, function(o){
15167             _this.addItem(o);
15168         });
15169         
15170         this.collapse();
15171         
15172     },
15173     
15174     validate : function()
15175     {
15176         if(this.getVisibilityEl().hasClass('hidden')){
15177             return true;
15178         }
15179         
15180         var v = this.getRawValue();
15181         
15182         if(this.multiple){
15183             v = this.getValue();
15184         }
15185         
15186         if(this.disabled || this.allowBlank || v.length){
15187             this.markValid();
15188             return true;
15189         }
15190         
15191         this.markInvalid();
15192         return false;
15193     },
15194     
15195     tickableInputEl : function()
15196     {
15197         if(!this.tickable || !this.editable){
15198             return this.inputEl();
15199         }
15200         
15201         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15202     },
15203     
15204     
15205     getAutoCreateTouchView : function()
15206     {
15207         var id = Roo.id();
15208         
15209         var cfg = {
15210             cls: 'form-group' //input-group
15211         };
15212         
15213         var input =  {
15214             tag: 'input',
15215             id : id,
15216             type : this.inputType,
15217             cls : 'form-control x-combo-noedit',
15218             autocomplete: 'new-password',
15219             placeholder : this.placeholder || '',
15220             readonly : true
15221         };
15222         
15223         if (this.name) {
15224             input.name = this.name;
15225         }
15226         
15227         if (this.size) {
15228             input.cls += ' input-' + this.size;
15229         }
15230         
15231         if (this.disabled) {
15232             input.disabled = true;
15233         }
15234         
15235         var inputblock = {
15236             cls : '',
15237             cn : [
15238                 input
15239             ]
15240         };
15241         
15242         if(this.before){
15243             inputblock.cls += ' input-group';
15244             
15245             inputblock.cn.unshift({
15246                 tag :'span',
15247                 cls : 'input-group-addon input-group-prepend input-group-text',
15248                 html : this.before
15249             });
15250         }
15251         
15252         if(this.removable && !this.multiple){
15253             inputblock.cls += ' roo-removable';
15254             
15255             inputblock.cn.push({
15256                 tag: 'button',
15257                 html : 'x',
15258                 cls : 'roo-combo-removable-btn close'
15259             });
15260         }
15261
15262         if(this.hasFeedback && !this.allowBlank){
15263             
15264             inputblock.cls += ' has-feedback';
15265             
15266             inputblock.cn.push({
15267                 tag: 'span',
15268                 cls: 'glyphicon form-control-feedback'
15269             });
15270             
15271         }
15272         
15273         if (this.after) {
15274             
15275             inputblock.cls += (this.before) ? '' : ' input-group';
15276             
15277             inputblock.cn.push({
15278                 tag :'span',
15279                 cls : 'input-group-addon input-group-append input-group-text',
15280                 html : this.after
15281             });
15282         }
15283
15284         
15285         var ibwrap = inputblock;
15286         
15287         if(this.multiple){
15288             ibwrap = {
15289                 tag: 'ul',
15290                 cls: 'roo-select2-choices',
15291                 cn:[
15292                     {
15293                         tag: 'li',
15294                         cls: 'roo-select2-search-field',
15295                         cn: [
15296
15297                             inputblock
15298                         ]
15299                     }
15300                 ]
15301             };
15302         
15303             
15304         }
15305         
15306         var combobox = {
15307             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15308             cn: [
15309                 {
15310                     tag: 'input',
15311                     type : 'hidden',
15312                     cls: 'form-hidden-field'
15313                 },
15314                 ibwrap
15315             ]
15316         };
15317         
15318         if(!this.multiple && this.showToggleBtn){
15319             
15320             var caret = {
15321                         tag: 'span',
15322                         cls: 'caret'
15323             };
15324             
15325             if (this.caret != false) {
15326                 caret = {
15327                      tag: 'i',
15328                      cls: 'fa fa-' + this.caret
15329                 };
15330                 
15331             }
15332             
15333             combobox.cn.push({
15334                 tag :'span',
15335                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15336                 cn : [
15337                     caret,
15338                     {
15339                         tag: 'span',
15340                         cls: 'combobox-clear',
15341                         cn  : [
15342                             {
15343                                 tag : 'i',
15344                                 cls: 'icon-remove'
15345                             }
15346                         ]
15347                     }
15348                 ]
15349
15350             })
15351         }
15352         
15353         if(this.multiple){
15354             combobox.cls += ' roo-select2-container-multi';
15355         }
15356         
15357         var align = this.labelAlign || this.parentLabelAlign();
15358         
15359         if (align ==='left' && this.fieldLabel.length) {
15360
15361             cfg.cn = [
15362                 {
15363                    tag : 'i',
15364                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15365                    tooltip : 'This field is required'
15366                 },
15367                 {
15368                     tag: 'label',
15369                     cls : 'control-label col-form-label',
15370                     html : this.fieldLabel
15371
15372                 },
15373                 {
15374                     cls : '', 
15375                     cn: [
15376                         combobox
15377                     ]
15378                 }
15379             ];
15380             
15381             var labelCfg = cfg.cn[1];
15382             var contentCfg = cfg.cn[2];
15383             
15384
15385             if(this.indicatorpos == 'right'){
15386                 cfg.cn = [
15387                     {
15388                         tag: 'label',
15389                         'for' :  id,
15390                         cls : 'control-label col-form-label',
15391                         cn : [
15392                             {
15393                                 tag : 'span',
15394                                 html : this.fieldLabel
15395                             },
15396                             {
15397                                 tag : 'i',
15398                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15399                                 tooltip : 'This field is required'
15400                             }
15401                         ]
15402                     },
15403                     {
15404                         cls : "",
15405                         cn: [
15406                             combobox
15407                         ]
15408                     }
15409
15410                 ];
15411                 
15412                 labelCfg = cfg.cn[0];
15413                 contentCfg = cfg.cn[1];
15414             }
15415             
15416            
15417             
15418             if(this.labelWidth > 12){
15419                 labelCfg.style = "width: " + this.labelWidth + 'px';
15420             }
15421             
15422             if(this.labelWidth < 13 && this.labelmd == 0){
15423                 this.labelmd = this.labelWidth;
15424             }
15425             
15426             if(this.labellg > 0){
15427                 labelCfg.cls += ' col-lg-' + this.labellg;
15428                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15429             }
15430             
15431             if(this.labelmd > 0){
15432                 labelCfg.cls += ' col-md-' + this.labelmd;
15433                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15434             }
15435             
15436             if(this.labelsm > 0){
15437                 labelCfg.cls += ' col-sm-' + this.labelsm;
15438                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15439             }
15440             
15441             if(this.labelxs > 0){
15442                 labelCfg.cls += ' col-xs-' + this.labelxs;
15443                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15444             }
15445                 
15446                 
15447         } else if ( this.fieldLabel.length) {
15448             cfg.cn = [
15449                 {
15450                    tag : 'i',
15451                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15452                    tooltip : 'This field is required'
15453                 },
15454                 {
15455                     tag: 'label',
15456                     cls : 'control-label',
15457                     html : this.fieldLabel
15458
15459                 },
15460                 {
15461                     cls : '', 
15462                     cn: [
15463                         combobox
15464                     ]
15465                 }
15466             ];
15467             
15468             if(this.indicatorpos == 'right'){
15469                 cfg.cn = [
15470                     {
15471                         tag: 'label',
15472                         cls : 'control-label',
15473                         html : this.fieldLabel,
15474                         cn : [
15475                             {
15476                                tag : 'i',
15477                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15478                                tooltip : 'This field is required'
15479                             }
15480                         ]
15481                     },
15482                     {
15483                         cls : '', 
15484                         cn: [
15485                             combobox
15486                         ]
15487                     }
15488                 ];
15489             }
15490         } else {
15491             cfg.cn = combobox;    
15492         }
15493         
15494         
15495         var settings = this;
15496         
15497         ['xs','sm','md','lg'].map(function(size){
15498             if (settings[size]) {
15499                 cfg.cls += ' col-' + size + '-' + settings[size];
15500             }
15501         });
15502         
15503         return cfg;
15504     },
15505     
15506     initTouchView : function()
15507     {
15508         this.renderTouchView();
15509         
15510         this.touchViewEl.on('scroll', function(){
15511             this.el.dom.scrollTop = 0;
15512         }, this);
15513         
15514         this.originalValue = this.getValue();
15515         
15516         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15517         
15518         this.inputEl().on("click", this.showTouchView, this);
15519         if (this.triggerEl) {
15520             this.triggerEl.on("click", this.showTouchView, this);
15521         }
15522         
15523         
15524         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15525         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15526         
15527         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15528         
15529         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15530         this.store.on('load', this.onTouchViewLoad, this);
15531         this.store.on('loadexception', this.onTouchViewLoadException, this);
15532         
15533         if(this.hiddenName){
15534             
15535             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15536             
15537             this.hiddenField.dom.value =
15538                 this.hiddenValue !== undefined ? this.hiddenValue :
15539                 this.value !== undefined ? this.value : '';
15540         
15541             this.el.dom.removeAttribute('name');
15542             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15543         }
15544         
15545         if(this.multiple){
15546             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15547             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15548         }
15549         
15550         if(this.removable && !this.multiple){
15551             var close = this.closeTriggerEl();
15552             if(close){
15553                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15554                 close.on('click', this.removeBtnClick, this, close);
15555             }
15556         }
15557         /*
15558          * fix the bug in Safari iOS8
15559          */
15560         this.inputEl().on("focus", function(e){
15561             document.activeElement.blur();
15562         }, this);
15563         
15564         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15565         
15566         return;
15567         
15568         
15569     },
15570     
15571     renderTouchView : function()
15572     {
15573         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15574         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15575         
15576         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15577         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15578         
15579         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15580         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15581         this.touchViewBodyEl.setStyle('overflow', 'auto');
15582         
15583         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15584         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15585         
15586         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15587         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15588         
15589     },
15590     
15591     showTouchView : function()
15592     {
15593         if(this.disabled){
15594             return;
15595         }
15596         
15597         this.touchViewHeaderEl.hide();
15598
15599         if(this.modalTitle.length){
15600             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15601             this.touchViewHeaderEl.show();
15602         }
15603
15604         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15605         this.touchViewEl.show();
15606
15607         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15608         
15609         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15610         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15611
15612         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15613
15614         if(this.modalTitle.length){
15615             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15616         }
15617         
15618         this.touchViewBodyEl.setHeight(bodyHeight);
15619
15620         if(this.animate){
15621             var _this = this;
15622             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15623         }else{
15624             this.touchViewEl.addClass('in');
15625         }
15626         
15627         if(this._touchViewMask){
15628             Roo.get(document.body).addClass("x-body-masked");
15629             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15630             this._touchViewMask.setStyle('z-index', 10000);
15631             this._touchViewMask.addClass('show');
15632         }
15633         
15634         this.doTouchViewQuery();
15635         
15636     },
15637     
15638     hideTouchView : function()
15639     {
15640         this.touchViewEl.removeClass('in');
15641
15642         if(this.animate){
15643             var _this = this;
15644             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15645         }else{
15646             this.touchViewEl.setStyle('display', 'none');
15647         }
15648         
15649         if(this._touchViewMask){
15650             this._touchViewMask.removeClass('show');
15651             Roo.get(document.body).removeClass("x-body-masked");
15652         }
15653     },
15654     
15655     setTouchViewValue : function()
15656     {
15657         if(this.multiple){
15658             this.clearItem();
15659         
15660             var _this = this;
15661
15662             Roo.each(this.tickItems, function(o){
15663                 this.addItem(o);
15664             }, this);
15665         }
15666         
15667         this.hideTouchView();
15668     },
15669     
15670     doTouchViewQuery : function()
15671     {
15672         var qe = {
15673             query: '',
15674             forceAll: true,
15675             combo: this,
15676             cancel:false
15677         };
15678         
15679         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15680             return false;
15681         }
15682         
15683         if(!this.alwaysQuery || this.mode == 'local'){
15684             this.onTouchViewLoad();
15685             return;
15686         }
15687         
15688         this.store.load();
15689     },
15690     
15691     onTouchViewBeforeLoad : function(combo,opts)
15692     {
15693         return;
15694     },
15695
15696     // private
15697     onTouchViewLoad : function()
15698     {
15699         if(this.store.getCount() < 1){
15700             this.onTouchViewEmptyResults();
15701             return;
15702         }
15703         
15704         this.clearTouchView();
15705         
15706         var rawValue = this.getRawValue();
15707         
15708         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15709         
15710         this.tickItems = [];
15711         
15712         this.store.data.each(function(d, rowIndex){
15713             var row = this.touchViewListGroup.createChild(template);
15714             
15715             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15716                 row.addClass(d.data.cls);
15717             }
15718             
15719             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15720                 var cfg = {
15721                     data : d.data,
15722                     html : d.data[this.displayField]
15723                 };
15724                 
15725                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15726                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15727                 }
15728             }
15729             row.removeClass('selected');
15730             if(!this.multiple && this.valueField &&
15731                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15732             {
15733                 // radio buttons..
15734                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15735                 row.addClass('selected');
15736             }
15737             
15738             if(this.multiple && this.valueField &&
15739                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15740             {
15741                 
15742                 // checkboxes...
15743                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15744                 this.tickItems.push(d.data);
15745             }
15746             
15747             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15748             
15749         }, this);
15750         
15751         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15752         
15753         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15754
15755         if(this.modalTitle.length){
15756             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15757         }
15758
15759         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15760         
15761         if(this.mobile_restrict_height && listHeight < bodyHeight){
15762             this.touchViewBodyEl.setHeight(listHeight);
15763         }
15764         
15765         var _this = this;
15766         
15767         if(firstChecked && listHeight > bodyHeight){
15768             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15769         }
15770         
15771     },
15772     
15773     onTouchViewLoadException : function()
15774     {
15775         this.hideTouchView();
15776     },
15777     
15778     onTouchViewEmptyResults : function()
15779     {
15780         this.clearTouchView();
15781         
15782         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15783         
15784         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15785         
15786     },
15787     
15788     clearTouchView : function()
15789     {
15790         this.touchViewListGroup.dom.innerHTML = '';
15791     },
15792     
15793     onTouchViewClick : function(e, el, o)
15794     {
15795         e.preventDefault();
15796         
15797         var row = o.row;
15798         var rowIndex = o.rowIndex;
15799         
15800         var r = this.store.getAt(rowIndex);
15801         
15802         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15803             
15804             if(!this.multiple){
15805                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15806                     c.dom.removeAttribute('checked');
15807                 }, this);
15808
15809                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15810
15811                 this.setFromData(r.data);
15812
15813                 var close = this.closeTriggerEl();
15814
15815                 if(close){
15816                     close.show();
15817                 }
15818
15819                 this.hideTouchView();
15820
15821                 this.fireEvent('select', this, r, rowIndex);
15822
15823                 return;
15824             }
15825
15826             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15827                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15828                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15829                 return;
15830             }
15831
15832             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15833             this.addItem(r.data);
15834             this.tickItems.push(r.data);
15835         }
15836     },
15837     
15838     getAutoCreateNativeIOS : function()
15839     {
15840         var cfg = {
15841             cls: 'form-group' //input-group,
15842         };
15843         
15844         var combobox =  {
15845             tag: 'select',
15846             cls : 'roo-ios-select'
15847         };
15848         
15849         if (this.name) {
15850             combobox.name = this.name;
15851         }
15852         
15853         if (this.disabled) {
15854             combobox.disabled = true;
15855         }
15856         
15857         var settings = this;
15858         
15859         ['xs','sm','md','lg'].map(function(size){
15860             if (settings[size]) {
15861                 cfg.cls += ' col-' + size + '-' + settings[size];
15862             }
15863         });
15864         
15865         cfg.cn = combobox;
15866         
15867         return cfg;
15868         
15869     },
15870     
15871     initIOSView : function()
15872     {
15873         this.store.on('load', this.onIOSViewLoad, this);
15874         
15875         return;
15876     },
15877     
15878     onIOSViewLoad : function()
15879     {
15880         if(this.store.getCount() < 1){
15881             return;
15882         }
15883         
15884         this.clearIOSView();
15885         
15886         if(this.allowBlank) {
15887             
15888             var default_text = '-- SELECT --';
15889             
15890             if(this.placeholder.length){
15891                 default_text = this.placeholder;
15892             }
15893             
15894             if(this.emptyTitle.length){
15895                 default_text += ' - ' + this.emptyTitle + ' -';
15896             }
15897             
15898             var opt = this.inputEl().createChild({
15899                 tag: 'option',
15900                 value : 0,
15901                 html : default_text
15902             });
15903             
15904             var o = {};
15905             o[this.valueField] = 0;
15906             o[this.displayField] = default_text;
15907             
15908             this.ios_options.push({
15909                 data : o,
15910                 el : opt
15911             });
15912             
15913         }
15914         
15915         this.store.data.each(function(d, rowIndex){
15916             
15917             var html = '';
15918             
15919             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15920                 html = d.data[this.displayField];
15921             }
15922             
15923             var value = '';
15924             
15925             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15926                 value = d.data[this.valueField];
15927             }
15928             
15929             var option = {
15930                 tag: 'option',
15931                 value : value,
15932                 html : html
15933             };
15934             
15935             if(this.value == d.data[this.valueField]){
15936                 option['selected'] = true;
15937             }
15938             
15939             var opt = this.inputEl().createChild(option);
15940             
15941             this.ios_options.push({
15942                 data : d.data,
15943                 el : opt
15944             });
15945             
15946         }, this);
15947         
15948         this.inputEl().on('change', function(){
15949            this.fireEvent('select', this);
15950         }, this);
15951         
15952     },
15953     
15954     clearIOSView: function()
15955     {
15956         this.inputEl().dom.innerHTML = '';
15957         
15958         this.ios_options = [];
15959     },
15960     
15961     setIOSValue: function(v)
15962     {
15963         this.value = v;
15964         
15965         if(!this.ios_options){
15966             return;
15967         }
15968         
15969         Roo.each(this.ios_options, function(opts){
15970            
15971            opts.el.dom.removeAttribute('selected');
15972            
15973            if(opts.data[this.valueField] != v){
15974                return;
15975            }
15976            
15977            opts.el.dom.setAttribute('selected', true);
15978            
15979         }, this);
15980     }
15981
15982     /** 
15983     * @cfg {Boolean} grow 
15984     * @hide 
15985     */
15986     /** 
15987     * @cfg {Number} growMin 
15988     * @hide 
15989     */
15990     /** 
15991     * @cfg {Number} growMax 
15992     * @hide 
15993     */
15994     /**
15995      * @hide
15996      * @method autoSize
15997      */
15998 });
15999
16000 Roo.apply(Roo.bootstrap.ComboBox,  {
16001     
16002     header : {
16003         tag: 'div',
16004         cls: 'modal-header',
16005         cn: [
16006             {
16007                 tag: 'h4',
16008                 cls: 'modal-title'
16009             }
16010         ]
16011     },
16012     
16013     body : {
16014         tag: 'div',
16015         cls: 'modal-body',
16016         cn: [
16017             {
16018                 tag: 'ul',
16019                 cls: 'list-group'
16020             }
16021         ]
16022     },
16023     
16024     listItemRadio : {
16025         tag: 'li',
16026         cls: 'list-group-item',
16027         cn: [
16028             {
16029                 tag: 'span',
16030                 cls: 'roo-combobox-list-group-item-value'
16031             },
16032             {
16033                 tag: 'div',
16034                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16035                 cn: [
16036                     {
16037                         tag: 'input',
16038                         type: 'radio'
16039                     },
16040                     {
16041                         tag: 'label'
16042                     }
16043                 ]
16044             }
16045         ]
16046     },
16047     
16048     listItemCheckbox : {
16049         tag: 'li',
16050         cls: 'list-group-item',
16051         cn: [
16052             {
16053                 tag: 'span',
16054                 cls: 'roo-combobox-list-group-item-value'
16055             },
16056             {
16057                 tag: 'div',
16058                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16059                 cn: [
16060                     {
16061                         tag: 'input',
16062                         type: 'checkbox'
16063                     },
16064                     {
16065                         tag: 'label'
16066                     }
16067                 ]
16068             }
16069         ]
16070     },
16071     
16072     emptyResult : {
16073         tag: 'div',
16074         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16075     },
16076     
16077     footer : {
16078         tag: 'div',
16079         cls: 'modal-footer',
16080         cn: [
16081             {
16082                 tag: 'div',
16083                 cls: 'row',
16084                 cn: [
16085                     {
16086                         tag: 'div',
16087                         cls: 'col-xs-6 text-left',
16088                         cn: {
16089                             tag: 'button',
16090                             cls: 'btn btn-danger roo-touch-view-cancel',
16091                             html: 'Cancel'
16092                         }
16093                     },
16094                     {
16095                         tag: 'div',
16096                         cls: 'col-xs-6 text-right',
16097                         cn: {
16098                             tag: 'button',
16099                             cls: 'btn btn-success roo-touch-view-ok',
16100                             html: 'OK'
16101                         }
16102                     }
16103                 ]
16104             }
16105         ]
16106         
16107     }
16108 });
16109
16110 Roo.apply(Roo.bootstrap.ComboBox,  {
16111     
16112     touchViewTemplate : {
16113         tag: 'div',
16114         cls: 'modal fade roo-combobox-touch-view',
16115         cn: [
16116             {
16117                 tag: 'div',
16118                 cls: 'modal-dialog',
16119                 style : 'position:fixed', // we have to fix position....
16120                 cn: [
16121                     {
16122                         tag: 'div',
16123                         cls: 'modal-content',
16124                         cn: [
16125                             Roo.bootstrap.ComboBox.header,
16126                             Roo.bootstrap.ComboBox.body,
16127                             Roo.bootstrap.ComboBox.footer
16128                         ]
16129                     }
16130                 ]
16131             }
16132         ]
16133     }
16134 });/*
16135  * Based on:
16136  * Ext JS Library 1.1.1
16137  * Copyright(c) 2006-2007, Ext JS, LLC.
16138  *
16139  * Originally Released Under LGPL - original licence link has changed is not relivant.
16140  *
16141  * Fork - LGPL
16142  * <script type="text/javascript">
16143  */
16144
16145 /**
16146  * @class Roo.View
16147  * @extends Roo.util.Observable
16148  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16149  * This class also supports single and multi selection modes. <br>
16150  * Create a data model bound view:
16151  <pre><code>
16152  var store = new Roo.data.Store(...);
16153
16154  var view = new Roo.View({
16155     el : "my-element",
16156     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16157  
16158     singleSelect: true,
16159     selectedClass: "ydataview-selected",
16160     store: store
16161  });
16162
16163  // listen for node click?
16164  view.on("click", function(vw, index, node, e){
16165  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16166  });
16167
16168  // load XML data
16169  dataModel.load("foobar.xml");
16170  </code></pre>
16171  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16172  * <br><br>
16173  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16174  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16175  * 
16176  * Note: old style constructor is still suported (container, template, config)
16177  * 
16178  * @constructor
16179  * Create a new View
16180  * @param {Object} config The config object
16181  * 
16182  */
16183 Roo.View = function(config, depreciated_tpl, depreciated_config){
16184     
16185     this.parent = false;
16186     
16187     if (typeof(depreciated_tpl) == 'undefined') {
16188         // new way.. - universal constructor.
16189         Roo.apply(this, config);
16190         this.el  = Roo.get(this.el);
16191     } else {
16192         // old format..
16193         this.el  = Roo.get(config);
16194         this.tpl = depreciated_tpl;
16195         Roo.apply(this, depreciated_config);
16196     }
16197     this.wrapEl  = this.el.wrap().wrap();
16198     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16199     
16200     
16201     if(typeof(this.tpl) == "string"){
16202         this.tpl = new Roo.Template(this.tpl);
16203     } else {
16204         // support xtype ctors..
16205         this.tpl = new Roo.factory(this.tpl, Roo);
16206     }
16207     
16208     
16209     this.tpl.compile();
16210     
16211     /** @private */
16212     this.addEvents({
16213         /**
16214          * @event beforeclick
16215          * Fires before a click is processed. Returns false to cancel the default action.
16216          * @param {Roo.View} this
16217          * @param {Number} index The index of the target node
16218          * @param {HTMLElement} node The target node
16219          * @param {Roo.EventObject} e The raw event object
16220          */
16221             "beforeclick" : true,
16222         /**
16223          * @event click
16224          * Fires when a template node is clicked.
16225          * @param {Roo.View} this
16226          * @param {Number} index The index of the target node
16227          * @param {HTMLElement} node The target node
16228          * @param {Roo.EventObject} e The raw event object
16229          */
16230             "click" : true,
16231         /**
16232          * @event dblclick
16233          * Fires when a template node is double clicked.
16234          * @param {Roo.View} this
16235          * @param {Number} index The index of the target node
16236          * @param {HTMLElement} node The target node
16237          * @param {Roo.EventObject} e The raw event object
16238          */
16239             "dblclick" : true,
16240         /**
16241          * @event contextmenu
16242          * Fires when a template node is right clicked.
16243          * @param {Roo.View} this
16244          * @param {Number} index The index of the target node
16245          * @param {HTMLElement} node The target node
16246          * @param {Roo.EventObject} e The raw event object
16247          */
16248             "contextmenu" : true,
16249         /**
16250          * @event selectionchange
16251          * Fires when the selected nodes change.
16252          * @param {Roo.View} this
16253          * @param {Array} selections Array of the selected nodes
16254          */
16255             "selectionchange" : true,
16256     
16257         /**
16258          * @event beforeselect
16259          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16260          * @param {Roo.View} this
16261          * @param {HTMLElement} node The node to be selected
16262          * @param {Array} selections Array of currently selected nodes
16263          */
16264             "beforeselect" : true,
16265         /**
16266          * @event preparedata
16267          * Fires on every row to render, to allow you to change the data.
16268          * @param {Roo.View} this
16269          * @param {Object} data to be rendered (change this)
16270          */
16271           "preparedata" : true
16272           
16273           
16274         });
16275
16276
16277
16278     this.el.on({
16279         "click": this.onClick,
16280         "dblclick": this.onDblClick,
16281         "contextmenu": this.onContextMenu,
16282         scope:this
16283     });
16284
16285     this.selections = [];
16286     this.nodes = [];
16287     this.cmp = new Roo.CompositeElementLite([]);
16288     if(this.store){
16289         this.store = Roo.factory(this.store, Roo.data);
16290         this.setStore(this.store, true);
16291     }
16292     
16293     if ( this.footer && this.footer.xtype) {
16294            
16295          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16296         
16297         this.footer.dataSource = this.store;
16298         this.footer.container = fctr;
16299         this.footer = Roo.factory(this.footer, Roo);
16300         fctr.insertFirst(this.el);
16301         
16302         // this is a bit insane - as the paging toolbar seems to detach the el..
16303 //        dom.parentNode.parentNode.parentNode
16304          // they get detached?
16305     }
16306     
16307     
16308     Roo.View.superclass.constructor.call(this);
16309     
16310     
16311 };
16312
16313 Roo.extend(Roo.View, Roo.util.Observable, {
16314     
16315      /**
16316      * @cfg {Roo.data.Store} store Data store to load data from.
16317      */
16318     store : false,
16319     
16320     /**
16321      * @cfg {String|Roo.Element} el The container element.
16322      */
16323     el : '',
16324     
16325     /**
16326      * @cfg {String|Roo.Template} tpl The template used by this View 
16327      */
16328     tpl : false,
16329     /**
16330      * @cfg {String} dataName the named area of the template to use as the data area
16331      *                          Works with domtemplates roo-name="name"
16332      */
16333     dataName: false,
16334     /**
16335      * @cfg {String} selectedClass The css class to add to selected nodes
16336      */
16337     selectedClass : "x-view-selected",
16338      /**
16339      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16340      */
16341     emptyText : "",
16342     
16343     /**
16344      * @cfg {String} text to display on mask (default Loading)
16345      */
16346     mask : false,
16347     /**
16348      * @cfg {Boolean} multiSelect Allow multiple selection
16349      */
16350     multiSelect : false,
16351     /**
16352      * @cfg {Boolean} singleSelect Allow single selection
16353      */
16354     singleSelect:  false,
16355     
16356     /**
16357      * @cfg {Boolean} toggleSelect - selecting 
16358      */
16359     toggleSelect : false,
16360     
16361     /**
16362      * @cfg {Boolean} tickable - selecting 
16363      */
16364     tickable : false,
16365     
16366     /**
16367      * Returns the element this view is bound to.
16368      * @return {Roo.Element}
16369      */
16370     getEl : function(){
16371         return this.wrapEl;
16372     },
16373     
16374     
16375
16376     /**
16377      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16378      */
16379     refresh : function(){
16380         //Roo.log('refresh');
16381         var t = this.tpl;
16382         
16383         // if we are using something like 'domtemplate', then
16384         // the what gets used is:
16385         // t.applySubtemplate(NAME, data, wrapping data..)
16386         // the outer template then get' applied with
16387         //     the store 'extra data'
16388         // and the body get's added to the
16389         //      roo-name="data" node?
16390         //      <span class='roo-tpl-{name}'></span> ?????
16391         
16392         
16393         
16394         this.clearSelections();
16395         this.el.update("");
16396         var html = [];
16397         var records = this.store.getRange();
16398         if(records.length < 1) {
16399             
16400             // is this valid??  = should it render a template??
16401             
16402             this.el.update(this.emptyText);
16403             return;
16404         }
16405         var el = this.el;
16406         if (this.dataName) {
16407             this.el.update(t.apply(this.store.meta)); //????
16408             el = this.el.child('.roo-tpl-' + this.dataName);
16409         }
16410         
16411         for(var i = 0, len = records.length; i < len; i++){
16412             var data = this.prepareData(records[i].data, i, records[i]);
16413             this.fireEvent("preparedata", this, data, i, records[i]);
16414             
16415             var d = Roo.apply({}, data);
16416             
16417             if(this.tickable){
16418                 Roo.apply(d, {'roo-id' : Roo.id()});
16419                 
16420                 var _this = this;
16421             
16422                 Roo.each(this.parent.item, function(item){
16423                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16424                         return;
16425                     }
16426                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16427                 });
16428             }
16429             
16430             html[html.length] = Roo.util.Format.trim(
16431                 this.dataName ?
16432                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16433                     t.apply(d)
16434             );
16435         }
16436         
16437         
16438         
16439         el.update(html.join(""));
16440         this.nodes = el.dom.childNodes;
16441         this.updateIndexes(0);
16442     },
16443     
16444
16445     /**
16446      * Function to override to reformat the data that is sent to
16447      * the template for each node.
16448      * DEPRICATED - use the preparedata event handler.
16449      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16450      * a JSON object for an UpdateManager bound view).
16451      */
16452     prepareData : function(data, index, record)
16453     {
16454         this.fireEvent("preparedata", this, data, index, record);
16455         return data;
16456     },
16457
16458     onUpdate : function(ds, record){
16459         // Roo.log('on update');   
16460         this.clearSelections();
16461         var index = this.store.indexOf(record);
16462         var n = this.nodes[index];
16463         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16464         n.parentNode.removeChild(n);
16465         this.updateIndexes(index, index);
16466     },
16467
16468     
16469     
16470 // --------- FIXME     
16471     onAdd : function(ds, records, index)
16472     {
16473         //Roo.log(['on Add', ds, records, index] );        
16474         this.clearSelections();
16475         if(this.nodes.length == 0){
16476             this.refresh();
16477             return;
16478         }
16479         var n = this.nodes[index];
16480         for(var i = 0, len = records.length; i < len; i++){
16481             var d = this.prepareData(records[i].data, i, records[i]);
16482             if(n){
16483                 this.tpl.insertBefore(n, d);
16484             }else{
16485                 
16486                 this.tpl.append(this.el, d);
16487             }
16488         }
16489         this.updateIndexes(index);
16490     },
16491
16492     onRemove : function(ds, record, index){
16493        // Roo.log('onRemove');
16494         this.clearSelections();
16495         var el = this.dataName  ?
16496             this.el.child('.roo-tpl-' + this.dataName) :
16497             this.el; 
16498         
16499         el.dom.removeChild(this.nodes[index]);
16500         this.updateIndexes(index);
16501     },
16502
16503     /**
16504      * Refresh an individual node.
16505      * @param {Number} index
16506      */
16507     refreshNode : function(index){
16508         this.onUpdate(this.store, this.store.getAt(index));
16509     },
16510
16511     updateIndexes : function(startIndex, endIndex){
16512         var ns = this.nodes;
16513         startIndex = startIndex || 0;
16514         endIndex = endIndex || ns.length - 1;
16515         for(var i = startIndex; i <= endIndex; i++){
16516             ns[i].nodeIndex = i;
16517         }
16518     },
16519
16520     /**
16521      * Changes the data store this view uses and refresh the view.
16522      * @param {Store} store
16523      */
16524     setStore : function(store, initial){
16525         if(!initial && this.store){
16526             this.store.un("datachanged", this.refresh);
16527             this.store.un("add", this.onAdd);
16528             this.store.un("remove", this.onRemove);
16529             this.store.un("update", this.onUpdate);
16530             this.store.un("clear", this.refresh);
16531             this.store.un("beforeload", this.onBeforeLoad);
16532             this.store.un("load", this.onLoad);
16533             this.store.un("loadexception", this.onLoad);
16534         }
16535         if(store){
16536           
16537             store.on("datachanged", this.refresh, this);
16538             store.on("add", this.onAdd, this);
16539             store.on("remove", this.onRemove, this);
16540             store.on("update", this.onUpdate, this);
16541             store.on("clear", this.refresh, this);
16542             store.on("beforeload", this.onBeforeLoad, this);
16543             store.on("load", this.onLoad, this);
16544             store.on("loadexception", this.onLoad, this);
16545         }
16546         
16547         if(store){
16548             this.refresh();
16549         }
16550     },
16551     /**
16552      * onbeforeLoad - masks the loading area.
16553      *
16554      */
16555     onBeforeLoad : function(store,opts)
16556     {
16557          //Roo.log('onBeforeLoad');   
16558         if (!opts.add) {
16559             this.el.update("");
16560         }
16561         this.el.mask(this.mask ? this.mask : "Loading" ); 
16562     },
16563     onLoad : function ()
16564     {
16565         this.el.unmask();
16566     },
16567     
16568
16569     /**
16570      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16571      * @param {HTMLElement} node
16572      * @return {HTMLElement} The template node
16573      */
16574     findItemFromChild : function(node){
16575         var el = this.dataName  ?
16576             this.el.child('.roo-tpl-' + this.dataName,true) :
16577             this.el.dom; 
16578         
16579         if(!node || node.parentNode == el){
16580                     return node;
16581             }
16582             var p = node.parentNode;
16583             while(p && p != el){
16584             if(p.parentNode == el){
16585                 return p;
16586             }
16587             p = p.parentNode;
16588         }
16589             return null;
16590     },
16591
16592     /** @ignore */
16593     onClick : function(e){
16594         var item = this.findItemFromChild(e.getTarget());
16595         if(item){
16596             var index = this.indexOf(item);
16597             if(this.onItemClick(item, index, e) !== false){
16598                 this.fireEvent("click", this, index, item, e);
16599             }
16600         }else{
16601             this.clearSelections();
16602         }
16603     },
16604
16605     /** @ignore */
16606     onContextMenu : function(e){
16607         var item = this.findItemFromChild(e.getTarget());
16608         if(item){
16609             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16610         }
16611     },
16612
16613     /** @ignore */
16614     onDblClick : function(e){
16615         var item = this.findItemFromChild(e.getTarget());
16616         if(item){
16617             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16618         }
16619     },
16620
16621     onItemClick : function(item, index, e)
16622     {
16623         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16624             return false;
16625         }
16626         if (this.toggleSelect) {
16627             var m = this.isSelected(item) ? 'unselect' : 'select';
16628             //Roo.log(m);
16629             var _t = this;
16630             _t[m](item, true, false);
16631             return true;
16632         }
16633         if(this.multiSelect || this.singleSelect){
16634             if(this.multiSelect && e.shiftKey && this.lastSelection){
16635                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16636             }else{
16637                 this.select(item, this.multiSelect && e.ctrlKey);
16638                 this.lastSelection = item;
16639             }
16640             
16641             if(!this.tickable){
16642                 e.preventDefault();
16643             }
16644             
16645         }
16646         return true;
16647     },
16648
16649     /**
16650      * Get the number of selected nodes.
16651      * @return {Number}
16652      */
16653     getSelectionCount : function(){
16654         return this.selections.length;
16655     },
16656
16657     /**
16658      * Get the currently selected nodes.
16659      * @return {Array} An array of HTMLElements
16660      */
16661     getSelectedNodes : function(){
16662         return this.selections;
16663     },
16664
16665     /**
16666      * Get the indexes of the selected nodes.
16667      * @return {Array}
16668      */
16669     getSelectedIndexes : function(){
16670         var indexes = [], s = this.selections;
16671         for(var i = 0, len = s.length; i < len; i++){
16672             indexes.push(s[i].nodeIndex);
16673         }
16674         return indexes;
16675     },
16676
16677     /**
16678      * Clear all selections
16679      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16680      */
16681     clearSelections : function(suppressEvent){
16682         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16683             this.cmp.elements = this.selections;
16684             this.cmp.removeClass(this.selectedClass);
16685             this.selections = [];
16686             if(!suppressEvent){
16687                 this.fireEvent("selectionchange", this, this.selections);
16688             }
16689         }
16690     },
16691
16692     /**
16693      * Returns true if the passed node is selected
16694      * @param {HTMLElement/Number} node The node or node index
16695      * @return {Boolean}
16696      */
16697     isSelected : function(node){
16698         var s = this.selections;
16699         if(s.length < 1){
16700             return false;
16701         }
16702         node = this.getNode(node);
16703         return s.indexOf(node) !== -1;
16704     },
16705
16706     /**
16707      * Selects nodes.
16708      * @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
16709      * @param {Boolean} keepExisting (optional) true to keep existing selections
16710      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16711      */
16712     select : function(nodeInfo, keepExisting, suppressEvent){
16713         if(nodeInfo instanceof Array){
16714             if(!keepExisting){
16715                 this.clearSelections(true);
16716             }
16717             for(var i = 0, len = nodeInfo.length; i < len; i++){
16718                 this.select(nodeInfo[i], true, true);
16719             }
16720             return;
16721         } 
16722         var node = this.getNode(nodeInfo);
16723         if(!node || this.isSelected(node)){
16724             return; // already selected.
16725         }
16726         if(!keepExisting){
16727             this.clearSelections(true);
16728         }
16729         
16730         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16731             Roo.fly(node).addClass(this.selectedClass);
16732             this.selections.push(node);
16733             if(!suppressEvent){
16734                 this.fireEvent("selectionchange", this, this.selections);
16735             }
16736         }
16737         
16738         
16739     },
16740       /**
16741      * Unselects nodes.
16742      * @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
16743      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16744      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16745      */
16746     unselect : function(nodeInfo, keepExisting, suppressEvent)
16747     {
16748         if(nodeInfo instanceof Array){
16749             Roo.each(this.selections, function(s) {
16750                 this.unselect(s, nodeInfo);
16751             }, this);
16752             return;
16753         }
16754         var node = this.getNode(nodeInfo);
16755         if(!node || !this.isSelected(node)){
16756             //Roo.log("not selected");
16757             return; // not selected.
16758         }
16759         // fireevent???
16760         var ns = [];
16761         Roo.each(this.selections, function(s) {
16762             if (s == node ) {
16763                 Roo.fly(node).removeClass(this.selectedClass);
16764
16765                 return;
16766             }
16767             ns.push(s);
16768         },this);
16769         
16770         this.selections= ns;
16771         this.fireEvent("selectionchange", this, this.selections);
16772     },
16773
16774     /**
16775      * Gets a template node.
16776      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16777      * @return {HTMLElement} The node or null if it wasn't found
16778      */
16779     getNode : function(nodeInfo){
16780         if(typeof nodeInfo == "string"){
16781             return document.getElementById(nodeInfo);
16782         }else if(typeof nodeInfo == "number"){
16783             return this.nodes[nodeInfo];
16784         }
16785         return nodeInfo;
16786     },
16787
16788     /**
16789      * Gets a range template nodes.
16790      * @param {Number} startIndex
16791      * @param {Number} endIndex
16792      * @return {Array} An array of nodes
16793      */
16794     getNodes : function(start, end){
16795         var ns = this.nodes;
16796         start = start || 0;
16797         end = typeof end == "undefined" ? ns.length - 1 : end;
16798         var nodes = [];
16799         if(start <= end){
16800             for(var i = start; i <= end; i++){
16801                 nodes.push(ns[i]);
16802             }
16803         } else{
16804             for(var i = start; i >= end; i--){
16805                 nodes.push(ns[i]);
16806             }
16807         }
16808         return nodes;
16809     },
16810
16811     /**
16812      * Finds the index of the passed node
16813      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16814      * @return {Number} The index of the node or -1
16815      */
16816     indexOf : function(node){
16817         node = this.getNode(node);
16818         if(typeof node.nodeIndex == "number"){
16819             return node.nodeIndex;
16820         }
16821         var ns = this.nodes;
16822         for(var i = 0, len = ns.length; i < len; i++){
16823             if(ns[i] == node){
16824                 return i;
16825             }
16826         }
16827         return -1;
16828     }
16829 });
16830 /*
16831  * - LGPL
16832  *
16833  * based on jquery fullcalendar
16834  * 
16835  */
16836
16837 Roo.bootstrap = Roo.bootstrap || {};
16838 /**
16839  * @class Roo.bootstrap.Calendar
16840  * @extends Roo.bootstrap.Component
16841  * Bootstrap Calendar class
16842  * @cfg {Boolean} loadMask (true|false) default false
16843  * @cfg {Object} header generate the user specific header of the calendar, default false
16844
16845  * @constructor
16846  * Create a new Container
16847  * @param {Object} config The config object
16848  */
16849
16850
16851
16852 Roo.bootstrap.Calendar = function(config){
16853     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16854      this.addEvents({
16855         /**
16856              * @event select
16857              * Fires when a date is selected
16858              * @param {DatePicker} this
16859              * @param {Date} date The selected date
16860              */
16861         'select': true,
16862         /**
16863              * @event monthchange
16864              * Fires when the displayed month changes 
16865              * @param {DatePicker} this
16866              * @param {Date} date The selected month
16867              */
16868         'monthchange': true,
16869         /**
16870              * @event evententer
16871              * Fires when mouse over an event
16872              * @param {Calendar} this
16873              * @param {event} Event
16874              */
16875         'evententer': true,
16876         /**
16877              * @event eventleave
16878              * Fires when the mouse leaves an
16879              * @param {Calendar} this
16880              * @param {event}
16881              */
16882         'eventleave': true,
16883         /**
16884              * @event eventclick
16885              * Fires when the mouse click an
16886              * @param {Calendar} this
16887              * @param {event}
16888              */
16889         'eventclick': true
16890         
16891     });
16892
16893 };
16894
16895 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16896     
16897      /**
16898      * @cfg {Number} startDay
16899      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16900      */
16901     startDay : 0,
16902     
16903     loadMask : false,
16904     
16905     header : false,
16906       
16907     getAutoCreate : function(){
16908         
16909         
16910         var fc_button = function(name, corner, style, content ) {
16911             return Roo.apply({},{
16912                 tag : 'span',
16913                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16914                          (corner.length ?
16915                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16916                             ''
16917                         ),
16918                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16919                 unselectable: 'on'
16920             });
16921         };
16922         
16923         var header = {};
16924         
16925         if(!this.header){
16926             header = {
16927                 tag : 'table',
16928                 cls : 'fc-header',
16929                 style : 'width:100%',
16930                 cn : [
16931                     {
16932                         tag: 'tr',
16933                         cn : [
16934                             {
16935                                 tag : 'td',
16936                                 cls : 'fc-header-left',
16937                                 cn : [
16938                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16939                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16940                                     { tag: 'span', cls: 'fc-header-space' },
16941                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16942
16943
16944                                 ]
16945                             },
16946
16947                             {
16948                                 tag : 'td',
16949                                 cls : 'fc-header-center',
16950                                 cn : [
16951                                     {
16952                                         tag: 'span',
16953                                         cls: 'fc-header-title',
16954                                         cn : {
16955                                             tag: 'H2',
16956                                             html : 'month / year'
16957                                         }
16958                                     }
16959
16960                                 ]
16961                             },
16962                             {
16963                                 tag : 'td',
16964                                 cls : 'fc-header-right',
16965                                 cn : [
16966                               /*      fc_button('month', 'left', '', 'month' ),
16967                                     fc_button('week', '', '', 'week' ),
16968                                     fc_button('day', 'right', '', 'day' )
16969                                 */    
16970
16971                                 ]
16972                             }
16973
16974                         ]
16975                     }
16976                 ]
16977             };
16978         }
16979         
16980         header = this.header;
16981         
16982        
16983         var cal_heads = function() {
16984             var ret = [];
16985             // fixme - handle this.
16986             
16987             for (var i =0; i < Date.dayNames.length; i++) {
16988                 var d = Date.dayNames[i];
16989                 ret.push({
16990                     tag: 'th',
16991                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16992                     html : d.substring(0,3)
16993                 });
16994                 
16995             }
16996             ret[0].cls += ' fc-first';
16997             ret[6].cls += ' fc-last';
16998             return ret;
16999         };
17000         var cal_cell = function(n) {
17001             return  {
17002                 tag: 'td',
17003                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17004                 cn : [
17005                     {
17006                         cn : [
17007                             {
17008                                 cls: 'fc-day-number',
17009                                 html: 'D'
17010                             },
17011                             {
17012                                 cls: 'fc-day-content',
17013                              
17014                                 cn : [
17015                                      {
17016                                         style: 'position: relative;' // height: 17px;
17017                                     }
17018                                 ]
17019                             }
17020                             
17021                             
17022                         ]
17023                     }
17024                 ]
17025                 
17026             }
17027         };
17028         var cal_rows = function() {
17029             
17030             var ret = [];
17031             for (var r = 0; r < 6; r++) {
17032                 var row= {
17033                     tag : 'tr',
17034                     cls : 'fc-week',
17035                     cn : []
17036                 };
17037                 
17038                 for (var i =0; i < Date.dayNames.length; i++) {
17039                     var d = Date.dayNames[i];
17040                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17041
17042                 }
17043                 row.cn[0].cls+=' fc-first';
17044                 row.cn[0].cn[0].style = 'min-height:90px';
17045                 row.cn[6].cls+=' fc-last';
17046                 ret.push(row);
17047                 
17048             }
17049             ret[0].cls += ' fc-first';
17050             ret[4].cls += ' fc-prev-last';
17051             ret[5].cls += ' fc-last';
17052             return ret;
17053             
17054         };
17055         
17056         var cal_table = {
17057             tag: 'table',
17058             cls: 'fc-border-separate',
17059             style : 'width:100%',
17060             cellspacing  : 0,
17061             cn : [
17062                 { 
17063                     tag: 'thead',
17064                     cn : [
17065                         { 
17066                             tag: 'tr',
17067                             cls : 'fc-first fc-last',
17068                             cn : cal_heads()
17069                         }
17070                     ]
17071                 },
17072                 { 
17073                     tag: 'tbody',
17074                     cn : cal_rows()
17075                 }
17076                   
17077             ]
17078         };
17079          
17080          var cfg = {
17081             cls : 'fc fc-ltr',
17082             cn : [
17083                 header,
17084                 {
17085                     cls : 'fc-content',
17086                     style : "position: relative;",
17087                     cn : [
17088                         {
17089                             cls : 'fc-view fc-view-month fc-grid',
17090                             style : 'position: relative',
17091                             unselectable : 'on',
17092                             cn : [
17093                                 {
17094                                     cls : 'fc-event-container',
17095                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17096                                 },
17097                                 cal_table
17098                             ]
17099                         }
17100                     ]
17101     
17102                 }
17103            ] 
17104             
17105         };
17106         
17107          
17108         
17109         return cfg;
17110     },
17111     
17112     
17113     initEvents : function()
17114     {
17115         if(!this.store){
17116             throw "can not find store for calendar";
17117         }
17118         
17119         var mark = {
17120             tag: "div",
17121             cls:"x-dlg-mask",
17122             style: "text-align:center",
17123             cn: [
17124                 {
17125                     tag: "div",
17126                     style: "background-color:white;width:50%;margin:250 auto",
17127                     cn: [
17128                         {
17129                             tag: "img",
17130                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17131                         },
17132                         {
17133                             tag: "span",
17134                             html: "Loading"
17135                         }
17136                         
17137                     ]
17138                 }
17139             ]
17140         };
17141         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17142         
17143         var size = this.el.select('.fc-content', true).first().getSize();
17144         this.maskEl.setSize(size.width, size.height);
17145         this.maskEl.enableDisplayMode("block");
17146         if(!this.loadMask){
17147             this.maskEl.hide();
17148         }
17149         
17150         this.store = Roo.factory(this.store, Roo.data);
17151         this.store.on('load', this.onLoad, this);
17152         this.store.on('beforeload', this.onBeforeLoad, this);
17153         
17154         this.resize();
17155         
17156         this.cells = this.el.select('.fc-day',true);
17157         //Roo.log(this.cells);
17158         this.textNodes = this.el.query('.fc-day-number');
17159         this.cells.addClassOnOver('fc-state-hover');
17160         
17161         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17162         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17163         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17164         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17165         
17166         this.on('monthchange', this.onMonthChange, this);
17167         
17168         this.update(new Date().clearTime());
17169     },
17170     
17171     resize : function() {
17172         var sz  = this.el.getSize();
17173         
17174         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17175         this.el.select('.fc-day-content div',true).setHeight(34);
17176     },
17177     
17178     
17179     // private
17180     showPrevMonth : function(e){
17181         this.update(this.activeDate.add("mo", -1));
17182     },
17183     showToday : function(e){
17184         this.update(new Date().clearTime());
17185     },
17186     // private
17187     showNextMonth : function(e){
17188         this.update(this.activeDate.add("mo", 1));
17189     },
17190
17191     // private
17192     showPrevYear : function(){
17193         this.update(this.activeDate.add("y", -1));
17194     },
17195
17196     // private
17197     showNextYear : function(){
17198         this.update(this.activeDate.add("y", 1));
17199     },
17200
17201     
17202    // private
17203     update : function(date)
17204     {
17205         var vd = this.activeDate;
17206         this.activeDate = date;
17207 //        if(vd && this.el){
17208 //            var t = date.getTime();
17209 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17210 //                Roo.log('using add remove');
17211 //                
17212 //                this.fireEvent('monthchange', this, date);
17213 //                
17214 //                this.cells.removeClass("fc-state-highlight");
17215 //                this.cells.each(function(c){
17216 //                   if(c.dateValue == t){
17217 //                       c.addClass("fc-state-highlight");
17218 //                       setTimeout(function(){
17219 //                            try{c.dom.firstChild.focus();}catch(e){}
17220 //                       }, 50);
17221 //                       return false;
17222 //                   }
17223 //                   return true;
17224 //                });
17225 //                return;
17226 //            }
17227 //        }
17228         
17229         var days = date.getDaysInMonth();
17230         
17231         var firstOfMonth = date.getFirstDateOfMonth();
17232         var startingPos = firstOfMonth.getDay()-this.startDay;
17233         
17234         if(startingPos < this.startDay){
17235             startingPos += 7;
17236         }
17237         
17238         var pm = date.add(Date.MONTH, -1);
17239         var prevStart = pm.getDaysInMonth()-startingPos;
17240 //        
17241         this.cells = this.el.select('.fc-day',true);
17242         this.textNodes = this.el.query('.fc-day-number');
17243         this.cells.addClassOnOver('fc-state-hover');
17244         
17245         var cells = this.cells.elements;
17246         var textEls = this.textNodes;
17247         
17248         Roo.each(cells, function(cell){
17249             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17250         });
17251         
17252         days += startingPos;
17253
17254         // convert everything to numbers so it's fast
17255         var day = 86400000;
17256         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17257         //Roo.log(d);
17258         //Roo.log(pm);
17259         //Roo.log(prevStart);
17260         
17261         var today = new Date().clearTime().getTime();
17262         var sel = date.clearTime().getTime();
17263         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17264         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17265         var ddMatch = this.disabledDatesRE;
17266         var ddText = this.disabledDatesText;
17267         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17268         var ddaysText = this.disabledDaysText;
17269         var format = this.format;
17270         
17271         var setCellClass = function(cal, cell){
17272             cell.row = 0;
17273             cell.events = [];
17274             cell.more = [];
17275             //Roo.log('set Cell Class');
17276             cell.title = "";
17277             var t = d.getTime();
17278             
17279             //Roo.log(d);
17280             
17281             cell.dateValue = t;
17282             if(t == today){
17283                 cell.className += " fc-today";
17284                 cell.className += " fc-state-highlight";
17285                 cell.title = cal.todayText;
17286             }
17287             if(t == sel){
17288                 // disable highlight in other month..
17289                 //cell.className += " fc-state-highlight";
17290                 
17291             }
17292             // disabling
17293             if(t < min) {
17294                 cell.className = " fc-state-disabled";
17295                 cell.title = cal.minText;
17296                 return;
17297             }
17298             if(t > max) {
17299                 cell.className = " fc-state-disabled";
17300                 cell.title = cal.maxText;
17301                 return;
17302             }
17303             if(ddays){
17304                 if(ddays.indexOf(d.getDay()) != -1){
17305                     cell.title = ddaysText;
17306                     cell.className = " fc-state-disabled";
17307                 }
17308             }
17309             if(ddMatch && format){
17310                 var fvalue = d.dateFormat(format);
17311                 if(ddMatch.test(fvalue)){
17312                     cell.title = ddText.replace("%0", fvalue);
17313                     cell.className = " fc-state-disabled";
17314                 }
17315             }
17316             
17317             if (!cell.initialClassName) {
17318                 cell.initialClassName = cell.dom.className;
17319             }
17320             
17321             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17322         };
17323
17324         var i = 0;
17325         
17326         for(; i < startingPos; i++) {
17327             textEls[i].innerHTML = (++prevStart);
17328             d.setDate(d.getDate()+1);
17329             
17330             cells[i].className = "fc-past fc-other-month";
17331             setCellClass(this, cells[i]);
17332         }
17333         
17334         var intDay = 0;
17335         
17336         for(; i < days; i++){
17337             intDay = i - startingPos + 1;
17338             textEls[i].innerHTML = (intDay);
17339             d.setDate(d.getDate()+1);
17340             
17341             cells[i].className = ''; // "x-date-active";
17342             setCellClass(this, cells[i]);
17343         }
17344         var extraDays = 0;
17345         
17346         for(; i < 42; i++) {
17347             textEls[i].innerHTML = (++extraDays);
17348             d.setDate(d.getDate()+1);
17349             
17350             cells[i].className = "fc-future fc-other-month";
17351             setCellClass(this, cells[i]);
17352         }
17353         
17354         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17355         
17356         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17357         
17358         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17359         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17360         
17361         if(totalRows != 6){
17362             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17363             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17364         }
17365         
17366         this.fireEvent('monthchange', this, date);
17367         
17368         
17369         /*
17370         if(!this.internalRender){
17371             var main = this.el.dom.firstChild;
17372             var w = main.offsetWidth;
17373             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17374             Roo.fly(main).setWidth(w);
17375             this.internalRender = true;
17376             // opera does not respect the auto grow header center column
17377             // then, after it gets a width opera refuses to recalculate
17378             // without a second pass
17379             if(Roo.isOpera && !this.secondPass){
17380                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17381                 this.secondPass = true;
17382                 this.update.defer(10, this, [date]);
17383             }
17384         }
17385         */
17386         
17387     },
17388     
17389     findCell : function(dt) {
17390         dt = dt.clearTime().getTime();
17391         var ret = false;
17392         this.cells.each(function(c){
17393             //Roo.log("check " +c.dateValue + '?=' + dt);
17394             if(c.dateValue == dt){
17395                 ret = c;
17396                 return false;
17397             }
17398             return true;
17399         });
17400         
17401         return ret;
17402     },
17403     
17404     findCells : function(ev) {
17405         var s = ev.start.clone().clearTime().getTime();
17406        // Roo.log(s);
17407         var e= ev.end.clone().clearTime().getTime();
17408        // Roo.log(e);
17409         var ret = [];
17410         this.cells.each(function(c){
17411              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17412             
17413             if(c.dateValue > e){
17414                 return ;
17415             }
17416             if(c.dateValue < s){
17417                 return ;
17418             }
17419             ret.push(c);
17420         });
17421         
17422         return ret;    
17423     },
17424     
17425 //    findBestRow: function(cells)
17426 //    {
17427 //        var ret = 0;
17428 //        
17429 //        for (var i =0 ; i < cells.length;i++) {
17430 //            ret  = Math.max(cells[i].rows || 0,ret);
17431 //        }
17432 //        return ret;
17433 //        
17434 //    },
17435     
17436     
17437     addItem : function(ev)
17438     {
17439         // look for vertical location slot in
17440         var cells = this.findCells(ev);
17441         
17442 //        ev.row = this.findBestRow(cells);
17443         
17444         // work out the location.
17445         
17446         var crow = false;
17447         var rows = [];
17448         for(var i =0; i < cells.length; i++) {
17449             
17450             cells[i].row = cells[0].row;
17451             
17452             if(i == 0){
17453                 cells[i].row = cells[i].row + 1;
17454             }
17455             
17456             if (!crow) {
17457                 crow = {
17458                     start : cells[i],
17459                     end :  cells[i]
17460                 };
17461                 continue;
17462             }
17463             if (crow.start.getY() == cells[i].getY()) {
17464                 // on same row.
17465                 crow.end = cells[i];
17466                 continue;
17467             }
17468             // different row.
17469             rows.push(crow);
17470             crow = {
17471                 start: cells[i],
17472                 end : cells[i]
17473             };
17474             
17475         }
17476         
17477         rows.push(crow);
17478         ev.els = [];
17479         ev.rows = rows;
17480         ev.cells = cells;
17481         
17482         cells[0].events.push(ev);
17483         
17484         this.calevents.push(ev);
17485     },
17486     
17487     clearEvents: function() {
17488         
17489         if(!this.calevents){
17490             return;
17491         }
17492         
17493         Roo.each(this.cells.elements, function(c){
17494             c.row = 0;
17495             c.events = [];
17496             c.more = [];
17497         });
17498         
17499         Roo.each(this.calevents, function(e) {
17500             Roo.each(e.els, function(el) {
17501                 el.un('mouseenter' ,this.onEventEnter, this);
17502                 el.un('mouseleave' ,this.onEventLeave, this);
17503                 el.remove();
17504             },this);
17505         },this);
17506         
17507         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17508             e.remove();
17509         });
17510         
17511     },
17512     
17513     renderEvents: function()
17514     {   
17515         var _this = this;
17516         
17517         this.cells.each(function(c) {
17518             
17519             if(c.row < 5){
17520                 return;
17521             }
17522             
17523             var ev = c.events;
17524             
17525             var r = 4;
17526             if(c.row != c.events.length){
17527                 r = 4 - (4 - (c.row - c.events.length));
17528             }
17529             
17530             c.events = ev.slice(0, r);
17531             c.more = ev.slice(r);
17532             
17533             if(c.more.length && c.more.length == 1){
17534                 c.events.push(c.more.pop());
17535             }
17536             
17537             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17538             
17539         });
17540             
17541         this.cells.each(function(c) {
17542             
17543             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17544             
17545             
17546             for (var e = 0; e < c.events.length; e++){
17547                 var ev = c.events[e];
17548                 var rows = ev.rows;
17549                 
17550                 for(var i = 0; i < rows.length; i++) {
17551                 
17552                     // how many rows should it span..
17553
17554                     var  cfg = {
17555                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17556                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17557
17558                         unselectable : "on",
17559                         cn : [
17560                             {
17561                                 cls: 'fc-event-inner',
17562                                 cn : [
17563     //                                {
17564     //                                  tag:'span',
17565     //                                  cls: 'fc-event-time',
17566     //                                  html : cells.length > 1 ? '' : ev.time
17567     //                                },
17568                                     {
17569                                       tag:'span',
17570                                       cls: 'fc-event-title',
17571                                       html : String.format('{0}', ev.title)
17572                                     }
17573
17574
17575                                 ]
17576                             },
17577                             {
17578                                 cls: 'ui-resizable-handle ui-resizable-e',
17579                                 html : '&nbsp;&nbsp;&nbsp'
17580                             }
17581
17582                         ]
17583                     };
17584
17585                     if (i == 0) {
17586                         cfg.cls += ' fc-event-start';
17587                     }
17588                     if ((i+1) == rows.length) {
17589                         cfg.cls += ' fc-event-end';
17590                     }
17591
17592                     var ctr = _this.el.select('.fc-event-container',true).first();
17593                     var cg = ctr.createChild(cfg);
17594
17595                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17596                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17597
17598                     var r = (c.more.length) ? 1 : 0;
17599                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17600                     cg.setWidth(ebox.right - sbox.x -2);
17601
17602                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17603                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17604                     cg.on('click', _this.onEventClick, _this, ev);
17605
17606                     ev.els.push(cg);
17607                     
17608                 }
17609                 
17610             }
17611             
17612             
17613             if(c.more.length){
17614                 var  cfg = {
17615                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17616                     style : 'position: absolute',
17617                     unselectable : "on",
17618                     cn : [
17619                         {
17620                             cls: 'fc-event-inner',
17621                             cn : [
17622                                 {
17623                                   tag:'span',
17624                                   cls: 'fc-event-title',
17625                                   html : 'More'
17626                                 }
17627
17628
17629                             ]
17630                         },
17631                         {
17632                             cls: 'ui-resizable-handle ui-resizable-e',
17633                             html : '&nbsp;&nbsp;&nbsp'
17634                         }
17635
17636                     ]
17637                 };
17638
17639                 var ctr = _this.el.select('.fc-event-container',true).first();
17640                 var cg = ctr.createChild(cfg);
17641
17642                 var sbox = c.select('.fc-day-content',true).first().getBox();
17643                 var ebox = c.select('.fc-day-content',true).first().getBox();
17644                 //Roo.log(cg);
17645                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17646                 cg.setWidth(ebox.right - sbox.x -2);
17647
17648                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17649                 
17650             }
17651             
17652         });
17653         
17654         
17655         
17656     },
17657     
17658     onEventEnter: function (e, el,event,d) {
17659         this.fireEvent('evententer', this, el, event);
17660     },
17661     
17662     onEventLeave: function (e, el,event,d) {
17663         this.fireEvent('eventleave', this, el, event);
17664     },
17665     
17666     onEventClick: function (e, el,event,d) {
17667         this.fireEvent('eventclick', this, el, event);
17668     },
17669     
17670     onMonthChange: function () {
17671         this.store.load();
17672     },
17673     
17674     onMoreEventClick: function(e, el, more)
17675     {
17676         var _this = this;
17677         
17678         this.calpopover.placement = 'right';
17679         this.calpopover.setTitle('More');
17680         
17681         this.calpopover.setContent('');
17682         
17683         var ctr = this.calpopover.el.select('.popover-content', true).first();
17684         
17685         Roo.each(more, function(m){
17686             var cfg = {
17687                 cls : 'fc-event-hori fc-event-draggable',
17688                 html : m.title
17689             };
17690             var cg = ctr.createChild(cfg);
17691             
17692             cg.on('click', _this.onEventClick, _this, m);
17693         });
17694         
17695         this.calpopover.show(el);
17696         
17697         
17698     },
17699     
17700     onLoad: function () 
17701     {   
17702         this.calevents = [];
17703         var cal = this;
17704         
17705         if(this.store.getCount() > 0){
17706             this.store.data.each(function(d){
17707                cal.addItem({
17708                     id : d.data.id,
17709                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17710                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17711                     time : d.data.start_time,
17712                     title : d.data.title,
17713                     description : d.data.description,
17714                     venue : d.data.venue
17715                 });
17716             });
17717         }
17718         
17719         this.renderEvents();
17720         
17721         if(this.calevents.length && this.loadMask){
17722             this.maskEl.hide();
17723         }
17724     },
17725     
17726     onBeforeLoad: function()
17727     {
17728         this.clearEvents();
17729         if(this.loadMask){
17730             this.maskEl.show();
17731         }
17732     }
17733 });
17734
17735  
17736  /*
17737  * - LGPL
17738  *
17739  * element
17740  * 
17741  */
17742
17743 /**
17744  * @class Roo.bootstrap.Popover
17745  * @extends Roo.bootstrap.Component
17746  * Bootstrap Popover class
17747  * @cfg {String} html contents of the popover   (or false to use children..)
17748  * @cfg {String} title of popover (or false to hide)
17749  * @cfg {String} placement how it is placed
17750  * @cfg {String} trigger click || hover (or false to trigger manually)
17751  * @cfg {String} over what (parent or false to trigger manually.)
17752  * @cfg {Number} delay - delay before showing
17753  
17754  * @constructor
17755  * Create a new Popover
17756  * @param {Object} config The config object
17757  */
17758
17759 Roo.bootstrap.Popover = function(config){
17760     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17761     
17762     this.addEvents({
17763         // raw events
17764          /**
17765          * @event show
17766          * After the popover show
17767          * 
17768          * @param {Roo.bootstrap.Popover} this
17769          */
17770         "show" : true,
17771         /**
17772          * @event hide
17773          * After the popover hide
17774          * 
17775          * @param {Roo.bootstrap.Popover} this
17776          */
17777         "hide" : true
17778     });
17779 };
17780
17781 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17782     
17783     title: 'Fill in a title',
17784     html: false,
17785     
17786     placement : 'right',
17787     trigger : 'hover', // hover
17788     
17789     delay : 0,
17790     
17791     over: 'parent',
17792     
17793     can_build_overlaid : false,
17794     
17795     getChildContainer : function()
17796     {
17797         return this.el.select('.popover-content',true).first();
17798     },
17799     
17800     getAutoCreate : function(){
17801          
17802         var cfg = {
17803            cls : 'popover roo-dynamic',
17804            style: 'display:block',
17805            cn : [
17806                 {
17807                     cls : 'arrow'
17808                 },
17809                 {
17810                     cls : 'popover-inner',
17811                     cn : [
17812                         {
17813                             tag: 'h3',
17814                             cls: 'popover-title popover-header',
17815                             html : this.title
17816                         },
17817                         {
17818                             cls : 'popover-content popover-body',
17819                             html : this.html
17820                         }
17821                     ]
17822                     
17823                 }
17824            ]
17825         };
17826         
17827         return cfg;
17828     },
17829     setTitle: function(str)
17830     {
17831         this.title = str;
17832         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17833     },
17834     setContent: function(str)
17835     {
17836         this.html = str;
17837         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17838     },
17839     // as it get's added to the bottom of the page.
17840     onRender : function(ct, position)
17841     {
17842         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17843         if(!this.el){
17844             var cfg = Roo.apply({},  this.getAutoCreate());
17845             cfg.id = Roo.id();
17846             
17847             if (this.cls) {
17848                 cfg.cls += ' ' + this.cls;
17849             }
17850             if (this.style) {
17851                 cfg.style = this.style;
17852             }
17853             //Roo.log("adding to ");
17854             this.el = Roo.get(document.body).createChild(cfg, position);
17855 //            Roo.log(this.el);
17856         }
17857         this.initEvents();
17858     },
17859     
17860     initEvents : function()
17861     {
17862         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17863         this.el.enableDisplayMode('block');
17864         this.el.hide();
17865         if (this.over === false) {
17866             return; 
17867         }
17868         if (this.triggers === false) {
17869             return;
17870         }
17871         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17872         var triggers = this.trigger ? this.trigger.split(' ') : [];
17873         Roo.each(triggers, function(trigger) {
17874         
17875             if (trigger == 'click') {
17876                 on_el.on('click', this.toggle, this);
17877             } else if (trigger != 'manual') {
17878                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17879                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17880       
17881                 on_el.on(eventIn  ,this.enter, this);
17882                 on_el.on(eventOut, this.leave, this);
17883             }
17884         }, this);
17885         
17886     },
17887     
17888     
17889     // private
17890     timeout : null,
17891     hoverState : null,
17892     
17893     toggle : function () {
17894         this.hoverState == 'in' ? this.leave() : this.enter();
17895     },
17896     
17897     enter : function () {
17898         
17899         clearTimeout(this.timeout);
17900     
17901         this.hoverState = 'in';
17902     
17903         if (!this.delay || !this.delay.show) {
17904             this.show();
17905             return;
17906         }
17907         var _t = this;
17908         this.timeout = setTimeout(function () {
17909             if (_t.hoverState == 'in') {
17910                 _t.show();
17911             }
17912         }, this.delay.show)
17913     },
17914     
17915     leave : function() {
17916         clearTimeout(this.timeout);
17917     
17918         this.hoverState = 'out';
17919     
17920         if (!this.delay || !this.delay.hide) {
17921             this.hide();
17922             return;
17923         }
17924         var _t = this;
17925         this.timeout = setTimeout(function () {
17926             if (_t.hoverState == 'out') {
17927                 _t.hide();
17928             }
17929         }, this.delay.hide)
17930     },
17931     
17932     show : function (on_el)
17933     {
17934         if (!on_el) {
17935             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17936         }
17937         
17938         // set content.
17939         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17940         if (this.html !== false) {
17941             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17942         }
17943         this.el.removeClass([
17944             'fade','top','bottom', 'left', 'right','in',
17945             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17946         ]);
17947         if (!this.title.length) {
17948             this.el.select('.popover-title',true).hide();
17949         }
17950         
17951         var placement = typeof this.placement == 'function' ?
17952             this.placement.call(this, this.el, on_el) :
17953             this.placement;
17954             
17955         var autoToken = /\s?auto?\s?/i;
17956         var autoPlace = autoToken.test(placement);
17957         if (autoPlace) {
17958             placement = placement.replace(autoToken, '') || 'top';
17959         }
17960         
17961         //this.el.detach()
17962         //this.el.setXY([0,0]);
17963         this.el.show();
17964         this.el.dom.style.display='block';
17965         this.el.addClass(placement);
17966         
17967         //this.el.appendTo(on_el);
17968         
17969         var p = this.getPosition();
17970         var box = this.el.getBox();
17971         
17972         if (autoPlace) {
17973             // fixme..
17974         }
17975         var align = Roo.bootstrap.Popover.alignment[placement];
17976         
17977 //        Roo.log(align);
17978         this.el.alignTo(on_el, align[0],align[1]);
17979         //var arrow = this.el.select('.arrow',true).first();
17980         //arrow.set(align[2], 
17981         
17982         this.el.addClass('in');
17983         
17984         
17985         if (this.el.hasClass('fade')) {
17986             // fade it?
17987         }
17988         
17989         this.hoverState = 'in';
17990         
17991         this.fireEvent('show', this);
17992         
17993     },
17994     hide : function()
17995     {
17996         this.el.setXY([0,0]);
17997         this.el.removeClass('in');
17998         this.el.hide();
17999         this.hoverState = null;
18000         
18001         this.fireEvent('hide', this);
18002     }
18003     
18004 });
18005
18006 Roo.bootstrap.Popover.alignment = {
18007     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18008     'right' : ['l-r', [10,0], 'left bs-popover-left'],
18009     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18010     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18011 };
18012
18013  /*
18014  * - LGPL
18015  *
18016  * Progress
18017  * 
18018  */
18019
18020 /**
18021  * @class Roo.bootstrap.Progress
18022  * @extends Roo.bootstrap.Component
18023  * Bootstrap Progress class
18024  * @cfg {Boolean} striped striped of the progress bar
18025  * @cfg {Boolean} active animated of the progress bar
18026  * 
18027  * 
18028  * @constructor
18029  * Create a new Progress
18030  * @param {Object} config The config object
18031  */
18032
18033 Roo.bootstrap.Progress = function(config){
18034     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18035 };
18036
18037 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
18038     
18039     striped : false,
18040     active: false,
18041     
18042     getAutoCreate : function(){
18043         var cfg = {
18044             tag: 'div',
18045             cls: 'progress'
18046         };
18047         
18048         
18049         if(this.striped){
18050             cfg.cls += ' progress-striped';
18051         }
18052       
18053         if(this.active){
18054             cfg.cls += ' active';
18055         }
18056         
18057         
18058         return cfg;
18059     }
18060    
18061 });
18062
18063  
18064
18065  /*
18066  * - LGPL
18067  *
18068  * ProgressBar
18069  * 
18070  */
18071
18072 /**
18073  * @class Roo.bootstrap.ProgressBar
18074  * @extends Roo.bootstrap.Component
18075  * Bootstrap ProgressBar class
18076  * @cfg {Number} aria_valuenow aria-value now
18077  * @cfg {Number} aria_valuemin aria-value min
18078  * @cfg {Number} aria_valuemax aria-value max
18079  * @cfg {String} label label for the progress bar
18080  * @cfg {String} panel (success | info | warning | danger )
18081  * @cfg {String} role role of the progress bar
18082  * @cfg {String} sr_only text
18083  * 
18084  * 
18085  * @constructor
18086  * Create a new ProgressBar
18087  * @param {Object} config The config object
18088  */
18089
18090 Roo.bootstrap.ProgressBar = function(config){
18091     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18092 };
18093
18094 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18095     
18096     aria_valuenow : 0,
18097     aria_valuemin : 0,
18098     aria_valuemax : 100,
18099     label : false,
18100     panel : false,
18101     role : false,
18102     sr_only: false,
18103     
18104     getAutoCreate : function()
18105     {
18106         
18107         var cfg = {
18108             tag: 'div',
18109             cls: 'progress-bar',
18110             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18111         };
18112         
18113         if(this.sr_only){
18114             cfg.cn = {
18115                 tag: 'span',
18116                 cls: 'sr-only',
18117                 html: this.sr_only
18118             }
18119         }
18120         
18121         if(this.role){
18122             cfg.role = this.role;
18123         }
18124         
18125         if(this.aria_valuenow){
18126             cfg['aria-valuenow'] = this.aria_valuenow;
18127         }
18128         
18129         if(this.aria_valuemin){
18130             cfg['aria-valuemin'] = this.aria_valuemin;
18131         }
18132         
18133         if(this.aria_valuemax){
18134             cfg['aria-valuemax'] = this.aria_valuemax;
18135         }
18136         
18137         if(this.label && !this.sr_only){
18138             cfg.html = this.label;
18139         }
18140         
18141         if(this.panel){
18142             cfg.cls += ' progress-bar-' + this.panel;
18143         }
18144         
18145         return cfg;
18146     },
18147     
18148     update : function(aria_valuenow)
18149     {
18150         this.aria_valuenow = aria_valuenow;
18151         
18152         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18153     }
18154    
18155 });
18156
18157  
18158
18159  /*
18160  * - LGPL
18161  *
18162  * column
18163  * 
18164  */
18165
18166 /**
18167  * @class Roo.bootstrap.TabGroup
18168  * @extends Roo.bootstrap.Column
18169  * Bootstrap Column class
18170  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18171  * @cfg {Boolean} carousel true to make the group behave like a carousel
18172  * @cfg {Boolean} bullets show bullets for the panels
18173  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18174  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18175  * @cfg {Boolean} showarrow (true|false) show arrow default true
18176  * 
18177  * @constructor
18178  * Create a new TabGroup
18179  * @param {Object} config The config object
18180  */
18181
18182 Roo.bootstrap.TabGroup = function(config){
18183     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18184     if (!this.navId) {
18185         this.navId = Roo.id();
18186     }
18187     this.tabs = [];
18188     Roo.bootstrap.TabGroup.register(this);
18189     
18190 };
18191
18192 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18193     
18194     carousel : false,
18195     transition : false,
18196     bullets : 0,
18197     timer : 0,
18198     autoslide : false,
18199     slideFn : false,
18200     slideOnTouch : false,
18201     showarrow : true,
18202     
18203     getAutoCreate : function()
18204     {
18205         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18206         
18207         cfg.cls += ' tab-content';
18208         
18209         if (this.carousel) {
18210             cfg.cls += ' carousel slide';
18211             
18212             cfg.cn = [{
18213                cls : 'carousel-inner',
18214                cn : []
18215             }];
18216         
18217             if(this.bullets  && !Roo.isTouch){
18218                 
18219                 var bullets = {
18220                     cls : 'carousel-bullets',
18221                     cn : []
18222                 };
18223                
18224                 if(this.bullets_cls){
18225                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18226                 }
18227                 
18228                 bullets.cn.push({
18229                     cls : 'clear'
18230                 });
18231                 
18232                 cfg.cn[0].cn.push(bullets);
18233             }
18234             
18235             if(this.showarrow){
18236                 cfg.cn[0].cn.push({
18237                     tag : 'div',
18238                     class : 'carousel-arrow',
18239                     cn : [
18240                         {
18241                             tag : 'div',
18242                             class : 'carousel-prev',
18243                             cn : [
18244                                 {
18245                                     tag : 'i',
18246                                     class : 'fa fa-chevron-left'
18247                                 }
18248                             ]
18249                         },
18250                         {
18251                             tag : 'div',
18252                             class : 'carousel-next',
18253                             cn : [
18254                                 {
18255                                     tag : 'i',
18256                                     class : 'fa fa-chevron-right'
18257                                 }
18258                             ]
18259                         }
18260                     ]
18261                 });
18262             }
18263             
18264         }
18265         
18266         return cfg;
18267     },
18268     
18269     initEvents:  function()
18270     {
18271 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18272 //            this.el.on("touchstart", this.onTouchStart, this);
18273 //        }
18274         
18275         if(this.autoslide){
18276             var _this = this;
18277             
18278             this.slideFn = window.setInterval(function() {
18279                 _this.showPanelNext();
18280             }, this.timer);
18281         }
18282         
18283         if(this.showarrow){
18284             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18285             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18286         }
18287         
18288         
18289     },
18290     
18291 //    onTouchStart : function(e, el, o)
18292 //    {
18293 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18294 //            return;
18295 //        }
18296 //        
18297 //        this.showPanelNext();
18298 //    },
18299     
18300     
18301     getChildContainer : function()
18302     {
18303         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18304     },
18305     
18306     /**
18307     * register a Navigation item
18308     * @param {Roo.bootstrap.NavItem} the navitem to add
18309     */
18310     register : function(item)
18311     {
18312         this.tabs.push( item);
18313         item.navId = this.navId; // not really needed..
18314         this.addBullet();
18315     
18316     },
18317     
18318     getActivePanel : function()
18319     {
18320         var r = false;
18321         Roo.each(this.tabs, function(t) {
18322             if (t.active) {
18323                 r = t;
18324                 return false;
18325             }
18326             return null;
18327         });
18328         return r;
18329         
18330     },
18331     getPanelByName : function(n)
18332     {
18333         var r = false;
18334         Roo.each(this.tabs, function(t) {
18335             if (t.tabId == n) {
18336                 r = t;
18337                 return false;
18338             }
18339             return null;
18340         });
18341         return r;
18342     },
18343     indexOfPanel : function(p)
18344     {
18345         var r = false;
18346         Roo.each(this.tabs, function(t,i) {
18347             if (t.tabId == p.tabId) {
18348                 r = i;
18349                 return false;
18350             }
18351             return null;
18352         });
18353         return r;
18354     },
18355     /**
18356      * show a specific panel
18357      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18358      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18359      */
18360     showPanel : function (pan)
18361     {
18362         if(this.transition || typeof(pan) == 'undefined'){
18363             Roo.log("waiting for the transitionend");
18364             return false;
18365         }
18366         
18367         if (typeof(pan) == 'number') {
18368             pan = this.tabs[pan];
18369         }
18370         
18371         if (typeof(pan) == 'string') {
18372             pan = this.getPanelByName(pan);
18373         }
18374         
18375         var cur = this.getActivePanel();
18376         
18377         if(!pan || !cur){
18378             Roo.log('pan or acitve pan is undefined');
18379             return false;
18380         }
18381         
18382         if (pan.tabId == this.getActivePanel().tabId) {
18383             return true;
18384         }
18385         
18386         if (false === cur.fireEvent('beforedeactivate')) {
18387             return false;
18388         }
18389         
18390         if(this.bullets > 0 && !Roo.isTouch){
18391             this.setActiveBullet(this.indexOfPanel(pan));
18392         }
18393         
18394         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18395             
18396             //class="carousel-item carousel-item-next carousel-item-left"
18397             
18398             this.transition = true;
18399             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18400             var lr = dir == 'next' ? 'left' : 'right';
18401             pan.el.addClass(dir); // or prev
18402             pan.el.addClass('carousel-item-' + dir); // or prev
18403             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18404             cur.el.addClass(lr); // or right
18405             pan.el.addClass(lr);
18406             cur.el.addClass('carousel-item-' +lr); // or right
18407             pan.el.addClass('carousel-item-' +lr);
18408             
18409             
18410             var _this = this;
18411             cur.el.on('transitionend', function() {
18412                 Roo.log("trans end?");
18413                 
18414                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18415                 pan.setActive(true);
18416                 
18417                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18418                 cur.setActive(false);
18419                 
18420                 _this.transition = false;
18421                 
18422             }, this, { single:  true } );
18423             
18424             return true;
18425         }
18426         
18427         cur.setActive(false);
18428         pan.setActive(true);
18429         
18430         return true;
18431         
18432     },
18433     showPanelNext : function()
18434     {
18435         var i = this.indexOfPanel(this.getActivePanel());
18436         
18437         if (i >= this.tabs.length - 1 && !this.autoslide) {
18438             return;
18439         }
18440         
18441         if (i >= this.tabs.length - 1 && this.autoslide) {
18442             i = -1;
18443         }
18444         
18445         this.showPanel(this.tabs[i+1]);
18446     },
18447     
18448     showPanelPrev : function()
18449     {
18450         var i = this.indexOfPanel(this.getActivePanel());
18451         
18452         if (i  < 1 && !this.autoslide) {
18453             return;
18454         }
18455         
18456         if (i < 1 && this.autoslide) {
18457             i = this.tabs.length;
18458         }
18459         
18460         this.showPanel(this.tabs[i-1]);
18461     },
18462     
18463     
18464     addBullet: function()
18465     {
18466         if(!this.bullets || Roo.isTouch){
18467             return;
18468         }
18469         var ctr = this.el.select('.carousel-bullets',true).first();
18470         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18471         var bullet = ctr.createChild({
18472             cls : 'bullet bullet-' + i
18473         },ctr.dom.lastChild);
18474         
18475         
18476         var _this = this;
18477         
18478         bullet.on('click', (function(e, el, o, ii, t){
18479
18480             e.preventDefault();
18481
18482             this.showPanel(ii);
18483
18484             if(this.autoslide && this.slideFn){
18485                 clearInterval(this.slideFn);
18486                 this.slideFn = window.setInterval(function() {
18487                     _this.showPanelNext();
18488                 }, this.timer);
18489             }
18490
18491         }).createDelegate(this, [i, bullet], true));
18492                 
18493         
18494     },
18495      
18496     setActiveBullet : function(i)
18497     {
18498         if(Roo.isTouch){
18499             return;
18500         }
18501         
18502         Roo.each(this.el.select('.bullet', true).elements, function(el){
18503             el.removeClass('selected');
18504         });
18505
18506         var bullet = this.el.select('.bullet-' + i, true).first();
18507         
18508         if(!bullet){
18509             return;
18510         }
18511         
18512         bullet.addClass('selected');
18513     }
18514     
18515     
18516   
18517 });
18518
18519  
18520
18521  
18522  
18523 Roo.apply(Roo.bootstrap.TabGroup, {
18524     
18525     groups: {},
18526      /**
18527     * register a Navigation Group
18528     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18529     */
18530     register : function(navgrp)
18531     {
18532         this.groups[navgrp.navId] = navgrp;
18533         
18534     },
18535     /**
18536     * fetch a Navigation Group based on the navigation ID
18537     * if one does not exist , it will get created.
18538     * @param {string} the navgroup to add
18539     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18540     */
18541     get: function(navId) {
18542         if (typeof(this.groups[navId]) == 'undefined') {
18543             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18544         }
18545         return this.groups[navId] ;
18546     }
18547     
18548     
18549     
18550 });
18551
18552  /*
18553  * - LGPL
18554  *
18555  * TabPanel
18556  * 
18557  */
18558
18559 /**
18560  * @class Roo.bootstrap.TabPanel
18561  * @extends Roo.bootstrap.Component
18562  * Bootstrap TabPanel class
18563  * @cfg {Boolean} active panel active
18564  * @cfg {String} html panel content
18565  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18566  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18567  * @cfg {String} href click to link..
18568  * 
18569  * 
18570  * @constructor
18571  * Create a new TabPanel
18572  * @param {Object} config The config object
18573  */
18574
18575 Roo.bootstrap.TabPanel = function(config){
18576     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18577     this.addEvents({
18578         /**
18579              * @event changed
18580              * Fires when the active status changes
18581              * @param {Roo.bootstrap.TabPanel} this
18582              * @param {Boolean} state the new state
18583             
18584          */
18585         'changed': true,
18586         /**
18587              * @event beforedeactivate
18588              * Fires before a tab is de-activated - can be used to do validation on a form.
18589              * @param {Roo.bootstrap.TabPanel} this
18590              * @return {Boolean} false if there is an error
18591             
18592          */
18593         'beforedeactivate': true
18594      });
18595     
18596     this.tabId = this.tabId || Roo.id();
18597   
18598 };
18599
18600 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18601     
18602     active: false,
18603     html: false,
18604     tabId: false,
18605     navId : false,
18606     href : '',
18607     
18608     getAutoCreate : function(){
18609         
18610         
18611         var cfg = {
18612             tag: 'div',
18613             // item is needed for carousel - not sure if it has any effect otherwise
18614             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18615             html: this.html || ''
18616         };
18617         
18618         if(this.active){
18619             cfg.cls += ' active';
18620         }
18621         
18622         if(this.tabId){
18623             cfg.tabId = this.tabId;
18624         }
18625         
18626         
18627         
18628         return cfg;
18629     },
18630     
18631     initEvents:  function()
18632     {
18633         var p = this.parent();
18634         
18635         this.navId = this.navId || p.navId;
18636         
18637         if (typeof(this.navId) != 'undefined') {
18638             // not really needed.. but just in case.. parent should be a NavGroup.
18639             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18640             
18641             tg.register(this);
18642             
18643             var i = tg.tabs.length - 1;
18644             
18645             if(this.active && tg.bullets > 0 && i < tg.bullets){
18646                 tg.setActiveBullet(i);
18647             }
18648         }
18649         
18650         this.el.on('click', this.onClick, this);
18651         
18652         if(Roo.isTouch){
18653             this.el.on("touchstart", this.onTouchStart, this);
18654             this.el.on("touchmove", this.onTouchMove, this);
18655             this.el.on("touchend", this.onTouchEnd, this);
18656         }
18657         
18658     },
18659     
18660     onRender : function(ct, position)
18661     {
18662         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18663     },
18664     
18665     setActive : function(state)
18666     {
18667         Roo.log("panel - set active " + this.tabId + "=" + state);
18668         
18669         this.active = state;
18670         if (!state) {
18671             this.el.removeClass('active');
18672             
18673         } else  if (!this.el.hasClass('active')) {
18674             this.el.addClass('active');
18675         }
18676         
18677         this.fireEvent('changed', this, state);
18678     },
18679     
18680     onClick : function(e)
18681     {
18682         e.preventDefault();
18683         
18684         if(!this.href.length){
18685             return;
18686         }
18687         
18688         window.location.href = this.href;
18689     },
18690     
18691     startX : 0,
18692     startY : 0,
18693     endX : 0,
18694     endY : 0,
18695     swiping : false,
18696     
18697     onTouchStart : function(e)
18698     {
18699         this.swiping = false;
18700         
18701         this.startX = e.browserEvent.touches[0].clientX;
18702         this.startY = e.browserEvent.touches[0].clientY;
18703     },
18704     
18705     onTouchMove : function(e)
18706     {
18707         this.swiping = true;
18708         
18709         this.endX = e.browserEvent.touches[0].clientX;
18710         this.endY = e.browserEvent.touches[0].clientY;
18711     },
18712     
18713     onTouchEnd : function(e)
18714     {
18715         if(!this.swiping){
18716             this.onClick(e);
18717             return;
18718         }
18719         
18720         var tabGroup = this.parent();
18721         
18722         if(this.endX > this.startX){ // swiping right
18723             tabGroup.showPanelPrev();
18724             return;
18725         }
18726         
18727         if(this.startX > this.endX){ // swiping left
18728             tabGroup.showPanelNext();
18729             return;
18730         }
18731     }
18732     
18733     
18734 });
18735  
18736
18737  
18738
18739  /*
18740  * - LGPL
18741  *
18742  * DateField
18743  * 
18744  */
18745
18746 /**
18747  * @class Roo.bootstrap.DateField
18748  * @extends Roo.bootstrap.Input
18749  * Bootstrap DateField class
18750  * @cfg {Number} weekStart default 0
18751  * @cfg {String} viewMode default empty, (months|years)
18752  * @cfg {String} minViewMode default empty, (months|years)
18753  * @cfg {Number} startDate default -Infinity
18754  * @cfg {Number} endDate default Infinity
18755  * @cfg {Boolean} todayHighlight default false
18756  * @cfg {Boolean} todayBtn default false
18757  * @cfg {Boolean} calendarWeeks default false
18758  * @cfg {Object} daysOfWeekDisabled default empty
18759  * @cfg {Boolean} singleMode default false (true | false)
18760  * 
18761  * @cfg {Boolean} keyboardNavigation default true
18762  * @cfg {String} language default en
18763  * 
18764  * @constructor
18765  * Create a new DateField
18766  * @param {Object} config The config object
18767  */
18768
18769 Roo.bootstrap.DateField = function(config){
18770     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18771      this.addEvents({
18772             /**
18773              * @event show
18774              * Fires when this field show.
18775              * @param {Roo.bootstrap.DateField} this
18776              * @param {Mixed} date The date value
18777              */
18778             show : true,
18779             /**
18780              * @event show
18781              * Fires when this field hide.
18782              * @param {Roo.bootstrap.DateField} this
18783              * @param {Mixed} date The date value
18784              */
18785             hide : true,
18786             /**
18787              * @event select
18788              * Fires when select a date.
18789              * @param {Roo.bootstrap.DateField} this
18790              * @param {Mixed} date The date value
18791              */
18792             select : true,
18793             /**
18794              * @event beforeselect
18795              * Fires when before select a date.
18796              * @param {Roo.bootstrap.DateField} this
18797              * @param {Mixed} date The date value
18798              */
18799             beforeselect : true
18800         });
18801 };
18802
18803 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18804     
18805     /**
18806      * @cfg {String} format
18807      * The default date format string which can be overriden for localization support.  The format must be
18808      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18809      */
18810     format : "m/d/y",
18811     /**
18812      * @cfg {String} altFormats
18813      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18814      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18815      */
18816     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18817     
18818     weekStart : 0,
18819     
18820     viewMode : '',
18821     
18822     minViewMode : '',
18823     
18824     todayHighlight : false,
18825     
18826     todayBtn: false,
18827     
18828     language: 'en',
18829     
18830     keyboardNavigation: true,
18831     
18832     calendarWeeks: false,
18833     
18834     startDate: -Infinity,
18835     
18836     endDate: Infinity,
18837     
18838     daysOfWeekDisabled: [],
18839     
18840     _events: [],
18841     
18842     singleMode : false,
18843     
18844     UTCDate: function()
18845     {
18846         return new Date(Date.UTC.apply(Date, arguments));
18847     },
18848     
18849     UTCToday: function()
18850     {
18851         var today = new Date();
18852         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18853     },
18854     
18855     getDate: function() {
18856             var d = this.getUTCDate();
18857             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18858     },
18859     
18860     getUTCDate: function() {
18861             return this.date;
18862     },
18863     
18864     setDate: function(d) {
18865             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18866     },
18867     
18868     setUTCDate: function(d) {
18869             this.date = d;
18870             this.setValue(this.formatDate(this.date));
18871     },
18872         
18873     onRender: function(ct, position)
18874     {
18875         
18876         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18877         
18878         this.language = this.language || 'en';
18879         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18880         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18881         
18882         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18883         this.format = this.format || 'm/d/y';
18884         this.isInline = false;
18885         this.isInput = true;
18886         this.component = this.el.select('.add-on', true).first() || false;
18887         this.component = (this.component && this.component.length === 0) ? false : this.component;
18888         this.hasInput = this.component && this.inputEl().length;
18889         
18890         if (typeof(this.minViewMode === 'string')) {
18891             switch (this.minViewMode) {
18892                 case 'months':
18893                     this.minViewMode = 1;
18894                     break;
18895                 case 'years':
18896                     this.minViewMode = 2;
18897                     break;
18898                 default:
18899                     this.minViewMode = 0;
18900                     break;
18901             }
18902         }
18903         
18904         if (typeof(this.viewMode === 'string')) {
18905             switch (this.viewMode) {
18906                 case 'months':
18907                     this.viewMode = 1;
18908                     break;
18909                 case 'years':
18910                     this.viewMode = 2;
18911                     break;
18912                 default:
18913                     this.viewMode = 0;
18914                     break;
18915             }
18916         }
18917                 
18918         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18919         
18920 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18921         
18922         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18923         
18924         this.picker().on('mousedown', this.onMousedown, this);
18925         this.picker().on('click', this.onClick, this);
18926         
18927         this.picker().addClass('datepicker-dropdown');
18928         
18929         this.startViewMode = this.viewMode;
18930         
18931         if(this.singleMode){
18932             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18933                 v.setVisibilityMode(Roo.Element.DISPLAY);
18934                 v.hide();
18935             });
18936             
18937             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18938                 v.setStyle('width', '189px');
18939             });
18940         }
18941         
18942         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18943             if(!this.calendarWeeks){
18944                 v.remove();
18945                 return;
18946             }
18947             
18948             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18949             v.attr('colspan', function(i, val){
18950                 return parseInt(val) + 1;
18951             });
18952         });
18953                         
18954         
18955         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18956         
18957         this.setStartDate(this.startDate);
18958         this.setEndDate(this.endDate);
18959         
18960         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18961         
18962         this.fillDow();
18963         this.fillMonths();
18964         this.update();
18965         this.showMode();
18966         
18967         if(this.isInline) {
18968             this.showPopup();
18969         }
18970     },
18971     
18972     picker : function()
18973     {
18974         return this.pickerEl;
18975 //        return this.el.select('.datepicker', true).first();
18976     },
18977     
18978     fillDow: function()
18979     {
18980         var dowCnt = this.weekStart;
18981         
18982         var dow = {
18983             tag: 'tr',
18984             cn: [
18985                 
18986             ]
18987         };
18988         
18989         if(this.calendarWeeks){
18990             dow.cn.push({
18991                 tag: 'th',
18992                 cls: 'cw',
18993                 html: '&nbsp;'
18994             })
18995         }
18996         
18997         while (dowCnt < this.weekStart + 7) {
18998             dow.cn.push({
18999                 tag: 'th',
19000                 cls: 'dow',
19001                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19002             });
19003         }
19004         
19005         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19006     },
19007     
19008     fillMonths: function()
19009     {    
19010         var i = 0;
19011         var months = this.picker().select('>.datepicker-months td', true).first();
19012         
19013         months.dom.innerHTML = '';
19014         
19015         while (i < 12) {
19016             var month = {
19017                 tag: 'span',
19018                 cls: 'month',
19019                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19020             };
19021             
19022             months.createChild(month);
19023         }
19024         
19025     },
19026     
19027     update: function()
19028     {
19029         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;
19030         
19031         if (this.date < this.startDate) {
19032             this.viewDate = new Date(this.startDate);
19033         } else if (this.date > this.endDate) {
19034             this.viewDate = new Date(this.endDate);
19035         } else {
19036             this.viewDate = new Date(this.date);
19037         }
19038         
19039         this.fill();
19040     },
19041     
19042     fill: function() 
19043     {
19044         var d = new Date(this.viewDate),
19045                 year = d.getUTCFullYear(),
19046                 month = d.getUTCMonth(),
19047                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19048                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19049                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19050                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19051                 currentDate = this.date && this.date.valueOf(),
19052                 today = this.UTCToday();
19053         
19054         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19055         
19056 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19057         
19058 //        this.picker.select('>tfoot th.today').
19059 //                                              .text(dates[this.language].today)
19060 //                                              .toggle(this.todayBtn !== false);
19061     
19062         this.updateNavArrows();
19063         this.fillMonths();
19064                                                 
19065         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19066         
19067         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19068          
19069         prevMonth.setUTCDate(day);
19070         
19071         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19072         
19073         var nextMonth = new Date(prevMonth);
19074         
19075         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19076         
19077         nextMonth = nextMonth.valueOf();
19078         
19079         var fillMonths = false;
19080         
19081         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19082         
19083         while(prevMonth.valueOf() <= nextMonth) {
19084             var clsName = '';
19085             
19086             if (prevMonth.getUTCDay() === this.weekStart) {
19087                 if(fillMonths){
19088                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19089                 }
19090                     
19091                 fillMonths = {
19092                     tag: 'tr',
19093                     cn: []
19094                 };
19095                 
19096                 if(this.calendarWeeks){
19097                     // ISO 8601: First week contains first thursday.
19098                     // ISO also states week starts on Monday, but we can be more abstract here.
19099                     var
19100                     // Start of current week: based on weekstart/current date
19101                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19102                     // Thursday of this week
19103                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19104                     // First Thursday of year, year from thursday
19105                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19106                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19107                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19108                     
19109                     fillMonths.cn.push({
19110                         tag: 'td',
19111                         cls: 'cw',
19112                         html: calWeek
19113                     });
19114                 }
19115             }
19116             
19117             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19118                 clsName += ' old';
19119             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19120                 clsName += ' new';
19121             }
19122             if (this.todayHighlight &&
19123                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19124                 prevMonth.getUTCMonth() == today.getMonth() &&
19125                 prevMonth.getUTCDate() == today.getDate()) {
19126                 clsName += ' today';
19127             }
19128             
19129             if (currentDate && prevMonth.valueOf() === currentDate) {
19130                 clsName += ' active';
19131             }
19132             
19133             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19134                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19135                     clsName += ' disabled';
19136             }
19137             
19138             fillMonths.cn.push({
19139                 tag: 'td',
19140                 cls: 'day ' + clsName,
19141                 html: prevMonth.getDate()
19142             });
19143             
19144             prevMonth.setDate(prevMonth.getDate()+1);
19145         }
19146           
19147         var currentYear = this.date && this.date.getUTCFullYear();
19148         var currentMonth = this.date && this.date.getUTCMonth();
19149         
19150         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19151         
19152         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19153             v.removeClass('active');
19154             
19155             if(currentYear === year && k === currentMonth){
19156                 v.addClass('active');
19157             }
19158             
19159             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19160                 v.addClass('disabled');
19161             }
19162             
19163         });
19164         
19165         
19166         year = parseInt(year/10, 10) * 10;
19167         
19168         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19169         
19170         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19171         
19172         year -= 1;
19173         for (var i = -1; i < 11; i++) {
19174             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19175                 tag: 'span',
19176                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19177                 html: year
19178             });
19179             
19180             year += 1;
19181         }
19182     },
19183     
19184     showMode: function(dir) 
19185     {
19186         if (dir) {
19187             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19188         }
19189         
19190         Roo.each(this.picker().select('>div',true).elements, function(v){
19191             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19192             v.hide();
19193         });
19194         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19195     },
19196     
19197     place: function()
19198     {
19199         if(this.isInline) {
19200             return;
19201         }
19202         
19203         this.picker().removeClass(['bottom', 'top']);
19204         
19205         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19206             /*
19207              * place to the top of element!
19208              *
19209              */
19210             
19211             this.picker().addClass('top');
19212             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19213             
19214             return;
19215         }
19216         
19217         this.picker().addClass('bottom');
19218         
19219         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19220     },
19221     
19222     parseDate : function(value)
19223     {
19224         if(!value || value instanceof Date){
19225             return value;
19226         }
19227         var v = Date.parseDate(value, this.format);
19228         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19229             v = Date.parseDate(value, 'Y-m-d');
19230         }
19231         if(!v && this.altFormats){
19232             if(!this.altFormatsArray){
19233                 this.altFormatsArray = this.altFormats.split("|");
19234             }
19235             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19236                 v = Date.parseDate(value, this.altFormatsArray[i]);
19237             }
19238         }
19239         return v;
19240     },
19241     
19242     formatDate : function(date, fmt)
19243     {   
19244         return (!date || !(date instanceof Date)) ?
19245         date : date.dateFormat(fmt || this.format);
19246     },
19247     
19248     onFocus : function()
19249     {
19250         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19251         this.showPopup();
19252     },
19253     
19254     onBlur : function()
19255     {
19256         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19257         
19258         var d = this.inputEl().getValue();
19259         
19260         this.setValue(d);
19261                 
19262         this.hidePopup();
19263     },
19264     
19265     showPopup : function()
19266     {
19267         this.picker().show();
19268         this.update();
19269         this.place();
19270         
19271         this.fireEvent('showpopup', this, this.date);
19272     },
19273     
19274     hidePopup : function()
19275     {
19276         if(this.isInline) {
19277             return;
19278         }
19279         this.picker().hide();
19280         this.viewMode = this.startViewMode;
19281         this.showMode();
19282         
19283         this.fireEvent('hidepopup', this, this.date);
19284         
19285     },
19286     
19287     onMousedown: function(e)
19288     {
19289         e.stopPropagation();
19290         e.preventDefault();
19291     },
19292     
19293     keyup: function(e)
19294     {
19295         Roo.bootstrap.DateField.superclass.keyup.call(this);
19296         this.update();
19297     },
19298
19299     setValue: function(v)
19300     {
19301         if(this.fireEvent('beforeselect', this, v) !== false){
19302             var d = new Date(this.parseDate(v) ).clearTime();
19303         
19304             if(isNaN(d.getTime())){
19305                 this.date = this.viewDate = '';
19306                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19307                 return;
19308             }
19309
19310             v = this.formatDate(d);
19311
19312             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19313
19314             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19315
19316             this.update();
19317
19318             this.fireEvent('select', this, this.date);
19319         }
19320     },
19321     
19322     getValue: function()
19323     {
19324         return this.formatDate(this.date);
19325     },
19326     
19327     fireKey: function(e)
19328     {
19329         if (!this.picker().isVisible()){
19330             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19331                 this.showPopup();
19332             }
19333             return;
19334         }
19335         
19336         var dateChanged = false,
19337         dir, day, month,
19338         newDate, newViewDate;
19339         
19340         switch(e.keyCode){
19341             case 27: // escape
19342                 this.hidePopup();
19343                 e.preventDefault();
19344                 break;
19345             case 37: // left
19346             case 39: // right
19347                 if (!this.keyboardNavigation) {
19348                     break;
19349                 }
19350                 dir = e.keyCode == 37 ? -1 : 1;
19351                 
19352                 if (e.ctrlKey){
19353                     newDate = this.moveYear(this.date, dir);
19354                     newViewDate = this.moveYear(this.viewDate, dir);
19355                 } else if (e.shiftKey){
19356                     newDate = this.moveMonth(this.date, dir);
19357                     newViewDate = this.moveMonth(this.viewDate, dir);
19358                 } else {
19359                     newDate = new Date(this.date);
19360                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19361                     newViewDate = new Date(this.viewDate);
19362                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19363                 }
19364                 if (this.dateWithinRange(newDate)){
19365                     this.date = newDate;
19366                     this.viewDate = newViewDate;
19367                     this.setValue(this.formatDate(this.date));
19368 //                    this.update();
19369                     e.preventDefault();
19370                     dateChanged = true;
19371                 }
19372                 break;
19373             case 38: // up
19374             case 40: // down
19375                 if (!this.keyboardNavigation) {
19376                     break;
19377                 }
19378                 dir = e.keyCode == 38 ? -1 : 1;
19379                 if (e.ctrlKey){
19380                     newDate = this.moveYear(this.date, dir);
19381                     newViewDate = this.moveYear(this.viewDate, dir);
19382                 } else if (e.shiftKey){
19383                     newDate = this.moveMonth(this.date, dir);
19384                     newViewDate = this.moveMonth(this.viewDate, dir);
19385                 } else {
19386                     newDate = new Date(this.date);
19387                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19388                     newViewDate = new Date(this.viewDate);
19389                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19390                 }
19391                 if (this.dateWithinRange(newDate)){
19392                     this.date = newDate;
19393                     this.viewDate = newViewDate;
19394                     this.setValue(this.formatDate(this.date));
19395 //                    this.update();
19396                     e.preventDefault();
19397                     dateChanged = true;
19398                 }
19399                 break;
19400             case 13: // enter
19401                 this.setValue(this.formatDate(this.date));
19402                 this.hidePopup();
19403                 e.preventDefault();
19404                 break;
19405             case 9: // tab
19406                 this.setValue(this.formatDate(this.date));
19407                 this.hidePopup();
19408                 break;
19409             case 16: // shift
19410             case 17: // ctrl
19411             case 18: // alt
19412                 break;
19413             default :
19414                 this.hidePopup();
19415                 
19416         }
19417     },
19418     
19419     
19420     onClick: function(e) 
19421     {
19422         e.stopPropagation();
19423         e.preventDefault();
19424         
19425         var target = e.getTarget();
19426         
19427         if(target.nodeName.toLowerCase() === 'i'){
19428             target = Roo.get(target).dom.parentNode;
19429         }
19430         
19431         var nodeName = target.nodeName;
19432         var className = target.className;
19433         var html = target.innerHTML;
19434         //Roo.log(nodeName);
19435         
19436         switch(nodeName.toLowerCase()) {
19437             case 'th':
19438                 switch(className) {
19439                     case 'switch':
19440                         this.showMode(1);
19441                         break;
19442                     case 'prev':
19443                     case 'next':
19444                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19445                         switch(this.viewMode){
19446                                 case 0:
19447                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19448                                         break;
19449                                 case 1:
19450                                 case 2:
19451                                         this.viewDate = this.moveYear(this.viewDate, dir);
19452                                         break;
19453                         }
19454                         this.fill();
19455                         break;
19456                     case 'today':
19457                         var date = new Date();
19458                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19459 //                        this.fill()
19460                         this.setValue(this.formatDate(this.date));
19461                         
19462                         this.hidePopup();
19463                         break;
19464                 }
19465                 break;
19466             case 'span':
19467                 if (className.indexOf('disabled') < 0) {
19468                     this.viewDate.setUTCDate(1);
19469                     if (className.indexOf('month') > -1) {
19470                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19471                     } else {
19472                         var year = parseInt(html, 10) || 0;
19473                         this.viewDate.setUTCFullYear(year);
19474                         
19475                     }
19476                     
19477                     if(this.singleMode){
19478                         this.setValue(this.formatDate(this.viewDate));
19479                         this.hidePopup();
19480                         return;
19481                     }
19482                     
19483                     this.showMode(-1);
19484                     this.fill();
19485                 }
19486                 break;
19487                 
19488             case 'td':
19489                 //Roo.log(className);
19490                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19491                     var day = parseInt(html, 10) || 1;
19492                     var year = this.viewDate.getUTCFullYear(),
19493                         month = this.viewDate.getUTCMonth();
19494
19495                     if (className.indexOf('old') > -1) {
19496                         if(month === 0 ){
19497                             month = 11;
19498                             year -= 1;
19499                         }else{
19500                             month -= 1;
19501                         }
19502                     } else if (className.indexOf('new') > -1) {
19503                         if (month == 11) {
19504                             month = 0;
19505                             year += 1;
19506                         } else {
19507                             month += 1;
19508                         }
19509                     }
19510                     //Roo.log([year,month,day]);
19511                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19512                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19513 //                    this.fill();
19514                     //Roo.log(this.formatDate(this.date));
19515                     this.setValue(this.formatDate(this.date));
19516                     this.hidePopup();
19517                 }
19518                 break;
19519         }
19520     },
19521     
19522     setStartDate: function(startDate)
19523     {
19524         this.startDate = startDate || -Infinity;
19525         if (this.startDate !== -Infinity) {
19526             this.startDate = this.parseDate(this.startDate);
19527         }
19528         this.update();
19529         this.updateNavArrows();
19530     },
19531
19532     setEndDate: function(endDate)
19533     {
19534         this.endDate = endDate || Infinity;
19535         if (this.endDate !== Infinity) {
19536             this.endDate = this.parseDate(this.endDate);
19537         }
19538         this.update();
19539         this.updateNavArrows();
19540     },
19541     
19542     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19543     {
19544         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19545         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19546             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19547         }
19548         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19549             return parseInt(d, 10);
19550         });
19551         this.update();
19552         this.updateNavArrows();
19553     },
19554     
19555     updateNavArrows: function() 
19556     {
19557         if(this.singleMode){
19558             return;
19559         }
19560         
19561         var d = new Date(this.viewDate),
19562         year = d.getUTCFullYear(),
19563         month = d.getUTCMonth();
19564         
19565         Roo.each(this.picker().select('.prev', true).elements, function(v){
19566             v.show();
19567             switch (this.viewMode) {
19568                 case 0:
19569
19570                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19571                         v.hide();
19572                     }
19573                     break;
19574                 case 1:
19575                 case 2:
19576                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19577                         v.hide();
19578                     }
19579                     break;
19580             }
19581         });
19582         
19583         Roo.each(this.picker().select('.next', true).elements, function(v){
19584             v.show();
19585             switch (this.viewMode) {
19586                 case 0:
19587
19588                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19589                         v.hide();
19590                     }
19591                     break;
19592                 case 1:
19593                 case 2:
19594                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19595                         v.hide();
19596                     }
19597                     break;
19598             }
19599         })
19600     },
19601     
19602     moveMonth: function(date, dir)
19603     {
19604         if (!dir) {
19605             return date;
19606         }
19607         var new_date = new Date(date.valueOf()),
19608         day = new_date.getUTCDate(),
19609         month = new_date.getUTCMonth(),
19610         mag = Math.abs(dir),
19611         new_month, test;
19612         dir = dir > 0 ? 1 : -1;
19613         if (mag == 1){
19614             test = dir == -1
19615             // If going back one month, make sure month is not current month
19616             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19617             ? function(){
19618                 return new_date.getUTCMonth() == month;
19619             }
19620             // If going forward one month, make sure month is as expected
19621             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19622             : function(){
19623                 return new_date.getUTCMonth() != new_month;
19624             };
19625             new_month = month + dir;
19626             new_date.setUTCMonth(new_month);
19627             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19628             if (new_month < 0 || new_month > 11) {
19629                 new_month = (new_month + 12) % 12;
19630             }
19631         } else {
19632             // For magnitudes >1, move one month at a time...
19633             for (var i=0; i<mag; i++) {
19634                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19635                 new_date = this.moveMonth(new_date, dir);
19636             }
19637             // ...then reset the day, keeping it in the new month
19638             new_month = new_date.getUTCMonth();
19639             new_date.setUTCDate(day);
19640             test = function(){
19641                 return new_month != new_date.getUTCMonth();
19642             };
19643         }
19644         // Common date-resetting loop -- if date is beyond end of month, make it
19645         // end of month
19646         while (test()){
19647             new_date.setUTCDate(--day);
19648             new_date.setUTCMonth(new_month);
19649         }
19650         return new_date;
19651     },
19652
19653     moveYear: function(date, dir)
19654     {
19655         return this.moveMonth(date, dir*12);
19656     },
19657
19658     dateWithinRange: function(date)
19659     {
19660         return date >= this.startDate && date <= this.endDate;
19661     },
19662
19663     
19664     remove: function() 
19665     {
19666         this.picker().remove();
19667     },
19668     
19669     validateValue : function(value)
19670     {
19671         if(this.getVisibilityEl().hasClass('hidden')){
19672             return true;
19673         }
19674         
19675         if(value.length < 1)  {
19676             if(this.allowBlank){
19677                 return true;
19678             }
19679             return false;
19680         }
19681         
19682         if(value.length < this.minLength){
19683             return false;
19684         }
19685         if(value.length > this.maxLength){
19686             return false;
19687         }
19688         if(this.vtype){
19689             var vt = Roo.form.VTypes;
19690             if(!vt[this.vtype](value, this)){
19691                 return false;
19692             }
19693         }
19694         if(typeof this.validator == "function"){
19695             var msg = this.validator(value);
19696             if(msg !== true){
19697                 return false;
19698             }
19699         }
19700         
19701         if(this.regex && !this.regex.test(value)){
19702             return false;
19703         }
19704         
19705         if(typeof(this.parseDate(value)) == 'undefined'){
19706             return false;
19707         }
19708         
19709         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19710             return false;
19711         }      
19712         
19713         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19714             return false;
19715         } 
19716         
19717         
19718         return true;
19719     },
19720     
19721     reset : function()
19722     {
19723         this.date = this.viewDate = '';
19724         
19725         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19726     }
19727    
19728 });
19729
19730 Roo.apply(Roo.bootstrap.DateField,  {
19731     
19732     head : {
19733         tag: 'thead',
19734         cn: [
19735         {
19736             tag: 'tr',
19737             cn: [
19738             {
19739                 tag: 'th',
19740                 cls: 'prev',
19741                 html: '<i class="fa fa-arrow-left"/>'
19742             },
19743             {
19744                 tag: 'th',
19745                 cls: 'switch',
19746                 colspan: '5'
19747             },
19748             {
19749                 tag: 'th',
19750                 cls: 'next',
19751                 html: '<i class="fa fa-arrow-right"/>'
19752             }
19753
19754             ]
19755         }
19756         ]
19757     },
19758     
19759     content : {
19760         tag: 'tbody',
19761         cn: [
19762         {
19763             tag: 'tr',
19764             cn: [
19765             {
19766                 tag: 'td',
19767                 colspan: '7'
19768             }
19769             ]
19770         }
19771         ]
19772     },
19773     
19774     footer : {
19775         tag: 'tfoot',
19776         cn: [
19777         {
19778             tag: 'tr',
19779             cn: [
19780             {
19781                 tag: 'th',
19782                 colspan: '7',
19783                 cls: 'today'
19784             }
19785                     
19786             ]
19787         }
19788         ]
19789     },
19790     
19791     dates:{
19792         en: {
19793             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19794             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19795             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19796             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19797             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19798             today: "Today"
19799         }
19800     },
19801     
19802     modes: [
19803     {
19804         clsName: 'days',
19805         navFnc: 'Month',
19806         navStep: 1
19807     },
19808     {
19809         clsName: 'months',
19810         navFnc: 'FullYear',
19811         navStep: 1
19812     },
19813     {
19814         clsName: 'years',
19815         navFnc: 'FullYear',
19816         navStep: 10
19817     }]
19818 });
19819
19820 Roo.apply(Roo.bootstrap.DateField,  {
19821   
19822     template : {
19823         tag: 'div',
19824         cls: 'datepicker dropdown-menu roo-dynamic',
19825         cn: [
19826         {
19827             tag: 'div',
19828             cls: 'datepicker-days',
19829             cn: [
19830             {
19831                 tag: 'table',
19832                 cls: 'table-condensed',
19833                 cn:[
19834                 Roo.bootstrap.DateField.head,
19835                 {
19836                     tag: 'tbody'
19837                 },
19838                 Roo.bootstrap.DateField.footer
19839                 ]
19840             }
19841             ]
19842         },
19843         {
19844             tag: 'div',
19845             cls: 'datepicker-months',
19846             cn: [
19847             {
19848                 tag: 'table',
19849                 cls: 'table-condensed',
19850                 cn:[
19851                 Roo.bootstrap.DateField.head,
19852                 Roo.bootstrap.DateField.content,
19853                 Roo.bootstrap.DateField.footer
19854                 ]
19855             }
19856             ]
19857         },
19858         {
19859             tag: 'div',
19860             cls: 'datepicker-years',
19861             cn: [
19862             {
19863                 tag: 'table',
19864                 cls: 'table-condensed',
19865                 cn:[
19866                 Roo.bootstrap.DateField.head,
19867                 Roo.bootstrap.DateField.content,
19868                 Roo.bootstrap.DateField.footer
19869                 ]
19870             }
19871             ]
19872         }
19873         ]
19874     }
19875 });
19876
19877  
19878
19879  /*
19880  * - LGPL
19881  *
19882  * TimeField
19883  * 
19884  */
19885
19886 /**
19887  * @class Roo.bootstrap.TimeField
19888  * @extends Roo.bootstrap.Input
19889  * Bootstrap DateField class
19890  * 
19891  * 
19892  * @constructor
19893  * Create a new TimeField
19894  * @param {Object} config The config object
19895  */
19896
19897 Roo.bootstrap.TimeField = function(config){
19898     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19899     this.addEvents({
19900             /**
19901              * @event show
19902              * Fires when this field show.
19903              * @param {Roo.bootstrap.DateField} thisthis
19904              * @param {Mixed} date The date value
19905              */
19906             show : true,
19907             /**
19908              * @event show
19909              * Fires when this field hide.
19910              * @param {Roo.bootstrap.DateField} this
19911              * @param {Mixed} date The date value
19912              */
19913             hide : true,
19914             /**
19915              * @event select
19916              * Fires when select a date.
19917              * @param {Roo.bootstrap.DateField} this
19918              * @param {Mixed} date The date value
19919              */
19920             select : true
19921         });
19922 };
19923
19924 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19925     
19926     /**
19927      * @cfg {String} format
19928      * The default time format string which can be overriden for localization support.  The format must be
19929      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19930      */
19931     format : "H:i",
19932        
19933     onRender: function(ct, position)
19934     {
19935         
19936         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19937                 
19938         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19939         
19940         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19941         
19942         this.pop = this.picker().select('>.datepicker-time',true).first();
19943         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19944         
19945         this.picker().on('mousedown', this.onMousedown, this);
19946         this.picker().on('click', this.onClick, this);
19947         
19948         this.picker().addClass('datepicker-dropdown');
19949     
19950         this.fillTime();
19951         this.update();
19952             
19953         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19954         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19955         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19956         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19957         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19958         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19959
19960     },
19961     
19962     fireKey: function(e){
19963         if (!this.picker().isVisible()){
19964             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19965                 this.show();
19966             }
19967             return;
19968         }
19969
19970         e.preventDefault();
19971         
19972         switch(e.keyCode){
19973             case 27: // escape
19974                 this.hide();
19975                 break;
19976             case 37: // left
19977             case 39: // right
19978                 this.onTogglePeriod();
19979                 break;
19980             case 38: // up
19981                 this.onIncrementMinutes();
19982                 break;
19983             case 40: // down
19984                 this.onDecrementMinutes();
19985                 break;
19986             case 13: // enter
19987             case 9: // tab
19988                 this.setTime();
19989                 break;
19990         }
19991     },
19992     
19993     onClick: function(e) {
19994         e.stopPropagation();
19995         e.preventDefault();
19996     },
19997     
19998     picker : function()
19999     {
20000         return this.el.select('.datepicker', true).first();
20001     },
20002     
20003     fillTime: function()
20004     {    
20005         var time = this.pop.select('tbody', true).first();
20006         
20007         time.dom.innerHTML = '';
20008         
20009         time.createChild({
20010             tag: 'tr',
20011             cn: [
20012                 {
20013                     tag: 'td',
20014                     cn: [
20015                         {
20016                             tag: 'a',
20017                             href: '#',
20018                             cls: 'btn',
20019                             cn: [
20020                                 {
20021                                     tag: 'span',
20022                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
20023                                 }
20024                             ]
20025                         } 
20026                     ]
20027                 },
20028                 {
20029                     tag: 'td',
20030                     cls: 'separator'
20031                 },
20032                 {
20033                     tag: 'td',
20034                     cn: [
20035                         {
20036                             tag: 'a',
20037                             href: '#',
20038                             cls: 'btn',
20039                             cn: [
20040                                 {
20041                                     tag: 'span',
20042                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
20043                                 }
20044                             ]
20045                         }
20046                     ]
20047                 },
20048                 {
20049                     tag: 'td',
20050                     cls: 'separator'
20051                 }
20052             ]
20053         });
20054         
20055         time.createChild({
20056             tag: 'tr',
20057             cn: [
20058                 {
20059                     tag: 'td',
20060                     cn: [
20061                         {
20062                             tag: 'span',
20063                             cls: 'timepicker-hour',
20064                             html: '00'
20065                         }  
20066                     ]
20067                 },
20068                 {
20069                     tag: 'td',
20070                     cls: 'separator',
20071                     html: ':'
20072                 },
20073                 {
20074                     tag: 'td',
20075                     cn: [
20076                         {
20077                             tag: 'span',
20078                             cls: 'timepicker-minute',
20079                             html: '00'
20080                         }  
20081                     ]
20082                 },
20083                 {
20084                     tag: 'td',
20085                     cls: 'separator'
20086                 },
20087                 {
20088                     tag: 'td',
20089                     cn: [
20090                         {
20091                             tag: 'button',
20092                             type: 'button',
20093                             cls: 'btn btn-primary period',
20094                             html: 'AM'
20095                             
20096                         }
20097                     ]
20098                 }
20099             ]
20100         });
20101         
20102         time.createChild({
20103             tag: 'tr',
20104             cn: [
20105                 {
20106                     tag: 'td',
20107                     cn: [
20108                         {
20109                             tag: 'a',
20110                             href: '#',
20111                             cls: 'btn',
20112                             cn: [
20113                                 {
20114                                     tag: 'span',
20115                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20116                                 }
20117                             ]
20118                         }
20119                     ]
20120                 },
20121                 {
20122                     tag: 'td',
20123                     cls: 'separator'
20124                 },
20125                 {
20126                     tag: 'td',
20127                     cn: [
20128                         {
20129                             tag: 'a',
20130                             href: '#',
20131                             cls: 'btn',
20132                             cn: [
20133                                 {
20134                                     tag: 'span',
20135                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20136                                 }
20137                             ]
20138                         }
20139                     ]
20140                 },
20141                 {
20142                     tag: 'td',
20143                     cls: 'separator'
20144                 }
20145             ]
20146         });
20147         
20148     },
20149     
20150     update: function()
20151     {
20152         
20153         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20154         
20155         this.fill();
20156     },
20157     
20158     fill: function() 
20159     {
20160         var hours = this.time.getHours();
20161         var minutes = this.time.getMinutes();
20162         var period = 'AM';
20163         
20164         if(hours > 11){
20165             period = 'PM';
20166         }
20167         
20168         if(hours == 0){
20169             hours = 12;
20170         }
20171         
20172         
20173         if(hours > 12){
20174             hours = hours - 12;
20175         }
20176         
20177         if(hours < 10){
20178             hours = '0' + hours;
20179         }
20180         
20181         if(minutes < 10){
20182             minutes = '0' + minutes;
20183         }
20184         
20185         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20186         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20187         this.pop.select('button', true).first().dom.innerHTML = period;
20188         
20189     },
20190     
20191     place: function()
20192     {   
20193         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20194         
20195         var cls = ['bottom'];
20196         
20197         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20198             cls.pop();
20199             cls.push('top');
20200         }
20201         
20202         cls.push('right');
20203         
20204         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20205             cls.pop();
20206             cls.push('left');
20207         }
20208         
20209         this.picker().addClass(cls.join('-'));
20210         
20211         var _this = this;
20212         
20213         Roo.each(cls, function(c){
20214             if(c == 'bottom'){
20215                 _this.picker().setTop(_this.inputEl().getHeight());
20216                 return;
20217             }
20218             if(c == 'top'){
20219                 _this.picker().setTop(0 - _this.picker().getHeight());
20220                 return;
20221             }
20222             
20223             if(c == 'left'){
20224                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20225                 return;
20226             }
20227             if(c == 'right'){
20228                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20229                 return;
20230             }
20231         });
20232         
20233     },
20234   
20235     onFocus : function()
20236     {
20237         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20238         this.show();
20239     },
20240     
20241     onBlur : function()
20242     {
20243         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20244         this.hide();
20245     },
20246     
20247     show : function()
20248     {
20249         this.picker().show();
20250         this.pop.show();
20251         this.update();
20252         this.place();
20253         
20254         this.fireEvent('show', this, this.date);
20255     },
20256     
20257     hide : function()
20258     {
20259         this.picker().hide();
20260         this.pop.hide();
20261         
20262         this.fireEvent('hide', this, this.date);
20263     },
20264     
20265     setTime : function()
20266     {
20267         this.hide();
20268         this.setValue(this.time.format(this.format));
20269         
20270         this.fireEvent('select', this, this.date);
20271         
20272         
20273     },
20274     
20275     onMousedown: function(e){
20276         e.stopPropagation();
20277         e.preventDefault();
20278     },
20279     
20280     onIncrementHours: function()
20281     {
20282         Roo.log('onIncrementHours');
20283         this.time = this.time.add(Date.HOUR, 1);
20284         this.update();
20285         
20286     },
20287     
20288     onDecrementHours: function()
20289     {
20290         Roo.log('onDecrementHours');
20291         this.time = this.time.add(Date.HOUR, -1);
20292         this.update();
20293     },
20294     
20295     onIncrementMinutes: function()
20296     {
20297         Roo.log('onIncrementMinutes');
20298         this.time = this.time.add(Date.MINUTE, 1);
20299         this.update();
20300     },
20301     
20302     onDecrementMinutes: function()
20303     {
20304         Roo.log('onDecrementMinutes');
20305         this.time = this.time.add(Date.MINUTE, -1);
20306         this.update();
20307     },
20308     
20309     onTogglePeriod: function()
20310     {
20311         Roo.log('onTogglePeriod');
20312         this.time = this.time.add(Date.HOUR, 12);
20313         this.update();
20314     }
20315     
20316    
20317 });
20318
20319 Roo.apply(Roo.bootstrap.TimeField,  {
20320     
20321     content : {
20322         tag: 'tbody',
20323         cn: [
20324             {
20325                 tag: 'tr',
20326                 cn: [
20327                 {
20328                     tag: 'td',
20329                     colspan: '7'
20330                 }
20331                 ]
20332             }
20333         ]
20334     },
20335     
20336     footer : {
20337         tag: 'tfoot',
20338         cn: [
20339             {
20340                 tag: 'tr',
20341                 cn: [
20342                 {
20343                     tag: 'th',
20344                     colspan: '7',
20345                     cls: '',
20346                     cn: [
20347                         {
20348                             tag: 'button',
20349                             cls: 'btn btn-info ok',
20350                             html: 'OK'
20351                         }
20352                     ]
20353                 }
20354
20355                 ]
20356             }
20357         ]
20358     }
20359 });
20360
20361 Roo.apply(Roo.bootstrap.TimeField,  {
20362   
20363     template : {
20364         tag: 'div',
20365         cls: 'datepicker dropdown-menu',
20366         cn: [
20367             {
20368                 tag: 'div',
20369                 cls: 'datepicker-time',
20370                 cn: [
20371                 {
20372                     tag: 'table',
20373                     cls: 'table-condensed',
20374                     cn:[
20375                     Roo.bootstrap.TimeField.content,
20376                     Roo.bootstrap.TimeField.footer
20377                     ]
20378                 }
20379                 ]
20380             }
20381         ]
20382     }
20383 });
20384
20385  
20386
20387  /*
20388  * - LGPL
20389  *
20390  * MonthField
20391  * 
20392  */
20393
20394 /**
20395  * @class Roo.bootstrap.MonthField
20396  * @extends Roo.bootstrap.Input
20397  * Bootstrap MonthField class
20398  * 
20399  * @cfg {String} language default en
20400  * 
20401  * @constructor
20402  * Create a new MonthField
20403  * @param {Object} config The config object
20404  */
20405
20406 Roo.bootstrap.MonthField = function(config){
20407     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20408     
20409     this.addEvents({
20410         /**
20411          * @event show
20412          * Fires when this field show.
20413          * @param {Roo.bootstrap.MonthField} this
20414          * @param {Mixed} date The date value
20415          */
20416         show : true,
20417         /**
20418          * @event show
20419          * Fires when this field hide.
20420          * @param {Roo.bootstrap.MonthField} this
20421          * @param {Mixed} date The date value
20422          */
20423         hide : true,
20424         /**
20425          * @event select
20426          * Fires when select a date.
20427          * @param {Roo.bootstrap.MonthField} this
20428          * @param {String} oldvalue The old value
20429          * @param {String} newvalue The new value
20430          */
20431         select : true
20432     });
20433 };
20434
20435 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20436     
20437     onRender: function(ct, position)
20438     {
20439         
20440         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20441         
20442         this.language = this.language || 'en';
20443         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20444         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20445         
20446         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20447         this.isInline = false;
20448         this.isInput = true;
20449         this.component = this.el.select('.add-on', true).first() || false;
20450         this.component = (this.component && this.component.length === 0) ? false : this.component;
20451         this.hasInput = this.component && this.inputEL().length;
20452         
20453         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20454         
20455         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20456         
20457         this.picker().on('mousedown', this.onMousedown, this);
20458         this.picker().on('click', this.onClick, this);
20459         
20460         this.picker().addClass('datepicker-dropdown');
20461         
20462         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20463             v.setStyle('width', '189px');
20464         });
20465         
20466         this.fillMonths();
20467         
20468         this.update();
20469         
20470         if(this.isInline) {
20471             this.show();
20472         }
20473         
20474     },
20475     
20476     setValue: function(v, suppressEvent)
20477     {   
20478         var o = this.getValue();
20479         
20480         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20481         
20482         this.update();
20483
20484         if(suppressEvent !== true){
20485             this.fireEvent('select', this, o, v);
20486         }
20487         
20488     },
20489     
20490     getValue: function()
20491     {
20492         return this.value;
20493     },
20494     
20495     onClick: function(e) 
20496     {
20497         e.stopPropagation();
20498         e.preventDefault();
20499         
20500         var target = e.getTarget();
20501         
20502         if(target.nodeName.toLowerCase() === 'i'){
20503             target = Roo.get(target).dom.parentNode;
20504         }
20505         
20506         var nodeName = target.nodeName;
20507         var className = target.className;
20508         var html = target.innerHTML;
20509         
20510         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20511             return;
20512         }
20513         
20514         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20515         
20516         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20517         
20518         this.hide();
20519                         
20520     },
20521     
20522     picker : function()
20523     {
20524         return this.pickerEl;
20525     },
20526     
20527     fillMonths: function()
20528     {    
20529         var i = 0;
20530         var months = this.picker().select('>.datepicker-months td', true).first();
20531         
20532         months.dom.innerHTML = '';
20533         
20534         while (i < 12) {
20535             var month = {
20536                 tag: 'span',
20537                 cls: 'month',
20538                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20539             };
20540             
20541             months.createChild(month);
20542         }
20543         
20544     },
20545     
20546     update: function()
20547     {
20548         var _this = this;
20549         
20550         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20551             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20552         }
20553         
20554         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20555             e.removeClass('active');
20556             
20557             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20558                 e.addClass('active');
20559             }
20560         })
20561     },
20562     
20563     place: function()
20564     {
20565         if(this.isInline) {
20566             return;
20567         }
20568         
20569         this.picker().removeClass(['bottom', 'top']);
20570         
20571         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20572             /*
20573              * place to the top of element!
20574              *
20575              */
20576             
20577             this.picker().addClass('top');
20578             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20579             
20580             return;
20581         }
20582         
20583         this.picker().addClass('bottom');
20584         
20585         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20586     },
20587     
20588     onFocus : function()
20589     {
20590         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20591         this.show();
20592     },
20593     
20594     onBlur : function()
20595     {
20596         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20597         
20598         var d = this.inputEl().getValue();
20599         
20600         this.setValue(d);
20601                 
20602         this.hide();
20603     },
20604     
20605     show : function()
20606     {
20607         this.picker().show();
20608         this.picker().select('>.datepicker-months', true).first().show();
20609         this.update();
20610         this.place();
20611         
20612         this.fireEvent('show', this, this.date);
20613     },
20614     
20615     hide : function()
20616     {
20617         if(this.isInline) {
20618             return;
20619         }
20620         this.picker().hide();
20621         this.fireEvent('hide', this, this.date);
20622         
20623     },
20624     
20625     onMousedown: function(e)
20626     {
20627         e.stopPropagation();
20628         e.preventDefault();
20629     },
20630     
20631     keyup: function(e)
20632     {
20633         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20634         this.update();
20635     },
20636
20637     fireKey: function(e)
20638     {
20639         if (!this.picker().isVisible()){
20640             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20641                 this.show();
20642             }
20643             return;
20644         }
20645         
20646         var dir;
20647         
20648         switch(e.keyCode){
20649             case 27: // escape
20650                 this.hide();
20651                 e.preventDefault();
20652                 break;
20653             case 37: // left
20654             case 39: // right
20655                 dir = e.keyCode == 37 ? -1 : 1;
20656                 
20657                 this.vIndex = this.vIndex + dir;
20658                 
20659                 if(this.vIndex < 0){
20660                     this.vIndex = 0;
20661                 }
20662                 
20663                 if(this.vIndex > 11){
20664                     this.vIndex = 11;
20665                 }
20666                 
20667                 if(isNaN(this.vIndex)){
20668                     this.vIndex = 0;
20669                 }
20670                 
20671                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20672                 
20673                 break;
20674             case 38: // up
20675             case 40: // down
20676                 
20677                 dir = e.keyCode == 38 ? -1 : 1;
20678                 
20679                 this.vIndex = this.vIndex + dir * 4;
20680                 
20681                 if(this.vIndex < 0){
20682                     this.vIndex = 0;
20683                 }
20684                 
20685                 if(this.vIndex > 11){
20686                     this.vIndex = 11;
20687                 }
20688                 
20689                 if(isNaN(this.vIndex)){
20690                     this.vIndex = 0;
20691                 }
20692                 
20693                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20694                 break;
20695                 
20696             case 13: // enter
20697                 
20698                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20699                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20700                 }
20701                 
20702                 this.hide();
20703                 e.preventDefault();
20704                 break;
20705             case 9: // tab
20706                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20707                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20708                 }
20709                 this.hide();
20710                 break;
20711             case 16: // shift
20712             case 17: // ctrl
20713             case 18: // alt
20714                 break;
20715             default :
20716                 this.hide();
20717                 
20718         }
20719     },
20720     
20721     remove: function() 
20722     {
20723         this.picker().remove();
20724     }
20725    
20726 });
20727
20728 Roo.apply(Roo.bootstrap.MonthField,  {
20729     
20730     content : {
20731         tag: 'tbody',
20732         cn: [
20733         {
20734             tag: 'tr',
20735             cn: [
20736             {
20737                 tag: 'td',
20738                 colspan: '7'
20739             }
20740             ]
20741         }
20742         ]
20743     },
20744     
20745     dates:{
20746         en: {
20747             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20748             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20749         }
20750     }
20751 });
20752
20753 Roo.apply(Roo.bootstrap.MonthField,  {
20754   
20755     template : {
20756         tag: 'div',
20757         cls: 'datepicker dropdown-menu roo-dynamic',
20758         cn: [
20759             {
20760                 tag: 'div',
20761                 cls: 'datepicker-months',
20762                 cn: [
20763                 {
20764                     tag: 'table',
20765                     cls: 'table-condensed',
20766                     cn:[
20767                         Roo.bootstrap.DateField.content
20768                     ]
20769                 }
20770                 ]
20771             }
20772         ]
20773     }
20774 });
20775
20776  
20777
20778  
20779  /*
20780  * - LGPL
20781  *
20782  * CheckBox
20783  * 
20784  */
20785
20786 /**
20787  * @class Roo.bootstrap.CheckBox
20788  * @extends Roo.bootstrap.Input
20789  * Bootstrap CheckBox class
20790  * 
20791  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20792  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20793  * @cfg {String} boxLabel The text that appears beside the checkbox
20794  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20795  * @cfg {Boolean} checked initnal the element
20796  * @cfg {Boolean} inline inline the element (default false)
20797  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20798  * @cfg {String} tooltip label tooltip
20799  * 
20800  * @constructor
20801  * Create a new CheckBox
20802  * @param {Object} config The config object
20803  */
20804
20805 Roo.bootstrap.CheckBox = function(config){
20806     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20807    
20808     this.addEvents({
20809         /**
20810         * @event check
20811         * Fires when the element is checked or unchecked.
20812         * @param {Roo.bootstrap.CheckBox} this This input
20813         * @param {Boolean} checked The new checked value
20814         */
20815        check : true,
20816        /**
20817         * @event click
20818         * Fires when the element is click.
20819         * @param {Roo.bootstrap.CheckBox} this This input
20820         */
20821        click : true
20822     });
20823     
20824 };
20825
20826 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20827   
20828     inputType: 'checkbox',
20829     inputValue: 1,
20830     valueOff: 0,
20831     boxLabel: false,
20832     checked: false,
20833     weight : false,
20834     inline: false,
20835     tooltip : '',
20836     
20837     getAutoCreate : function()
20838     {
20839         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20840         
20841         var id = Roo.id();
20842         
20843         var cfg = {};
20844         
20845         cfg.cls = 'form-group ' + this.inputType; //input-group
20846         
20847         if(this.inline){
20848             cfg.cls += ' ' + this.inputType + '-inline';
20849         }
20850         
20851         var input =  {
20852             tag: 'input',
20853             id : id,
20854             type : this.inputType,
20855             value : this.inputValue,
20856             cls : 'roo-' + this.inputType, //'form-box',
20857             placeholder : this.placeholder || ''
20858             
20859         };
20860         
20861         if(this.inputType != 'radio'){
20862             var hidden =  {
20863                 tag: 'input',
20864                 type : 'hidden',
20865                 cls : 'roo-hidden-value',
20866                 value : this.checked ? this.inputValue : this.valueOff
20867             };
20868         }
20869         
20870             
20871         if (this.weight) { // Validity check?
20872             cfg.cls += " " + this.inputType + "-" + this.weight;
20873         }
20874         
20875         if (this.disabled) {
20876             input.disabled=true;
20877         }
20878         
20879         if(this.checked){
20880             input.checked = this.checked;
20881         }
20882         
20883         if (this.name) {
20884             
20885             input.name = this.name;
20886             
20887             if(this.inputType != 'radio'){
20888                 hidden.name = this.name;
20889                 input.name = '_hidden_' + this.name;
20890             }
20891         }
20892         
20893         if (this.size) {
20894             input.cls += ' input-' + this.size;
20895         }
20896         
20897         var settings=this;
20898         
20899         ['xs','sm','md','lg'].map(function(size){
20900             if (settings[size]) {
20901                 cfg.cls += ' col-' + size + '-' + settings[size];
20902             }
20903         });
20904         
20905         var inputblock = input;
20906          
20907         if (this.before || this.after) {
20908             
20909             inputblock = {
20910                 cls : 'input-group',
20911                 cn :  [] 
20912             };
20913             
20914             if (this.before) {
20915                 inputblock.cn.push({
20916                     tag :'span',
20917                     cls : 'input-group-addon',
20918                     html : this.before
20919                 });
20920             }
20921             
20922             inputblock.cn.push(input);
20923             
20924             if(this.inputType != 'radio'){
20925                 inputblock.cn.push(hidden);
20926             }
20927             
20928             if (this.after) {
20929                 inputblock.cn.push({
20930                     tag :'span',
20931                     cls : 'input-group-addon',
20932                     html : this.after
20933                 });
20934             }
20935             
20936         }
20937         
20938         if (align ==='left' && this.fieldLabel.length) {
20939 //                Roo.log("left and has label");
20940             cfg.cn = [
20941                 {
20942                     tag: 'label',
20943                     'for' :  id,
20944                     cls : 'control-label',
20945                     html : this.fieldLabel
20946                 },
20947                 {
20948                     cls : "", 
20949                     cn: [
20950                         inputblock
20951                     ]
20952                 }
20953             ];
20954             
20955             if(this.labelWidth > 12){
20956                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20957             }
20958             
20959             if(this.labelWidth < 13 && this.labelmd == 0){
20960                 this.labelmd = this.labelWidth;
20961             }
20962             
20963             if(this.labellg > 0){
20964                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20965                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20966             }
20967             
20968             if(this.labelmd > 0){
20969                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20970                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20971             }
20972             
20973             if(this.labelsm > 0){
20974                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20975                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20976             }
20977             
20978             if(this.labelxs > 0){
20979                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20980                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20981             }
20982             
20983         } else if ( this.fieldLabel.length) {
20984 //                Roo.log(" label");
20985                 cfg.cn = [
20986                    
20987                     {
20988                         tag: this.boxLabel ? 'span' : 'label',
20989                         'for': id,
20990                         cls: 'control-label box-input-label',
20991                         //cls : 'input-group-addon',
20992                         html : this.fieldLabel
20993                     },
20994                     
20995                     inputblock
20996                     
20997                 ];
20998
20999         } else {
21000             
21001 //                Roo.log(" no label && no align");
21002                 cfg.cn = [  inputblock ] ;
21003                 
21004                 
21005         }
21006         
21007         if(this.boxLabel){
21008              var boxLabelCfg = {
21009                 tag: 'label',
21010                 //'for': id, // box label is handled by onclick - so no for...
21011                 cls: 'box-label',
21012                 html: this.boxLabel
21013             };
21014             
21015             if(this.tooltip){
21016                 boxLabelCfg.tooltip = this.tooltip;
21017             }
21018              
21019             cfg.cn.push(boxLabelCfg);
21020         }
21021         
21022         if(this.inputType != 'radio'){
21023             cfg.cn.push(hidden);
21024         }
21025         
21026         return cfg;
21027         
21028     },
21029     
21030     /**
21031      * return the real input element.
21032      */
21033     inputEl: function ()
21034     {
21035         return this.el.select('input.roo-' + this.inputType,true).first();
21036     },
21037     hiddenEl: function ()
21038     {
21039         return this.el.select('input.roo-hidden-value',true).first();
21040     },
21041     
21042     labelEl: function()
21043     {
21044         return this.el.select('label.control-label',true).first();
21045     },
21046     /* depricated... */
21047     
21048     label: function()
21049     {
21050         return this.labelEl();
21051     },
21052     
21053     boxLabelEl: function()
21054     {
21055         return this.el.select('label.box-label',true).first();
21056     },
21057     
21058     initEvents : function()
21059     {
21060 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21061         
21062         this.inputEl().on('click', this.onClick,  this);
21063         
21064         if (this.boxLabel) { 
21065             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21066         }
21067         
21068         this.startValue = this.getValue();
21069         
21070         if(this.groupId){
21071             Roo.bootstrap.CheckBox.register(this);
21072         }
21073     },
21074     
21075     onClick : function(e)
21076     {   
21077         if(this.fireEvent('click', this, e) !== false){
21078             this.setChecked(!this.checked);
21079         }
21080         
21081     },
21082     
21083     setChecked : function(state,suppressEvent)
21084     {
21085         this.startValue = this.getValue();
21086
21087         if(this.inputType == 'radio'){
21088             
21089             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21090                 e.dom.checked = false;
21091             });
21092             
21093             this.inputEl().dom.checked = true;
21094             
21095             this.inputEl().dom.value = this.inputValue;
21096             
21097             if(suppressEvent !== true){
21098                 this.fireEvent('check', this, true);
21099             }
21100             
21101             this.validate();
21102             
21103             return;
21104         }
21105         
21106         this.checked = state;
21107         
21108         this.inputEl().dom.checked = state;
21109         
21110         
21111         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21112         
21113         if(suppressEvent !== true){
21114             this.fireEvent('check', this, state);
21115         }
21116         
21117         this.validate();
21118     },
21119     
21120     getValue : function()
21121     {
21122         if(this.inputType == 'radio'){
21123             return this.getGroupValue();
21124         }
21125         
21126         return this.hiddenEl().dom.value;
21127         
21128     },
21129     
21130     getGroupValue : function()
21131     {
21132         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21133             return '';
21134         }
21135         
21136         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21137     },
21138     
21139     setValue : function(v,suppressEvent)
21140     {
21141         if(this.inputType == 'radio'){
21142             this.setGroupValue(v, suppressEvent);
21143             return;
21144         }
21145         
21146         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21147         
21148         this.validate();
21149     },
21150     
21151     setGroupValue : function(v, suppressEvent)
21152     {
21153         this.startValue = this.getValue();
21154         
21155         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21156             e.dom.checked = false;
21157             
21158             if(e.dom.value == v){
21159                 e.dom.checked = true;
21160             }
21161         });
21162         
21163         if(suppressEvent !== true){
21164             this.fireEvent('check', this, true);
21165         }
21166
21167         this.validate();
21168         
21169         return;
21170     },
21171     
21172     validate : function()
21173     {
21174         if(this.getVisibilityEl().hasClass('hidden')){
21175             return true;
21176         }
21177         
21178         if(
21179                 this.disabled || 
21180                 (this.inputType == 'radio' && this.validateRadio()) ||
21181                 (this.inputType == 'checkbox' && this.validateCheckbox())
21182         ){
21183             this.markValid();
21184             return true;
21185         }
21186         
21187         this.markInvalid();
21188         return false;
21189     },
21190     
21191     validateRadio : function()
21192     {
21193         if(this.getVisibilityEl().hasClass('hidden')){
21194             return true;
21195         }
21196         
21197         if(this.allowBlank){
21198             return true;
21199         }
21200         
21201         var valid = false;
21202         
21203         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21204             if(!e.dom.checked){
21205                 return;
21206             }
21207             
21208             valid = true;
21209             
21210             return false;
21211         });
21212         
21213         return valid;
21214     },
21215     
21216     validateCheckbox : function()
21217     {
21218         if(!this.groupId){
21219             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21220             //return (this.getValue() == this.inputValue) ? true : false;
21221         }
21222         
21223         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21224         
21225         if(!group){
21226             return false;
21227         }
21228         
21229         var r = false;
21230         
21231         for(var i in group){
21232             if(group[i].el.isVisible(true)){
21233                 r = false;
21234                 break;
21235             }
21236             
21237             r = true;
21238         }
21239         
21240         for(var i in group){
21241             if(r){
21242                 break;
21243             }
21244             
21245             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21246         }
21247         
21248         return r;
21249     },
21250     
21251     /**
21252      * Mark this field as valid
21253      */
21254     markValid : function()
21255     {
21256         var _this = this;
21257         
21258         this.fireEvent('valid', this);
21259         
21260         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21261         
21262         if(this.groupId){
21263             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21264         }
21265         
21266         if(label){
21267             label.markValid();
21268         }
21269
21270         if(this.inputType == 'radio'){
21271             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21272                 var fg = e.findParent('.form-group', false, true);
21273                 if (Roo.bootstrap.version == 3) {
21274                     fg.removeClass([_this.invalidClass, _this.validClass]);
21275                     fg.addClass(_this.validClass);
21276                 } else {
21277                     fg.removeClass(['is-valid', 'is-invalid']);
21278                     fg.addClass('is-valid');
21279                 }
21280             });
21281             
21282             return;
21283         }
21284
21285         if(!this.groupId){
21286             var fg = this.el.findParent('.form-group', false, true);
21287             if (Roo.bootstrap.version == 3) {
21288                 fg.removeClass([this.invalidClass, this.validClass]);
21289                 fg.addClass(this.validClass);
21290             } else {
21291                 fg.removeClass(['is-valid', 'is-invalid']);
21292                 fg.addClass('is-valid');
21293             }
21294             return;
21295         }
21296         
21297         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21298         
21299         if(!group){
21300             return;
21301         }
21302         
21303         for(var i in group){
21304             var fg = group[i].el.findParent('.form-group', false, true);
21305             if (Roo.bootstrap.version == 3) {
21306                 fg.removeClass([this.invalidClass, this.validClass]);
21307                 fg.addClass(this.validClass);
21308             } else {
21309                 fg.removeClass(['is-valid', 'is-invalid']);
21310                 fg.addClass('is-valid');
21311             }
21312         }
21313     },
21314     
21315      /**
21316      * Mark this field as invalid
21317      * @param {String} msg The validation message
21318      */
21319     markInvalid : function(msg)
21320     {
21321         if(this.allowBlank){
21322             return;
21323         }
21324         
21325         var _this = this;
21326         
21327         this.fireEvent('invalid', this, msg);
21328         
21329         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21330         
21331         if(this.groupId){
21332             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21333         }
21334         
21335         if(label){
21336             label.markInvalid();
21337         }
21338             
21339         if(this.inputType == 'radio'){
21340             
21341             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21342                 var fg = e.findParent('.form-group', false, true);
21343                 if (Roo.bootstrap.version == 3) {
21344                     fg.removeClass([_this.invalidClass, _this.validClass]);
21345                     fg.addClass(_this.invalidClass);
21346                 } else {
21347                     fg.removeClass(['is-invalid', 'is-valid']);
21348                     fg.addClass('is-invalid');
21349                 }
21350             });
21351             
21352             return;
21353         }
21354         
21355         if(!this.groupId){
21356             var fg = this.el.findParent('.form-group', false, true);
21357             if (Roo.bootstrap.version == 3) {
21358                 fg.removeClass([_this.invalidClass, _this.validClass]);
21359                 fg.addClass(_this.invalidClass);
21360             } else {
21361                 fg.removeClass(['is-invalid', 'is-valid']);
21362                 fg.addClass('is-invalid');
21363             }
21364             return;
21365         }
21366         
21367         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21368         
21369         if(!group){
21370             return;
21371         }
21372         
21373         for(var i in group){
21374             var fg = group[i].el.findParent('.form-group', false, true);
21375             if (Roo.bootstrap.version == 3) {
21376                 fg.removeClass([_this.invalidClass, _this.validClass]);
21377                 fg.addClass(_this.invalidClass);
21378             } else {
21379                 fg.removeClass(['is-invalid', 'is-valid']);
21380                 fg.addClass('is-invalid');
21381             }
21382         }
21383         
21384     },
21385     
21386     clearInvalid : function()
21387     {
21388         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21389         
21390         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21391         
21392         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21393         
21394         if (label && label.iconEl) {
21395             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21396             label.iconEl.removeClass(['is-invalid', 'is-valid']);
21397         }
21398     },
21399     
21400     disable : function()
21401     {
21402         if(this.inputType != 'radio'){
21403             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21404             return;
21405         }
21406         
21407         var _this = this;
21408         
21409         if(this.rendered){
21410             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21411                 _this.getActionEl().addClass(this.disabledClass);
21412                 e.dom.disabled = true;
21413             });
21414         }
21415         
21416         this.disabled = true;
21417         this.fireEvent("disable", this);
21418         return this;
21419     },
21420
21421     enable : function()
21422     {
21423         if(this.inputType != 'radio'){
21424             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21425             return;
21426         }
21427         
21428         var _this = this;
21429         
21430         if(this.rendered){
21431             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21432                 _this.getActionEl().removeClass(this.disabledClass);
21433                 e.dom.disabled = false;
21434             });
21435         }
21436         
21437         this.disabled = false;
21438         this.fireEvent("enable", this);
21439         return this;
21440     },
21441     
21442     setBoxLabel : function(v)
21443     {
21444         this.boxLabel = v;
21445         
21446         if(this.rendered){
21447             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21448         }
21449     }
21450
21451 });
21452
21453 Roo.apply(Roo.bootstrap.CheckBox, {
21454     
21455     groups: {},
21456     
21457      /**
21458     * register a CheckBox Group
21459     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21460     */
21461     register : function(checkbox)
21462     {
21463         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21464             this.groups[checkbox.groupId] = {};
21465         }
21466         
21467         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21468             return;
21469         }
21470         
21471         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21472         
21473     },
21474     /**
21475     * fetch a CheckBox Group based on the group ID
21476     * @param {string} the group ID
21477     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21478     */
21479     get: function(groupId) {
21480         if (typeof(this.groups[groupId]) == 'undefined') {
21481             return false;
21482         }
21483         
21484         return this.groups[groupId] ;
21485     }
21486     
21487     
21488 });
21489 /*
21490  * - LGPL
21491  *
21492  * RadioItem
21493  * 
21494  */
21495
21496 /**
21497  * @class Roo.bootstrap.Radio
21498  * @extends Roo.bootstrap.Component
21499  * Bootstrap Radio class
21500  * @cfg {String} boxLabel - the label associated
21501  * @cfg {String} value - the value of radio
21502  * 
21503  * @constructor
21504  * Create a new Radio
21505  * @param {Object} config The config object
21506  */
21507 Roo.bootstrap.Radio = function(config){
21508     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21509     
21510 };
21511
21512 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21513     
21514     boxLabel : '',
21515     
21516     value : '',
21517     
21518     getAutoCreate : function()
21519     {
21520         var cfg = {
21521             tag : 'div',
21522             cls : 'form-group radio',
21523             cn : [
21524                 {
21525                     tag : 'label',
21526                     cls : 'box-label',
21527                     html : this.boxLabel
21528                 }
21529             ]
21530         };
21531         
21532         return cfg;
21533     },
21534     
21535     initEvents : function() 
21536     {
21537         this.parent().register(this);
21538         
21539         this.el.on('click', this.onClick, this);
21540         
21541     },
21542     
21543     onClick : function(e)
21544     {
21545         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21546             this.setChecked(true);
21547         }
21548     },
21549     
21550     setChecked : function(state, suppressEvent)
21551     {
21552         this.parent().setValue(this.value, suppressEvent);
21553         
21554     },
21555     
21556     setBoxLabel : function(v)
21557     {
21558         this.boxLabel = v;
21559         
21560         if(this.rendered){
21561             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21562         }
21563     }
21564     
21565 });
21566  
21567
21568  /*
21569  * - LGPL
21570  *
21571  * Input
21572  * 
21573  */
21574
21575 /**
21576  * @class Roo.bootstrap.SecurePass
21577  * @extends Roo.bootstrap.Input
21578  * Bootstrap SecurePass class
21579  *
21580  * 
21581  * @constructor
21582  * Create a new SecurePass
21583  * @param {Object} config The config object
21584  */
21585  
21586 Roo.bootstrap.SecurePass = function (config) {
21587     // these go here, so the translation tool can replace them..
21588     this.errors = {
21589         PwdEmpty: "Please type a password, and then retype it to confirm.",
21590         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21591         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21592         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21593         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21594         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21595         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21596         TooWeak: "Your password is Too Weak."
21597     },
21598     this.meterLabel = "Password strength:";
21599     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21600     this.meterClass = [
21601         "roo-password-meter-tooweak", 
21602         "roo-password-meter-weak", 
21603         "roo-password-meter-medium", 
21604         "roo-password-meter-strong", 
21605         "roo-password-meter-grey"
21606     ];
21607     
21608     this.errors = {};
21609     
21610     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21611 }
21612
21613 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21614     /**
21615      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21616      * {
21617      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21618      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21619      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21620      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21621      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21622      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21623      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21624      * })
21625      */
21626     // private
21627     
21628     meterWidth: 300,
21629     errorMsg :'',    
21630     errors: false,
21631     imageRoot: '/',
21632     /**
21633      * @cfg {String/Object} Label for the strength meter (defaults to
21634      * 'Password strength:')
21635      */
21636     // private
21637     meterLabel: '',
21638     /**
21639      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21640      * ['Weak', 'Medium', 'Strong'])
21641      */
21642     // private    
21643     pwdStrengths: false,    
21644     // private
21645     strength: 0,
21646     // private
21647     _lastPwd: null,
21648     // private
21649     kCapitalLetter: 0,
21650     kSmallLetter: 1,
21651     kDigit: 2,
21652     kPunctuation: 3,
21653     
21654     insecure: false,
21655     // private
21656     initEvents: function ()
21657     {
21658         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21659
21660         if (this.el.is('input[type=password]') && Roo.isSafari) {
21661             this.el.on('keydown', this.SafariOnKeyDown, this);
21662         }
21663
21664         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21665     },
21666     // private
21667     onRender: function (ct, position)
21668     {
21669         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21670         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21671         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21672
21673         this.trigger.createChild({
21674                    cn: [
21675                     {
21676                     //id: 'PwdMeter',
21677                     tag: 'div',
21678                     cls: 'roo-password-meter-grey col-xs-12',
21679                     style: {
21680                         //width: 0,
21681                         //width: this.meterWidth + 'px'                                                
21682                         }
21683                     },
21684                     {                            
21685                          cls: 'roo-password-meter-text'                          
21686                     }
21687                 ]            
21688         });
21689
21690          
21691         if (this.hideTrigger) {
21692             this.trigger.setDisplayed(false);
21693         }
21694         this.setSize(this.width || '', this.height || '');
21695     },
21696     // private
21697     onDestroy: function ()
21698     {
21699         if (this.trigger) {
21700             this.trigger.removeAllListeners();
21701             this.trigger.remove();
21702         }
21703         if (this.wrap) {
21704             this.wrap.remove();
21705         }
21706         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21707     },
21708     // private
21709     checkStrength: function ()
21710     {
21711         var pwd = this.inputEl().getValue();
21712         if (pwd == this._lastPwd) {
21713             return;
21714         }
21715
21716         var strength;
21717         if (this.ClientSideStrongPassword(pwd)) {
21718             strength = 3;
21719         } else if (this.ClientSideMediumPassword(pwd)) {
21720             strength = 2;
21721         } else if (this.ClientSideWeakPassword(pwd)) {
21722             strength = 1;
21723         } else {
21724             strength = 0;
21725         }
21726         
21727         Roo.log('strength1: ' + strength);
21728         
21729         //var pm = this.trigger.child('div/div/div').dom;
21730         var pm = this.trigger.child('div/div');
21731         pm.removeClass(this.meterClass);
21732         pm.addClass(this.meterClass[strength]);
21733                 
21734         
21735         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21736                 
21737         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21738         
21739         this._lastPwd = pwd;
21740     },
21741     reset: function ()
21742     {
21743         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21744         
21745         this._lastPwd = '';
21746         
21747         var pm = this.trigger.child('div/div');
21748         pm.removeClass(this.meterClass);
21749         pm.addClass('roo-password-meter-grey');        
21750         
21751         
21752         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21753         
21754         pt.innerHTML = '';
21755         this.inputEl().dom.type='password';
21756     },
21757     // private
21758     validateValue: function (value)
21759     {
21760         
21761         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21762             return false;
21763         }
21764         if (value.length == 0) {
21765             if (this.allowBlank) {
21766                 this.clearInvalid();
21767                 return true;
21768             }
21769
21770             this.markInvalid(this.errors.PwdEmpty);
21771             this.errorMsg = this.errors.PwdEmpty;
21772             return false;
21773         }
21774         
21775         if(this.insecure){
21776             return true;
21777         }
21778         
21779         if ('[\x21-\x7e]*'.match(value)) {
21780             this.markInvalid(this.errors.PwdBadChar);
21781             this.errorMsg = this.errors.PwdBadChar;
21782             return false;
21783         }
21784         if (value.length < 6) {
21785             this.markInvalid(this.errors.PwdShort);
21786             this.errorMsg = this.errors.PwdShort;
21787             return false;
21788         }
21789         if (value.length > 16) {
21790             this.markInvalid(this.errors.PwdLong);
21791             this.errorMsg = this.errors.PwdLong;
21792             return false;
21793         }
21794         var strength;
21795         if (this.ClientSideStrongPassword(value)) {
21796             strength = 3;
21797         } else if (this.ClientSideMediumPassword(value)) {
21798             strength = 2;
21799         } else if (this.ClientSideWeakPassword(value)) {
21800             strength = 1;
21801         } else {
21802             strength = 0;
21803         }
21804
21805         
21806         if (strength < 2) {
21807             //this.markInvalid(this.errors.TooWeak);
21808             this.errorMsg = this.errors.TooWeak;
21809             //return false;
21810         }
21811         
21812         
21813         console.log('strength2: ' + strength);
21814         
21815         //var pm = this.trigger.child('div/div/div').dom;
21816         
21817         var pm = this.trigger.child('div/div');
21818         pm.removeClass(this.meterClass);
21819         pm.addClass(this.meterClass[strength]);
21820                 
21821         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21822                 
21823         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21824         
21825         this.errorMsg = ''; 
21826         return true;
21827     },
21828     // private
21829     CharacterSetChecks: function (type)
21830     {
21831         this.type = type;
21832         this.fResult = false;
21833     },
21834     // private
21835     isctype: function (character, type)
21836     {
21837         switch (type) {  
21838             case this.kCapitalLetter:
21839                 if (character >= 'A' && character <= 'Z') {
21840                     return true;
21841                 }
21842                 break;
21843             
21844             case this.kSmallLetter:
21845                 if (character >= 'a' && character <= 'z') {
21846                     return true;
21847                 }
21848                 break;
21849             
21850             case this.kDigit:
21851                 if (character >= '0' && character <= '9') {
21852                     return true;
21853                 }
21854                 break;
21855             
21856             case this.kPunctuation:
21857                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21858                     return true;
21859                 }
21860                 break;
21861             
21862             default:
21863                 return false;
21864         }
21865
21866     },
21867     // private
21868     IsLongEnough: function (pwd, size)
21869     {
21870         return !(pwd == null || isNaN(size) || pwd.length < size);
21871     },
21872     // private
21873     SpansEnoughCharacterSets: function (word, nb)
21874     {
21875         if (!this.IsLongEnough(word, nb))
21876         {
21877             return false;
21878         }
21879
21880         var characterSetChecks = new Array(
21881             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21882             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21883         );
21884         
21885         for (var index = 0; index < word.length; ++index) {
21886             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21887                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21888                     characterSetChecks[nCharSet].fResult = true;
21889                     break;
21890                 }
21891             }
21892         }
21893
21894         var nCharSets = 0;
21895         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21896             if (characterSetChecks[nCharSet].fResult) {
21897                 ++nCharSets;
21898             }
21899         }
21900
21901         if (nCharSets < nb) {
21902             return false;
21903         }
21904         return true;
21905     },
21906     // private
21907     ClientSideStrongPassword: function (pwd)
21908     {
21909         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21910     },
21911     // private
21912     ClientSideMediumPassword: function (pwd)
21913     {
21914         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21915     },
21916     // private
21917     ClientSideWeakPassword: function (pwd)
21918     {
21919         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21920     }
21921           
21922 })//<script type="text/javascript">
21923
21924 /*
21925  * Based  Ext JS Library 1.1.1
21926  * Copyright(c) 2006-2007, Ext JS, LLC.
21927  * LGPL
21928  *
21929  */
21930  
21931 /**
21932  * @class Roo.HtmlEditorCore
21933  * @extends Roo.Component
21934  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21935  *
21936  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21937  */
21938
21939 Roo.HtmlEditorCore = function(config){
21940     
21941     
21942     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21943     
21944     
21945     this.addEvents({
21946         /**
21947          * @event initialize
21948          * Fires when the editor is fully initialized (including the iframe)
21949          * @param {Roo.HtmlEditorCore} this
21950          */
21951         initialize: true,
21952         /**
21953          * @event activate
21954          * Fires when the editor is first receives the focus. Any insertion must wait
21955          * until after this event.
21956          * @param {Roo.HtmlEditorCore} this
21957          */
21958         activate: true,
21959          /**
21960          * @event beforesync
21961          * Fires before the textarea is updated with content from the editor iframe. Return false
21962          * to cancel the sync.
21963          * @param {Roo.HtmlEditorCore} this
21964          * @param {String} html
21965          */
21966         beforesync: true,
21967          /**
21968          * @event beforepush
21969          * Fires before the iframe editor is updated with content from the textarea. Return false
21970          * to cancel the push.
21971          * @param {Roo.HtmlEditorCore} this
21972          * @param {String} html
21973          */
21974         beforepush: true,
21975          /**
21976          * @event sync
21977          * Fires when the textarea is updated with content from the editor iframe.
21978          * @param {Roo.HtmlEditorCore} this
21979          * @param {String} html
21980          */
21981         sync: true,
21982          /**
21983          * @event push
21984          * Fires when the iframe editor is updated with content from the textarea.
21985          * @param {Roo.HtmlEditorCore} this
21986          * @param {String} html
21987          */
21988         push: true,
21989         
21990         /**
21991          * @event editorevent
21992          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21993          * @param {Roo.HtmlEditorCore} this
21994          */
21995         editorevent: true
21996         
21997     });
21998     
21999     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22000     
22001     // defaults : white / black...
22002     this.applyBlacklists();
22003     
22004     
22005     
22006 };
22007
22008
22009 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
22010
22011
22012      /**
22013      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
22014      */
22015     
22016     owner : false,
22017     
22018      /**
22019      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22020      *                        Roo.resizable.
22021      */
22022     resizable : false,
22023      /**
22024      * @cfg {Number} height (in pixels)
22025      */   
22026     height: 300,
22027    /**
22028      * @cfg {Number} width (in pixels)
22029      */   
22030     width: 500,
22031     
22032     /**
22033      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22034      * 
22035      */
22036     stylesheets: false,
22037     
22038     // id of frame..
22039     frameId: false,
22040     
22041     // private properties
22042     validationEvent : false,
22043     deferHeight: true,
22044     initialized : false,
22045     activated : false,
22046     sourceEditMode : false,
22047     onFocus : Roo.emptyFn,
22048     iframePad:3,
22049     hideMode:'offsets',
22050     
22051     clearUp: true,
22052     
22053     // blacklist + whitelisted elements..
22054     black: false,
22055     white: false,
22056      
22057     bodyCls : '',
22058
22059     /**
22060      * Protected method that will not generally be called directly. It
22061      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22062      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22063      */
22064     getDocMarkup : function(){
22065         // body styles..
22066         var st = '';
22067         
22068         // inherit styels from page...?? 
22069         if (this.stylesheets === false) {
22070             
22071             Roo.get(document.head).select('style').each(function(node) {
22072                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22073             });
22074             
22075             Roo.get(document.head).select('link').each(function(node) { 
22076                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22077             });
22078             
22079         } else if (!this.stylesheets.length) {
22080                 // simple..
22081                 st = '<style type="text/css">' +
22082                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22083                    '</style>';
22084         } else { 
22085             st = '<style type="text/css">' +
22086                     this.stylesheets +
22087                 '</style>';
22088         }
22089         
22090         st +=  '<style type="text/css">' +
22091             'IMG { cursor: pointer } ' +
22092         '</style>';
22093
22094         var cls = 'roo-htmleditor-body';
22095         
22096         if(this.bodyCls.length){
22097             cls += ' ' + this.bodyCls;
22098         }
22099         
22100         return '<html><head>' + st  +
22101             //<style type="text/css">' +
22102             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22103             //'</style>' +
22104             ' </head><body class="' +  cls + '"></body></html>';
22105     },
22106
22107     // private
22108     onRender : function(ct, position)
22109     {
22110         var _t = this;
22111         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22112         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22113         
22114         
22115         this.el.dom.style.border = '0 none';
22116         this.el.dom.setAttribute('tabIndex', -1);
22117         this.el.addClass('x-hidden hide');
22118         
22119         
22120         
22121         if(Roo.isIE){ // fix IE 1px bogus margin
22122             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22123         }
22124        
22125         
22126         this.frameId = Roo.id();
22127         
22128          
22129         
22130         var iframe = this.owner.wrap.createChild({
22131             tag: 'iframe',
22132             cls: 'form-control', // bootstrap..
22133             id: this.frameId,
22134             name: this.frameId,
22135             frameBorder : 'no',
22136             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22137         }, this.el
22138         );
22139         
22140         
22141         this.iframe = iframe.dom;
22142
22143          this.assignDocWin();
22144         
22145         this.doc.designMode = 'on';
22146        
22147         this.doc.open();
22148         this.doc.write(this.getDocMarkup());
22149         this.doc.close();
22150
22151         
22152         var task = { // must defer to wait for browser to be ready
22153             run : function(){
22154                 //console.log("run task?" + this.doc.readyState);
22155                 this.assignDocWin();
22156                 if(this.doc.body || this.doc.readyState == 'complete'){
22157                     try {
22158                         this.doc.designMode="on";
22159                     } catch (e) {
22160                         return;
22161                     }
22162                     Roo.TaskMgr.stop(task);
22163                     this.initEditor.defer(10, this);
22164                 }
22165             },
22166             interval : 10,
22167             duration: 10000,
22168             scope: this
22169         };
22170         Roo.TaskMgr.start(task);
22171
22172     },
22173
22174     // private
22175     onResize : function(w, h)
22176     {
22177          Roo.log('resize: ' +w + ',' + h );
22178         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22179         if(!this.iframe){
22180             return;
22181         }
22182         if(typeof w == 'number'){
22183             
22184             this.iframe.style.width = w + 'px';
22185         }
22186         if(typeof h == 'number'){
22187             
22188             this.iframe.style.height = h + 'px';
22189             if(this.doc){
22190                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22191             }
22192         }
22193         
22194     },
22195
22196     /**
22197      * Toggles the editor between standard and source edit mode.
22198      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22199      */
22200     toggleSourceEdit : function(sourceEditMode){
22201         
22202         this.sourceEditMode = sourceEditMode === true;
22203         
22204         if(this.sourceEditMode){
22205  
22206             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22207             
22208         }else{
22209             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22210             //this.iframe.className = '';
22211             this.deferFocus();
22212         }
22213         //this.setSize(this.owner.wrap.getSize());
22214         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22215     },
22216
22217     
22218   
22219
22220     /**
22221      * Protected method that will not generally be called directly. If you need/want
22222      * custom HTML cleanup, this is the method you should override.
22223      * @param {String} html The HTML to be cleaned
22224      * return {String} The cleaned HTML
22225      */
22226     cleanHtml : function(html){
22227         html = String(html);
22228         if(html.length > 5){
22229             if(Roo.isSafari){ // strip safari nonsense
22230                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22231             }
22232         }
22233         if(html == '&nbsp;'){
22234             html = '';
22235         }
22236         return html;
22237     },
22238
22239     /**
22240      * HTML Editor -> Textarea
22241      * Protected method that will not generally be called directly. Syncs the contents
22242      * of the editor iframe with the textarea.
22243      */
22244     syncValue : function(){
22245         if(this.initialized){
22246             var bd = (this.doc.body || this.doc.documentElement);
22247             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22248             var html = bd.innerHTML;
22249             if(Roo.isSafari){
22250                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22251                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22252                 if(m && m[1]){
22253                     html = '<div style="'+m[0]+'">' + html + '</div>';
22254                 }
22255             }
22256             html = this.cleanHtml(html);
22257             // fix up the special chars.. normaly like back quotes in word...
22258             // however we do not want to do this with chinese..
22259             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22260                 var cc = b.charCodeAt();
22261                 if (
22262                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22263                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22264                     (cc >= 0xf900 && cc < 0xfb00 )
22265                 ) {
22266                         return b;
22267                 }
22268                 return "&#"+cc+";" 
22269             });
22270             if(this.owner.fireEvent('beforesync', this, html) !== false){
22271                 this.el.dom.value = html;
22272                 this.owner.fireEvent('sync', this, html);
22273             }
22274         }
22275     },
22276
22277     /**
22278      * Protected method that will not generally be called directly. Pushes the value of the textarea
22279      * into the iframe editor.
22280      */
22281     pushValue : function(){
22282         if(this.initialized){
22283             var v = this.el.dom.value.trim();
22284             
22285 //            if(v.length < 1){
22286 //                v = '&#160;';
22287 //            }
22288             
22289             if(this.owner.fireEvent('beforepush', this, v) !== false){
22290                 var d = (this.doc.body || this.doc.documentElement);
22291                 d.innerHTML = v;
22292                 this.cleanUpPaste();
22293                 this.el.dom.value = d.innerHTML;
22294                 this.owner.fireEvent('push', this, v);
22295             }
22296         }
22297     },
22298
22299     // private
22300     deferFocus : function(){
22301         this.focus.defer(10, this);
22302     },
22303
22304     // doc'ed in Field
22305     focus : function(){
22306         if(this.win && !this.sourceEditMode){
22307             this.win.focus();
22308         }else{
22309             this.el.focus();
22310         }
22311     },
22312     
22313     assignDocWin: function()
22314     {
22315         var iframe = this.iframe;
22316         
22317          if(Roo.isIE){
22318             this.doc = iframe.contentWindow.document;
22319             this.win = iframe.contentWindow;
22320         } else {
22321 //            if (!Roo.get(this.frameId)) {
22322 //                return;
22323 //            }
22324 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22325 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22326             
22327             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22328                 return;
22329             }
22330             
22331             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22332             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22333         }
22334     },
22335     
22336     // private
22337     initEditor : function(){
22338         //console.log("INIT EDITOR");
22339         this.assignDocWin();
22340         
22341         
22342         
22343         this.doc.designMode="on";
22344         this.doc.open();
22345         this.doc.write(this.getDocMarkup());
22346         this.doc.close();
22347         
22348         var dbody = (this.doc.body || this.doc.documentElement);
22349         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22350         // this copies styles from the containing element into thsi one..
22351         // not sure why we need all of this..
22352         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22353         
22354         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22355         //ss['background-attachment'] = 'fixed'; // w3c
22356         dbody.bgProperties = 'fixed'; // ie
22357         //Roo.DomHelper.applyStyles(dbody, ss);
22358         Roo.EventManager.on(this.doc, {
22359             //'mousedown': this.onEditorEvent,
22360             'mouseup': this.onEditorEvent,
22361             'dblclick': this.onEditorEvent,
22362             'click': this.onEditorEvent,
22363             'keyup': this.onEditorEvent,
22364             buffer:100,
22365             scope: this
22366         });
22367         if(Roo.isGecko){
22368             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22369         }
22370         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22371             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22372         }
22373         this.initialized = true;
22374
22375         this.owner.fireEvent('initialize', this);
22376         this.pushValue();
22377     },
22378
22379     // private
22380     onDestroy : function(){
22381         
22382         
22383         
22384         if(this.rendered){
22385             
22386             //for (var i =0; i < this.toolbars.length;i++) {
22387             //    // fixme - ask toolbars for heights?
22388             //    this.toolbars[i].onDestroy();
22389            // }
22390             
22391             //this.wrap.dom.innerHTML = '';
22392             //this.wrap.remove();
22393         }
22394     },
22395
22396     // private
22397     onFirstFocus : function(){
22398         
22399         this.assignDocWin();
22400         
22401         
22402         this.activated = true;
22403          
22404     
22405         if(Roo.isGecko){ // prevent silly gecko errors
22406             this.win.focus();
22407             var s = this.win.getSelection();
22408             if(!s.focusNode || s.focusNode.nodeType != 3){
22409                 var r = s.getRangeAt(0);
22410                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22411                 r.collapse(true);
22412                 this.deferFocus();
22413             }
22414             try{
22415                 this.execCmd('useCSS', true);
22416                 this.execCmd('styleWithCSS', false);
22417             }catch(e){}
22418         }
22419         this.owner.fireEvent('activate', this);
22420     },
22421
22422     // private
22423     adjustFont: function(btn){
22424         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22425         //if(Roo.isSafari){ // safari
22426         //    adjust *= 2;
22427        // }
22428         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22429         if(Roo.isSafari){ // safari
22430             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22431             v =  (v < 10) ? 10 : v;
22432             v =  (v > 48) ? 48 : v;
22433             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22434             
22435         }
22436         
22437         
22438         v = Math.max(1, v+adjust);
22439         
22440         this.execCmd('FontSize', v  );
22441     },
22442
22443     onEditorEvent : function(e)
22444     {
22445         this.owner.fireEvent('editorevent', this, e);
22446       //  this.updateToolbar();
22447         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22448     },
22449
22450     insertTag : function(tg)
22451     {
22452         // could be a bit smarter... -> wrap the current selected tRoo..
22453         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22454             
22455             range = this.createRange(this.getSelection());
22456             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22457             wrappingNode.appendChild(range.extractContents());
22458             range.insertNode(wrappingNode);
22459
22460             return;
22461             
22462             
22463             
22464         }
22465         this.execCmd("formatblock",   tg);
22466         
22467     },
22468     
22469     insertText : function(txt)
22470     {
22471         
22472         
22473         var range = this.createRange();
22474         range.deleteContents();
22475                //alert(Sender.getAttribute('label'));
22476                
22477         range.insertNode(this.doc.createTextNode(txt));
22478     } ,
22479     
22480      
22481
22482     /**
22483      * Executes a Midas editor command on the editor document and performs necessary focus and
22484      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22485      * @param {String} cmd The Midas command
22486      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22487      */
22488     relayCmd : function(cmd, value){
22489         this.win.focus();
22490         this.execCmd(cmd, value);
22491         this.owner.fireEvent('editorevent', this);
22492         //this.updateToolbar();
22493         this.owner.deferFocus();
22494     },
22495
22496     /**
22497      * Executes a Midas editor command directly on the editor document.
22498      * For visual commands, you should use {@link #relayCmd} instead.
22499      * <b>This should only be called after the editor is initialized.</b>
22500      * @param {String} cmd The Midas command
22501      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22502      */
22503     execCmd : function(cmd, value){
22504         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22505         this.syncValue();
22506     },
22507  
22508  
22509    
22510     /**
22511      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22512      * to insert tRoo.
22513      * @param {String} text | dom node.. 
22514      */
22515     insertAtCursor : function(text)
22516     {
22517         
22518         if(!this.activated){
22519             return;
22520         }
22521         /*
22522         if(Roo.isIE){
22523             this.win.focus();
22524             var r = this.doc.selection.createRange();
22525             if(r){
22526                 r.collapse(true);
22527                 r.pasteHTML(text);
22528                 this.syncValue();
22529                 this.deferFocus();
22530             
22531             }
22532             return;
22533         }
22534         */
22535         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22536             this.win.focus();
22537             
22538             
22539             // from jquery ui (MIT licenced)
22540             var range, node;
22541             var win = this.win;
22542             
22543             if (win.getSelection && win.getSelection().getRangeAt) {
22544                 range = win.getSelection().getRangeAt(0);
22545                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22546                 range.insertNode(node);
22547             } else if (win.document.selection && win.document.selection.createRange) {
22548                 // no firefox support
22549                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22550                 win.document.selection.createRange().pasteHTML(txt);
22551             } else {
22552                 // no firefox support
22553                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22554                 this.execCmd('InsertHTML', txt);
22555             } 
22556             
22557             this.syncValue();
22558             
22559             this.deferFocus();
22560         }
22561     },
22562  // private
22563     mozKeyPress : function(e){
22564         if(e.ctrlKey){
22565             var c = e.getCharCode(), cmd;
22566           
22567             if(c > 0){
22568                 c = String.fromCharCode(c).toLowerCase();
22569                 switch(c){
22570                     case 'b':
22571                         cmd = 'bold';
22572                         break;
22573                     case 'i':
22574                         cmd = 'italic';
22575                         break;
22576                     
22577                     case 'u':
22578                         cmd = 'underline';
22579                         break;
22580                     
22581                     case 'v':
22582                         this.cleanUpPaste.defer(100, this);
22583                         return;
22584                         
22585                 }
22586                 if(cmd){
22587                     this.win.focus();
22588                     this.execCmd(cmd);
22589                     this.deferFocus();
22590                     e.preventDefault();
22591                 }
22592                 
22593             }
22594         }
22595     },
22596
22597     // private
22598     fixKeys : function(){ // load time branching for fastest keydown performance
22599         if(Roo.isIE){
22600             return function(e){
22601                 var k = e.getKey(), r;
22602                 if(k == e.TAB){
22603                     e.stopEvent();
22604                     r = this.doc.selection.createRange();
22605                     if(r){
22606                         r.collapse(true);
22607                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22608                         this.deferFocus();
22609                     }
22610                     return;
22611                 }
22612                 
22613                 if(k == e.ENTER){
22614                     r = this.doc.selection.createRange();
22615                     if(r){
22616                         var target = r.parentElement();
22617                         if(!target || target.tagName.toLowerCase() != 'li'){
22618                             e.stopEvent();
22619                             r.pasteHTML('<br />');
22620                             r.collapse(false);
22621                             r.select();
22622                         }
22623                     }
22624                 }
22625                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22626                     this.cleanUpPaste.defer(100, this);
22627                     return;
22628                 }
22629                 
22630                 
22631             };
22632         }else if(Roo.isOpera){
22633             return function(e){
22634                 var k = e.getKey();
22635                 if(k == e.TAB){
22636                     e.stopEvent();
22637                     this.win.focus();
22638                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22639                     this.deferFocus();
22640                 }
22641                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22642                     this.cleanUpPaste.defer(100, this);
22643                     return;
22644                 }
22645                 
22646             };
22647         }else if(Roo.isSafari){
22648             return function(e){
22649                 var k = e.getKey();
22650                 
22651                 if(k == e.TAB){
22652                     e.stopEvent();
22653                     this.execCmd('InsertText','\t');
22654                     this.deferFocus();
22655                     return;
22656                 }
22657                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22658                     this.cleanUpPaste.defer(100, this);
22659                     return;
22660                 }
22661                 
22662              };
22663         }
22664     }(),
22665     
22666     getAllAncestors: function()
22667     {
22668         var p = this.getSelectedNode();
22669         var a = [];
22670         if (!p) {
22671             a.push(p); // push blank onto stack..
22672             p = this.getParentElement();
22673         }
22674         
22675         
22676         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22677             a.push(p);
22678             p = p.parentNode;
22679         }
22680         a.push(this.doc.body);
22681         return a;
22682     },
22683     lastSel : false,
22684     lastSelNode : false,
22685     
22686     
22687     getSelection : function() 
22688     {
22689         this.assignDocWin();
22690         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22691     },
22692     
22693     getSelectedNode: function() 
22694     {
22695         // this may only work on Gecko!!!
22696         
22697         // should we cache this!!!!
22698         
22699         
22700         
22701          
22702         var range = this.createRange(this.getSelection()).cloneRange();
22703         
22704         if (Roo.isIE) {
22705             var parent = range.parentElement();
22706             while (true) {
22707                 var testRange = range.duplicate();
22708                 testRange.moveToElementText(parent);
22709                 if (testRange.inRange(range)) {
22710                     break;
22711                 }
22712                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22713                     break;
22714                 }
22715                 parent = parent.parentElement;
22716             }
22717             return parent;
22718         }
22719         
22720         // is ancestor a text element.
22721         var ac =  range.commonAncestorContainer;
22722         if (ac.nodeType == 3) {
22723             ac = ac.parentNode;
22724         }
22725         
22726         var ar = ac.childNodes;
22727          
22728         var nodes = [];
22729         var other_nodes = [];
22730         var has_other_nodes = false;
22731         for (var i=0;i<ar.length;i++) {
22732             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22733                 continue;
22734             }
22735             // fullly contained node.
22736             
22737             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22738                 nodes.push(ar[i]);
22739                 continue;
22740             }
22741             
22742             // probably selected..
22743             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22744                 other_nodes.push(ar[i]);
22745                 continue;
22746             }
22747             // outer..
22748             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22749                 continue;
22750             }
22751             
22752             
22753             has_other_nodes = true;
22754         }
22755         if (!nodes.length && other_nodes.length) {
22756             nodes= other_nodes;
22757         }
22758         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22759             return false;
22760         }
22761         
22762         return nodes[0];
22763     },
22764     createRange: function(sel)
22765     {
22766         // this has strange effects when using with 
22767         // top toolbar - not sure if it's a great idea.
22768         //this.editor.contentWindow.focus();
22769         if (typeof sel != "undefined") {
22770             try {
22771                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22772             } catch(e) {
22773                 return this.doc.createRange();
22774             }
22775         } else {
22776             return this.doc.createRange();
22777         }
22778     },
22779     getParentElement: function()
22780     {
22781         
22782         this.assignDocWin();
22783         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22784         
22785         var range = this.createRange(sel);
22786          
22787         try {
22788             var p = range.commonAncestorContainer;
22789             while (p.nodeType == 3) { // text node
22790                 p = p.parentNode;
22791             }
22792             return p;
22793         } catch (e) {
22794             return null;
22795         }
22796     
22797     },
22798     /***
22799      *
22800      * Range intersection.. the hard stuff...
22801      *  '-1' = before
22802      *  '0' = hits..
22803      *  '1' = after.
22804      *         [ -- selected range --- ]
22805      *   [fail]                        [fail]
22806      *
22807      *    basically..
22808      *      if end is before start or  hits it. fail.
22809      *      if start is after end or hits it fail.
22810      *
22811      *   if either hits (but other is outside. - then it's not 
22812      *   
22813      *    
22814      **/
22815     
22816     
22817     // @see http://www.thismuchiknow.co.uk/?p=64.
22818     rangeIntersectsNode : function(range, node)
22819     {
22820         var nodeRange = node.ownerDocument.createRange();
22821         try {
22822             nodeRange.selectNode(node);
22823         } catch (e) {
22824             nodeRange.selectNodeContents(node);
22825         }
22826     
22827         var rangeStartRange = range.cloneRange();
22828         rangeStartRange.collapse(true);
22829     
22830         var rangeEndRange = range.cloneRange();
22831         rangeEndRange.collapse(false);
22832     
22833         var nodeStartRange = nodeRange.cloneRange();
22834         nodeStartRange.collapse(true);
22835     
22836         var nodeEndRange = nodeRange.cloneRange();
22837         nodeEndRange.collapse(false);
22838     
22839         return rangeStartRange.compareBoundaryPoints(
22840                  Range.START_TO_START, nodeEndRange) == -1 &&
22841                rangeEndRange.compareBoundaryPoints(
22842                  Range.START_TO_START, nodeStartRange) == 1;
22843         
22844          
22845     },
22846     rangeCompareNode : function(range, node)
22847     {
22848         var nodeRange = node.ownerDocument.createRange();
22849         try {
22850             nodeRange.selectNode(node);
22851         } catch (e) {
22852             nodeRange.selectNodeContents(node);
22853         }
22854         
22855         
22856         range.collapse(true);
22857     
22858         nodeRange.collapse(true);
22859      
22860         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22861         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22862          
22863         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22864         
22865         var nodeIsBefore   =  ss == 1;
22866         var nodeIsAfter    = ee == -1;
22867         
22868         if (nodeIsBefore && nodeIsAfter) {
22869             return 0; // outer
22870         }
22871         if (!nodeIsBefore && nodeIsAfter) {
22872             return 1; //right trailed.
22873         }
22874         
22875         if (nodeIsBefore && !nodeIsAfter) {
22876             return 2;  // left trailed.
22877         }
22878         // fully contined.
22879         return 3;
22880     },
22881
22882     // private? - in a new class?
22883     cleanUpPaste :  function()
22884     {
22885         // cleans up the whole document..
22886         Roo.log('cleanuppaste');
22887         
22888         this.cleanUpChildren(this.doc.body);
22889         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22890         if (clean != this.doc.body.innerHTML) {
22891             this.doc.body.innerHTML = clean;
22892         }
22893         
22894     },
22895     
22896     cleanWordChars : function(input) {// change the chars to hex code
22897         var he = Roo.HtmlEditorCore;
22898         
22899         var output = input;
22900         Roo.each(he.swapCodes, function(sw) { 
22901             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22902             
22903             output = output.replace(swapper, sw[1]);
22904         });
22905         
22906         return output;
22907     },
22908     
22909     
22910     cleanUpChildren : function (n)
22911     {
22912         if (!n.childNodes.length) {
22913             return;
22914         }
22915         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22916            this.cleanUpChild(n.childNodes[i]);
22917         }
22918     },
22919     
22920     
22921         
22922     
22923     cleanUpChild : function (node)
22924     {
22925         var ed = this;
22926         //console.log(node);
22927         if (node.nodeName == "#text") {
22928             // clean up silly Windows -- stuff?
22929             return; 
22930         }
22931         if (node.nodeName == "#comment") {
22932             node.parentNode.removeChild(node);
22933             // clean up silly Windows -- stuff?
22934             return; 
22935         }
22936         var lcname = node.tagName.toLowerCase();
22937         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22938         // whitelist of tags..
22939         
22940         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22941             // remove node.
22942             node.parentNode.removeChild(node);
22943             return;
22944             
22945         }
22946         
22947         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22948         
22949         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22950         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22951         
22952         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22953         //    remove_keep_children = true;
22954         //}
22955         
22956         if (remove_keep_children) {
22957             this.cleanUpChildren(node);
22958             // inserts everything just before this node...
22959             while (node.childNodes.length) {
22960                 var cn = node.childNodes[0];
22961                 node.removeChild(cn);
22962                 node.parentNode.insertBefore(cn, node);
22963             }
22964             node.parentNode.removeChild(node);
22965             return;
22966         }
22967         
22968         if (!node.attributes || !node.attributes.length) {
22969             this.cleanUpChildren(node);
22970             return;
22971         }
22972         
22973         function cleanAttr(n,v)
22974         {
22975             
22976             if (v.match(/^\./) || v.match(/^\//)) {
22977                 return;
22978             }
22979             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22980                 return;
22981             }
22982             if (v.match(/^#/)) {
22983                 return;
22984             }
22985 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22986             node.removeAttribute(n);
22987             
22988         }
22989         
22990         var cwhite = this.cwhite;
22991         var cblack = this.cblack;
22992             
22993         function cleanStyle(n,v)
22994         {
22995             if (v.match(/expression/)) { //XSS?? should we even bother..
22996                 node.removeAttribute(n);
22997                 return;
22998             }
22999             
23000             var parts = v.split(/;/);
23001             var clean = [];
23002             
23003             Roo.each(parts, function(p) {
23004                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23005                 if (!p.length) {
23006                     return true;
23007                 }
23008                 var l = p.split(':').shift().replace(/\s+/g,'');
23009                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23010                 
23011                 if ( cwhite.length && cblack.indexOf(l) > -1) {
23012 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23013                     //node.removeAttribute(n);
23014                     return true;
23015                 }
23016                 //Roo.log()
23017                 // only allow 'c whitelisted system attributes'
23018                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
23019 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23020                     //node.removeAttribute(n);
23021                     return true;
23022                 }
23023                 
23024                 
23025                  
23026                 
23027                 clean.push(p);
23028                 return true;
23029             });
23030             if (clean.length) { 
23031                 node.setAttribute(n, clean.join(';'));
23032             } else {
23033                 node.removeAttribute(n);
23034             }
23035             
23036         }
23037         
23038         
23039         for (var i = node.attributes.length-1; i > -1 ; i--) {
23040             var a = node.attributes[i];
23041             //console.log(a);
23042             
23043             if (a.name.toLowerCase().substr(0,2)=='on')  {
23044                 node.removeAttribute(a.name);
23045                 continue;
23046             }
23047             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23048                 node.removeAttribute(a.name);
23049                 continue;
23050             }
23051             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23052                 cleanAttr(a.name,a.value); // fixme..
23053                 continue;
23054             }
23055             if (a.name == 'style') {
23056                 cleanStyle(a.name,a.value);
23057                 continue;
23058             }
23059             /// clean up MS crap..
23060             // tecnically this should be a list of valid class'es..
23061             
23062             
23063             if (a.name == 'class') {
23064                 if (a.value.match(/^Mso/)) {
23065                     node.className = '';
23066                 }
23067                 
23068                 if (a.value.match(/^body$/)) {
23069                     node.className = '';
23070                 }
23071                 continue;
23072             }
23073             
23074             // style cleanup!?
23075             // class cleanup?
23076             
23077         }
23078         
23079         
23080         this.cleanUpChildren(node);
23081         
23082         
23083     },
23084     
23085     /**
23086      * Clean up MS wordisms...
23087      */
23088     cleanWord : function(node)
23089     {
23090         
23091         
23092         if (!node) {
23093             this.cleanWord(this.doc.body);
23094             return;
23095         }
23096         if (node.nodeName == "#text") {
23097             // clean up silly Windows -- stuff?
23098             return; 
23099         }
23100         if (node.nodeName == "#comment") {
23101             node.parentNode.removeChild(node);
23102             // clean up silly Windows -- stuff?
23103             return; 
23104         }
23105         
23106         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23107             node.parentNode.removeChild(node);
23108             return;
23109         }
23110         
23111         // remove - but keep children..
23112         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
23113             while (node.childNodes.length) {
23114                 var cn = node.childNodes[0];
23115                 node.removeChild(cn);
23116                 node.parentNode.insertBefore(cn, node);
23117             }
23118             node.parentNode.removeChild(node);
23119             this.iterateChildren(node, this.cleanWord);
23120             return;
23121         }
23122         // clean styles
23123         if (node.className.length) {
23124             
23125             var cn = node.className.split(/\W+/);
23126             var cna = [];
23127             Roo.each(cn, function(cls) {
23128                 if (cls.match(/Mso[a-zA-Z]+/)) {
23129                     return;
23130                 }
23131                 cna.push(cls);
23132             });
23133             node.className = cna.length ? cna.join(' ') : '';
23134             if (!cna.length) {
23135                 node.removeAttribute("class");
23136             }
23137         }
23138         
23139         if (node.hasAttribute("lang")) {
23140             node.removeAttribute("lang");
23141         }
23142         
23143         if (node.hasAttribute("style")) {
23144             
23145             var styles = node.getAttribute("style").split(";");
23146             var nstyle = [];
23147             Roo.each(styles, function(s) {
23148                 if (!s.match(/:/)) {
23149                     return;
23150                 }
23151                 var kv = s.split(":");
23152                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23153                     return;
23154                 }
23155                 // what ever is left... we allow.
23156                 nstyle.push(s);
23157             });
23158             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23159             if (!nstyle.length) {
23160                 node.removeAttribute('style');
23161             }
23162         }
23163         this.iterateChildren(node, this.cleanWord);
23164         
23165         
23166         
23167     },
23168     /**
23169      * iterateChildren of a Node, calling fn each time, using this as the scole..
23170      * @param {DomNode} node node to iterate children of.
23171      * @param {Function} fn method of this class to call on each item.
23172      */
23173     iterateChildren : function(node, fn)
23174     {
23175         if (!node.childNodes.length) {
23176                 return;
23177         }
23178         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23179            fn.call(this, node.childNodes[i])
23180         }
23181     },
23182     
23183     
23184     /**
23185      * cleanTableWidths.
23186      *
23187      * Quite often pasting from word etc.. results in tables with column and widths.
23188      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23189      *
23190      */
23191     cleanTableWidths : function(node)
23192     {
23193          
23194          
23195         if (!node) {
23196             this.cleanTableWidths(this.doc.body);
23197             return;
23198         }
23199         
23200         // ignore list...
23201         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23202             return; 
23203         }
23204         Roo.log(node.tagName);
23205         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23206             this.iterateChildren(node, this.cleanTableWidths);
23207             return;
23208         }
23209         if (node.hasAttribute('width')) {
23210             node.removeAttribute('width');
23211         }
23212         
23213          
23214         if (node.hasAttribute("style")) {
23215             // pretty basic...
23216             
23217             var styles = node.getAttribute("style").split(";");
23218             var nstyle = [];
23219             Roo.each(styles, function(s) {
23220                 if (!s.match(/:/)) {
23221                     return;
23222                 }
23223                 var kv = s.split(":");
23224                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23225                     return;
23226                 }
23227                 // what ever is left... we allow.
23228                 nstyle.push(s);
23229             });
23230             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23231             if (!nstyle.length) {
23232                 node.removeAttribute('style');
23233             }
23234         }
23235         
23236         this.iterateChildren(node, this.cleanTableWidths);
23237         
23238         
23239     },
23240     
23241     
23242     
23243     
23244     domToHTML : function(currentElement, depth, nopadtext) {
23245         
23246         depth = depth || 0;
23247         nopadtext = nopadtext || false;
23248     
23249         if (!currentElement) {
23250             return this.domToHTML(this.doc.body);
23251         }
23252         
23253         //Roo.log(currentElement);
23254         var j;
23255         var allText = false;
23256         var nodeName = currentElement.nodeName;
23257         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23258         
23259         if  (nodeName == '#text') {
23260             
23261             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23262         }
23263         
23264         
23265         var ret = '';
23266         if (nodeName != 'BODY') {
23267              
23268             var i = 0;
23269             // Prints the node tagName, such as <A>, <IMG>, etc
23270             if (tagName) {
23271                 var attr = [];
23272                 for(i = 0; i < currentElement.attributes.length;i++) {
23273                     // quoting?
23274                     var aname = currentElement.attributes.item(i).name;
23275                     if (!currentElement.attributes.item(i).value.length) {
23276                         continue;
23277                     }
23278                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23279                 }
23280                 
23281                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23282             } 
23283             else {
23284                 
23285                 // eack
23286             }
23287         } else {
23288             tagName = false;
23289         }
23290         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23291             return ret;
23292         }
23293         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23294             nopadtext = true;
23295         }
23296         
23297         
23298         // Traverse the tree
23299         i = 0;
23300         var currentElementChild = currentElement.childNodes.item(i);
23301         var allText = true;
23302         var innerHTML  = '';
23303         lastnode = '';
23304         while (currentElementChild) {
23305             // Formatting code (indent the tree so it looks nice on the screen)
23306             var nopad = nopadtext;
23307             if (lastnode == 'SPAN') {
23308                 nopad  = true;
23309             }
23310             // text
23311             if  (currentElementChild.nodeName == '#text') {
23312                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23313                 toadd = nopadtext ? toadd : toadd.trim();
23314                 if (!nopad && toadd.length > 80) {
23315                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23316                 }
23317                 innerHTML  += toadd;
23318                 
23319                 i++;
23320                 currentElementChild = currentElement.childNodes.item(i);
23321                 lastNode = '';
23322                 continue;
23323             }
23324             allText = false;
23325             
23326             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23327                 
23328             // Recursively traverse the tree structure of the child node
23329             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23330             lastnode = currentElementChild.nodeName;
23331             i++;
23332             currentElementChild=currentElement.childNodes.item(i);
23333         }
23334         
23335         ret += innerHTML;
23336         
23337         if (!allText) {
23338                 // The remaining code is mostly for formatting the tree
23339             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23340         }
23341         
23342         
23343         if (tagName) {
23344             ret+= "</"+tagName+">";
23345         }
23346         return ret;
23347         
23348     },
23349         
23350     applyBlacklists : function()
23351     {
23352         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23353         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23354         
23355         this.white = [];
23356         this.black = [];
23357         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23358             if (b.indexOf(tag) > -1) {
23359                 return;
23360             }
23361             this.white.push(tag);
23362             
23363         }, this);
23364         
23365         Roo.each(w, function(tag) {
23366             if (b.indexOf(tag) > -1) {
23367                 return;
23368             }
23369             if (this.white.indexOf(tag) > -1) {
23370                 return;
23371             }
23372             this.white.push(tag);
23373             
23374         }, this);
23375         
23376         
23377         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23378             if (w.indexOf(tag) > -1) {
23379                 return;
23380             }
23381             this.black.push(tag);
23382             
23383         }, this);
23384         
23385         Roo.each(b, function(tag) {
23386             if (w.indexOf(tag) > -1) {
23387                 return;
23388             }
23389             if (this.black.indexOf(tag) > -1) {
23390                 return;
23391             }
23392             this.black.push(tag);
23393             
23394         }, this);
23395         
23396         
23397         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23398         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23399         
23400         this.cwhite = [];
23401         this.cblack = [];
23402         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23403             if (b.indexOf(tag) > -1) {
23404                 return;
23405             }
23406             this.cwhite.push(tag);
23407             
23408         }, this);
23409         
23410         Roo.each(w, function(tag) {
23411             if (b.indexOf(tag) > -1) {
23412                 return;
23413             }
23414             if (this.cwhite.indexOf(tag) > -1) {
23415                 return;
23416             }
23417             this.cwhite.push(tag);
23418             
23419         }, this);
23420         
23421         
23422         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23423             if (w.indexOf(tag) > -1) {
23424                 return;
23425             }
23426             this.cblack.push(tag);
23427             
23428         }, this);
23429         
23430         Roo.each(b, function(tag) {
23431             if (w.indexOf(tag) > -1) {
23432                 return;
23433             }
23434             if (this.cblack.indexOf(tag) > -1) {
23435                 return;
23436             }
23437             this.cblack.push(tag);
23438             
23439         }, this);
23440     },
23441     
23442     setStylesheets : function(stylesheets)
23443     {
23444         if(typeof(stylesheets) == 'string'){
23445             Roo.get(this.iframe.contentDocument.head).createChild({
23446                 tag : 'link',
23447                 rel : 'stylesheet',
23448                 type : 'text/css',
23449                 href : stylesheets
23450             });
23451             
23452             return;
23453         }
23454         var _this = this;
23455      
23456         Roo.each(stylesheets, function(s) {
23457             if(!s.length){
23458                 return;
23459             }
23460             
23461             Roo.get(_this.iframe.contentDocument.head).createChild({
23462                 tag : 'link',
23463                 rel : 'stylesheet',
23464                 type : 'text/css',
23465                 href : s
23466             });
23467         });
23468
23469         
23470     },
23471     
23472     removeStylesheets : function()
23473     {
23474         var _this = this;
23475         
23476         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23477             s.remove();
23478         });
23479     },
23480     
23481     setStyle : function(style)
23482     {
23483         Roo.get(this.iframe.contentDocument.head).createChild({
23484             tag : 'style',
23485             type : 'text/css',
23486             html : style
23487         });
23488
23489         return;
23490     }
23491     
23492     // hide stuff that is not compatible
23493     /**
23494      * @event blur
23495      * @hide
23496      */
23497     /**
23498      * @event change
23499      * @hide
23500      */
23501     /**
23502      * @event focus
23503      * @hide
23504      */
23505     /**
23506      * @event specialkey
23507      * @hide
23508      */
23509     /**
23510      * @cfg {String} fieldClass @hide
23511      */
23512     /**
23513      * @cfg {String} focusClass @hide
23514      */
23515     /**
23516      * @cfg {String} autoCreate @hide
23517      */
23518     /**
23519      * @cfg {String} inputType @hide
23520      */
23521     /**
23522      * @cfg {String} invalidClass @hide
23523      */
23524     /**
23525      * @cfg {String} invalidText @hide
23526      */
23527     /**
23528      * @cfg {String} msgFx @hide
23529      */
23530     /**
23531      * @cfg {String} validateOnBlur @hide
23532      */
23533 });
23534
23535 Roo.HtmlEditorCore.white = [
23536         'area', 'br', 'img', 'input', 'hr', 'wbr',
23537         
23538        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23539        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23540        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23541        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23542        'table',   'ul',         'xmp', 
23543        
23544        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23545       'thead',   'tr', 
23546      
23547       'dir', 'menu', 'ol', 'ul', 'dl',
23548        
23549       'embed',  'object'
23550 ];
23551
23552
23553 Roo.HtmlEditorCore.black = [
23554     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23555         'applet', // 
23556         'base',   'basefont', 'bgsound', 'blink',  'body', 
23557         'frame',  'frameset', 'head',    'html',   'ilayer', 
23558         'iframe', 'layer',  'link',     'meta',    'object',   
23559         'script', 'style' ,'title',  'xml' // clean later..
23560 ];
23561 Roo.HtmlEditorCore.clean = [
23562     'script', 'style', 'title', 'xml'
23563 ];
23564 Roo.HtmlEditorCore.remove = [
23565     'font'
23566 ];
23567 // attributes..
23568
23569 Roo.HtmlEditorCore.ablack = [
23570     'on'
23571 ];
23572     
23573 Roo.HtmlEditorCore.aclean = [ 
23574     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23575 ];
23576
23577 // protocols..
23578 Roo.HtmlEditorCore.pwhite= [
23579         'http',  'https',  'mailto'
23580 ];
23581
23582 // white listed style attributes.
23583 Roo.HtmlEditorCore.cwhite= [
23584       //  'text-align', /// default is to allow most things..
23585       
23586          
23587 //        'font-size'//??
23588 ];
23589
23590 // black listed style attributes.
23591 Roo.HtmlEditorCore.cblack= [
23592       //  'font-size' -- this can be set by the project 
23593 ];
23594
23595
23596 Roo.HtmlEditorCore.swapCodes   =[ 
23597     [    8211, "--" ], 
23598     [    8212, "--" ], 
23599     [    8216,  "'" ],  
23600     [    8217, "'" ],  
23601     [    8220, '"' ],  
23602     [    8221, '"' ],  
23603     [    8226, "*" ],  
23604     [    8230, "..." ]
23605 ]; 
23606
23607     /*
23608  * - LGPL
23609  *
23610  * HtmlEditor
23611  * 
23612  */
23613
23614 /**
23615  * @class Roo.bootstrap.HtmlEditor
23616  * @extends Roo.bootstrap.TextArea
23617  * Bootstrap HtmlEditor class
23618
23619  * @constructor
23620  * Create a new HtmlEditor
23621  * @param {Object} config The config object
23622  */
23623
23624 Roo.bootstrap.HtmlEditor = function(config){
23625     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23626     if (!this.toolbars) {
23627         this.toolbars = [];
23628     }
23629     
23630     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23631     this.addEvents({
23632             /**
23633              * @event initialize
23634              * Fires when the editor is fully initialized (including the iframe)
23635              * @param {HtmlEditor} this
23636              */
23637             initialize: true,
23638             /**
23639              * @event activate
23640              * Fires when the editor is first receives the focus. Any insertion must wait
23641              * until after this event.
23642              * @param {HtmlEditor} this
23643              */
23644             activate: true,
23645              /**
23646              * @event beforesync
23647              * Fires before the textarea is updated with content from the editor iframe. Return false
23648              * to cancel the sync.
23649              * @param {HtmlEditor} this
23650              * @param {String} html
23651              */
23652             beforesync: true,
23653              /**
23654              * @event beforepush
23655              * Fires before the iframe editor is updated with content from the textarea. Return false
23656              * to cancel the push.
23657              * @param {HtmlEditor} this
23658              * @param {String} html
23659              */
23660             beforepush: true,
23661              /**
23662              * @event sync
23663              * Fires when the textarea is updated with content from the editor iframe.
23664              * @param {HtmlEditor} this
23665              * @param {String} html
23666              */
23667             sync: true,
23668              /**
23669              * @event push
23670              * Fires when the iframe editor is updated with content from the textarea.
23671              * @param {HtmlEditor} this
23672              * @param {String} html
23673              */
23674             push: true,
23675              /**
23676              * @event editmodechange
23677              * Fires when the editor switches edit modes
23678              * @param {HtmlEditor} this
23679              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23680              */
23681             editmodechange: true,
23682             /**
23683              * @event editorevent
23684              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23685              * @param {HtmlEditor} this
23686              */
23687             editorevent: true,
23688             /**
23689              * @event firstfocus
23690              * Fires when on first focus - needed by toolbars..
23691              * @param {HtmlEditor} this
23692              */
23693             firstfocus: true,
23694             /**
23695              * @event autosave
23696              * Auto save the htmlEditor value as a file into Events
23697              * @param {HtmlEditor} this
23698              */
23699             autosave: true,
23700             /**
23701              * @event savedpreview
23702              * preview the saved version of htmlEditor
23703              * @param {HtmlEditor} this
23704              */
23705             savedpreview: true
23706         });
23707 };
23708
23709
23710 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23711     
23712     
23713       /**
23714      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23715      */
23716     toolbars : false,
23717     
23718      /**
23719     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23720     */
23721     btns : [],
23722    
23723      /**
23724      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23725      *                        Roo.resizable.
23726      */
23727     resizable : false,
23728      /**
23729      * @cfg {Number} height (in pixels)
23730      */   
23731     height: 300,
23732    /**
23733      * @cfg {Number} width (in pixels)
23734      */   
23735     width: false,
23736     
23737     /**
23738      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23739      * 
23740      */
23741     stylesheets: false,
23742     
23743     // id of frame..
23744     frameId: false,
23745     
23746     // private properties
23747     validationEvent : false,
23748     deferHeight: true,
23749     initialized : false,
23750     activated : false,
23751     
23752     onFocus : Roo.emptyFn,
23753     iframePad:3,
23754     hideMode:'offsets',
23755     
23756     tbContainer : false,
23757     
23758     bodyCls : '',
23759     
23760     toolbarContainer :function() {
23761         return this.wrap.select('.x-html-editor-tb',true).first();
23762     },
23763
23764     /**
23765      * Protected method that will not generally be called directly. It
23766      * is called when the editor creates its toolbar. Override this method if you need to
23767      * add custom toolbar buttons.
23768      * @param {HtmlEditor} editor
23769      */
23770     createToolbar : function(){
23771         Roo.log('renewing');
23772         Roo.log("create toolbars");
23773         
23774         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23775         this.toolbars[0].render(this.toolbarContainer());
23776         
23777         return;
23778         
23779 //        if (!editor.toolbars || !editor.toolbars.length) {
23780 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23781 //        }
23782 //        
23783 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23784 //            editor.toolbars[i] = Roo.factory(
23785 //                    typeof(editor.toolbars[i]) == 'string' ?
23786 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23787 //                Roo.bootstrap.HtmlEditor);
23788 //            editor.toolbars[i].init(editor);
23789 //        }
23790     },
23791
23792      
23793     // private
23794     onRender : function(ct, position)
23795     {
23796        // Roo.log("Call onRender: " + this.xtype);
23797         var _t = this;
23798         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23799       
23800         this.wrap = this.inputEl().wrap({
23801             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23802         });
23803         
23804         this.editorcore.onRender(ct, position);
23805          
23806         if (this.resizable) {
23807             this.resizeEl = new Roo.Resizable(this.wrap, {
23808                 pinned : true,
23809                 wrap: true,
23810                 dynamic : true,
23811                 minHeight : this.height,
23812                 height: this.height,
23813                 handles : this.resizable,
23814                 width: this.width,
23815                 listeners : {
23816                     resize : function(r, w, h) {
23817                         _t.onResize(w,h); // -something
23818                     }
23819                 }
23820             });
23821             
23822         }
23823         this.createToolbar(this);
23824        
23825         
23826         if(!this.width && this.resizable){
23827             this.setSize(this.wrap.getSize());
23828         }
23829         if (this.resizeEl) {
23830             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23831             // should trigger onReize..
23832         }
23833         
23834     },
23835
23836     // private
23837     onResize : function(w, h)
23838     {
23839         Roo.log('resize: ' +w + ',' + h );
23840         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23841         var ew = false;
23842         var eh = false;
23843         
23844         if(this.inputEl() ){
23845             if(typeof w == 'number'){
23846                 var aw = w - this.wrap.getFrameWidth('lr');
23847                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23848                 ew = aw;
23849             }
23850             if(typeof h == 'number'){
23851                  var tbh = -11;  // fixme it needs to tool bar size!
23852                 for (var i =0; i < this.toolbars.length;i++) {
23853                     // fixme - ask toolbars for heights?
23854                     tbh += this.toolbars[i].el.getHeight();
23855                     //if (this.toolbars[i].footer) {
23856                     //    tbh += this.toolbars[i].footer.el.getHeight();
23857                     //}
23858                 }
23859               
23860                 
23861                 
23862                 
23863                 
23864                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23865                 ah -= 5; // knock a few pixes off for look..
23866                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23867                 var eh = ah;
23868             }
23869         }
23870         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23871         this.editorcore.onResize(ew,eh);
23872         
23873     },
23874
23875     /**
23876      * Toggles the editor between standard and source edit mode.
23877      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23878      */
23879     toggleSourceEdit : function(sourceEditMode)
23880     {
23881         this.editorcore.toggleSourceEdit(sourceEditMode);
23882         
23883         if(this.editorcore.sourceEditMode){
23884             Roo.log('editor - showing textarea');
23885             
23886 //            Roo.log('in');
23887 //            Roo.log(this.syncValue());
23888             this.syncValue();
23889             this.inputEl().removeClass(['hide', 'x-hidden']);
23890             this.inputEl().dom.removeAttribute('tabIndex');
23891             this.inputEl().focus();
23892         }else{
23893             Roo.log('editor - hiding textarea');
23894 //            Roo.log('out')
23895 //            Roo.log(this.pushValue()); 
23896             this.pushValue();
23897             
23898             this.inputEl().addClass(['hide', 'x-hidden']);
23899             this.inputEl().dom.setAttribute('tabIndex', -1);
23900             //this.deferFocus();
23901         }
23902          
23903         if(this.resizable){
23904             this.setSize(this.wrap.getSize());
23905         }
23906         
23907         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23908     },
23909  
23910     // private (for BoxComponent)
23911     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23912
23913     // private (for BoxComponent)
23914     getResizeEl : function(){
23915         return this.wrap;
23916     },
23917
23918     // private (for BoxComponent)
23919     getPositionEl : function(){
23920         return this.wrap;
23921     },
23922
23923     // private
23924     initEvents : function(){
23925         this.originalValue = this.getValue();
23926     },
23927
23928 //    /**
23929 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23930 //     * @method
23931 //     */
23932 //    markInvalid : Roo.emptyFn,
23933 //    /**
23934 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23935 //     * @method
23936 //     */
23937 //    clearInvalid : Roo.emptyFn,
23938
23939     setValue : function(v){
23940         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23941         this.editorcore.pushValue();
23942     },
23943
23944      
23945     // private
23946     deferFocus : function(){
23947         this.focus.defer(10, this);
23948     },
23949
23950     // doc'ed in Field
23951     focus : function(){
23952         this.editorcore.focus();
23953         
23954     },
23955       
23956
23957     // private
23958     onDestroy : function(){
23959         
23960         
23961         
23962         if(this.rendered){
23963             
23964             for (var i =0; i < this.toolbars.length;i++) {
23965                 // fixme - ask toolbars for heights?
23966                 this.toolbars[i].onDestroy();
23967             }
23968             
23969             this.wrap.dom.innerHTML = '';
23970             this.wrap.remove();
23971         }
23972     },
23973
23974     // private
23975     onFirstFocus : function(){
23976         //Roo.log("onFirstFocus");
23977         this.editorcore.onFirstFocus();
23978          for (var i =0; i < this.toolbars.length;i++) {
23979             this.toolbars[i].onFirstFocus();
23980         }
23981         
23982     },
23983     
23984     // private
23985     syncValue : function()
23986     {   
23987         this.editorcore.syncValue();
23988     },
23989     
23990     pushValue : function()
23991     {   
23992         this.editorcore.pushValue();
23993     }
23994      
23995     
23996     // hide stuff that is not compatible
23997     /**
23998      * @event blur
23999      * @hide
24000      */
24001     /**
24002      * @event change
24003      * @hide
24004      */
24005     /**
24006      * @event focus
24007      * @hide
24008      */
24009     /**
24010      * @event specialkey
24011      * @hide
24012      */
24013     /**
24014      * @cfg {String} fieldClass @hide
24015      */
24016     /**
24017      * @cfg {String} focusClass @hide
24018      */
24019     /**
24020      * @cfg {String} autoCreate @hide
24021      */
24022     /**
24023      * @cfg {String} inputType @hide
24024      */
24025      
24026     /**
24027      * @cfg {String} invalidText @hide
24028      */
24029     /**
24030      * @cfg {String} msgFx @hide
24031      */
24032     /**
24033      * @cfg {String} validateOnBlur @hide
24034      */
24035 });
24036  
24037     
24038    
24039    
24040    
24041       
24042 Roo.namespace('Roo.bootstrap.htmleditor');
24043 /**
24044  * @class Roo.bootstrap.HtmlEditorToolbar1
24045  * Basic Toolbar
24046  * 
24047  * Usage:
24048  *
24049  new Roo.bootstrap.HtmlEditor({
24050     ....
24051     toolbars : [
24052         new Roo.bootstrap.HtmlEditorToolbar1({
24053             disable : { fonts: 1 , format: 1, ..., ... , ...],
24054             btns : [ .... ]
24055         })
24056     }
24057      
24058  * 
24059  * @cfg {Object} disable List of elements to disable..
24060  * @cfg {Array} btns List of additional buttons.
24061  * 
24062  * 
24063  * NEEDS Extra CSS? 
24064  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24065  */
24066  
24067 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24068 {
24069     
24070     Roo.apply(this, config);
24071     
24072     // default disabled, based on 'good practice'..
24073     this.disable = this.disable || {};
24074     Roo.applyIf(this.disable, {
24075         fontSize : true,
24076         colors : true,
24077         specialElements : true
24078     });
24079     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24080     
24081     this.editor = config.editor;
24082     this.editorcore = config.editor.editorcore;
24083     
24084     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24085     
24086     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24087     // dont call parent... till later.
24088 }
24089 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
24090      
24091     bar : true,
24092     
24093     editor : false,
24094     editorcore : false,
24095     
24096     
24097     formats : [
24098         "p" ,  
24099         "h1","h2","h3","h4","h5","h6", 
24100         "pre", "code", 
24101         "abbr", "acronym", "address", "cite", "samp", "var",
24102         'div','span'
24103     ],
24104     
24105     onRender : function(ct, position)
24106     {
24107        // Roo.log("Call onRender: " + this.xtype);
24108         
24109        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24110        Roo.log(this.el);
24111        this.el.dom.style.marginBottom = '0';
24112        var _this = this;
24113        var editorcore = this.editorcore;
24114        var editor= this.editor;
24115        
24116        var children = [];
24117        var btn = function(id,cmd , toggle, handler, html){
24118        
24119             var  event = toggle ? 'toggle' : 'click';
24120        
24121             var a = {
24122                 size : 'sm',
24123                 xtype: 'Button',
24124                 xns: Roo.bootstrap,
24125                 //glyphicon : id,
24126                 fa: id,
24127                 cmd : id || cmd,
24128                 enableToggle:toggle !== false,
24129                 html : html || '',
24130                 pressed : toggle ? false : null,
24131                 listeners : {}
24132             };
24133             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24134                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24135             };
24136             children.push(a);
24137             return a;
24138        }
24139        
24140     //    var cb_box = function...
24141         
24142         var style = {
24143                 xtype: 'Button',
24144                 size : 'sm',
24145                 xns: Roo.bootstrap,
24146                 fa : 'font',
24147                 //html : 'submit'
24148                 menu : {
24149                     xtype: 'Menu',
24150                     xns: Roo.bootstrap,
24151                     items:  []
24152                 }
24153         };
24154         Roo.each(this.formats, function(f) {
24155             style.menu.items.push({
24156                 xtype :'MenuItem',
24157                 xns: Roo.bootstrap,
24158                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24159                 tagname : f,
24160                 listeners : {
24161                     click : function()
24162                     {
24163                         editorcore.insertTag(this.tagname);
24164                         editor.focus();
24165                     }
24166                 }
24167                 
24168             });
24169         });
24170         children.push(style);   
24171         
24172         btn('bold',false,true);
24173         btn('italic',false,true);
24174         btn('align-left', 'justifyleft',true);
24175         btn('align-center', 'justifycenter',true);
24176         btn('align-right' , 'justifyright',true);
24177         btn('link', false, false, function(btn) {
24178             //Roo.log("create link?");
24179             var url = prompt(this.createLinkText, this.defaultLinkValue);
24180             if(url && url != 'http:/'+'/'){
24181                 this.editorcore.relayCmd('createlink', url);
24182             }
24183         }),
24184         btn('list','insertunorderedlist',true);
24185         btn('pencil', false,true, function(btn){
24186                 Roo.log(this);
24187                 this.toggleSourceEdit(btn.pressed);
24188         });
24189         
24190         if (this.editor.btns.length > 0) {
24191             for (var i = 0; i<this.editor.btns.length; i++) {
24192                 children.push(this.editor.btns[i]);
24193             }
24194         }
24195         
24196         /*
24197         var cog = {
24198                 xtype: 'Button',
24199                 size : 'sm',
24200                 xns: Roo.bootstrap,
24201                 glyphicon : 'cog',
24202                 //html : 'submit'
24203                 menu : {
24204                     xtype: 'Menu',
24205                     xns: Roo.bootstrap,
24206                     items:  []
24207                 }
24208         };
24209         
24210         cog.menu.items.push({
24211             xtype :'MenuItem',
24212             xns: Roo.bootstrap,
24213             html : Clean styles,
24214             tagname : f,
24215             listeners : {
24216                 click : function()
24217                 {
24218                     editorcore.insertTag(this.tagname);
24219                     editor.focus();
24220                 }
24221             }
24222             
24223         });
24224        */
24225         
24226          
24227        this.xtype = 'NavSimplebar';
24228         
24229         for(var i=0;i< children.length;i++) {
24230             
24231             this.buttons.add(this.addxtypeChild(children[i]));
24232             
24233         }
24234         
24235         editor.on('editorevent', this.updateToolbar, this);
24236     },
24237     onBtnClick : function(id)
24238     {
24239        this.editorcore.relayCmd(id);
24240        this.editorcore.focus();
24241     },
24242     
24243     /**
24244      * Protected method that will not generally be called directly. It triggers
24245      * a toolbar update by reading the markup state of the current selection in the editor.
24246      */
24247     updateToolbar: function(){
24248
24249         if(!this.editorcore.activated){
24250             this.editor.onFirstFocus(); // is this neeed?
24251             return;
24252         }
24253
24254         var btns = this.buttons; 
24255         var doc = this.editorcore.doc;
24256         btns.get('bold').setActive(doc.queryCommandState('bold'));
24257         btns.get('italic').setActive(doc.queryCommandState('italic'));
24258         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24259         
24260         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24261         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24262         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24263         
24264         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24265         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24266          /*
24267         
24268         var ans = this.editorcore.getAllAncestors();
24269         if (this.formatCombo) {
24270             
24271             
24272             var store = this.formatCombo.store;
24273             this.formatCombo.setValue("");
24274             for (var i =0; i < ans.length;i++) {
24275                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24276                     // select it..
24277                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24278                     break;
24279                 }
24280             }
24281         }
24282         
24283         
24284         
24285         // hides menus... - so this cant be on a menu...
24286         Roo.bootstrap.MenuMgr.hideAll();
24287         */
24288         Roo.bootstrap.MenuMgr.hideAll();
24289         //this.editorsyncValue();
24290     },
24291     onFirstFocus: function() {
24292         this.buttons.each(function(item){
24293            item.enable();
24294         });
24295     },
24296     toggleSourceEdit : function(sourceEditMode){
24297         
24298           
24299         if(sourceEditMode){
24300             Roo.log("disabling buttons");
24301            this.buttons.each( function(item){
24302                 if(item.cmd != 'pencil'){
24303                     item.disable();
24304                 }
24305             });
24306           
24307         }else{
24308             Roo.log("enabling buttons");
24309             if(this.editorcore.initialized){
24310                 this.buttons.each( function(item){
24311                     item.enable();
24312                 });
24313             }
24314             
24315         }
24316         Roo.log("calling toggole on editor");
24317         // tell the editor that it's been pressed..
24318         this.editor.toggleSourceEdit(sourceEditMode);
24319        
24320     }
24321 });
24322
24323
24324
24325
24326
24327 /**
24328  * @class Roo.bootstrap.Table.AbstractSelectionModel
24329  * @extends Roo.util.Observable
24330  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24331  * implemented by descendant classes.  This class should not be directly instantiated.
24332  * @constructor
24333  */
24334 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24335     this.locked = false;
24336     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24337 };
24338
24339
24340 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24341     /** @ignore Called by the grid automatically. Do not call directly. */
24342     init : function(grid){
24343         this.grid = grid;
24344         this.initEvents();
24345     },
24346
24347     /**
24348      * Locks the selections.
24349      */
24350     lock : function(){
24351         this.locked = true;
24352     },
24353
24354     /**
24355      * Unlocks the selections.
24356      */
24357     unlock : function(){
24358         this.locked = false;
24359     },
24360
24361     /**
24362      * Returns true if the selections are locked.
24363      * @return {Boolean}
24364      */
24365     isLocked : function(){
24366         return this.locked;
24367     }
24368 });
24369 /**
24370  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24371  * @class Roo.bootstrap.Table.RowSelectionModel
24372  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24373  * It supports multiple selections and keyboard selection/navigation. 
24374  * @constructor
24375  * @param {Object} config
24376  */
24377
24378 Roo.bootstrap.Table.RowSelectionModel = function(config){
24379     Roo.apply(this, config);
24380     this.selections = new Roo.util.MixedCollection(false, function(o){
24381         return o.id;
24382     });
24383
24384     this.last = false;
24385     this.lastActive = false;
24386
24387     this.addEvents({
24388         /**
24389              * @event selectionchange
24390              * Fires when the selection changes
24391              * @param {SelectionModel} this
24392              */
24393             "selectionchange" : true,
24394         /**
24395              * @event afterselectionchange
24396              * Fires after the selection changes (eg. by key press or clicking)
24397              * @param {SelectionModel} this
24398              */
24399             "afterselectionchange" : true,
24400         /**
24401              * @event beforerowselect
24402              * Fires when a row is selected being selected, return false to cancel.
24403              * @param {SelectionModel} this
24404              * @param {Number} rowIndex The selected index
24405              * @param {Boolean} keepExisting False if other selections will be cleared
24406              */
24407             "beforerowselect" : true,
24408         /**
24409              * @event rowselect
24410              * Fires when a row is selected.
24411              * @param {SelectionModel} this
24412              * @param {Number} rowIndex The selected index
24413              * @param {Roo.data.Record} r The record
24414              */
24415             "rowselect" : true,
24416         /**
24417              * @event rowdeselect
24418              * Fires when a row is deselected.
24419              * @param {SelectionModel} this
24420              * @param {Number} rowIndex The selected index
24421              */
24422         "rowdeselect" : true
24423     });
24424     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24425     this.locked = false;
24426  };
24427
24428 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24429     /**
24430      * @cfg {Boolean} singleSelect
24431      * True to allow selection of only one row at a time (defaults to false)
24432      */
24433     singleSelect : false,
24434
24435     // private
24436     initEvents : function()
24437     {
24438
24439         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24440         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24441         //}else{ // allow click to work like normal
24442          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24443         //}
24444         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24445         this.grid.on("rowclick", this.handleMouseDown, this);
24446         
24447         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24448             "up" : function(e){
24449                 if(!e.shiftKey){
24450                     this.selectPrevious(e.shiftKey);
24451                 }else if(this.last !== false && this.lastActive !== false){
24452                     var last = this.last;
24453                     this.selectRange(this.last,  this.lastActive-1);
24454                     this.grid.getView().focusRow(this.lastActive);
24455                     if(last !== false){
24456                         this.last = last;
24457                     }
24458                 }else{
24459                     this.selectFirstRow();
24460                 }
24461                 this.fireEvent("afterselectionchange", this);
24462             },
24463             "down" : function(e){
24464                 if(!e.shiftKey){
24465                     this.selectNext(e.shiftKey);
24466                 }else if(this.last !== false && this.lastActive !== false){
24467                     var last = this.last;
24468                     this.selectRange(this.last,  this.lastActive+1);
24469                     this.grid.getView().focusRow(this.lastActive);
24470                     if(last !== false){
24471                         this.last = last;
24472                     }
24473                 }else{
24474                     this.selectFirstRow();
24475                 }
24476                 this.fireEvent("afterselectionchange", this);
24477             },
24478             scope: this
24479         });
24480         this.grid.store.on('load', function(){
24481             this.selections.clear();
24482         },this);
24483         /*
24484         var view = this.grid.view;
24485         view.on("refresh", this.onRefresh, this);
24486         view.on("rowupdated", this.onRowUpdated, this);
24487         view.on("rowremoved", this.onRemove, this);
24488         */
24489     },
24490
24491     // private
24492     onRefresh : function()
24493     {
24494         var ds = this.grid.store, i, v = this.grid.view;
24495         var s = this.selections;
24496         s.each(function(r){
24497             if((i = ds.indexOfId(r.id)) != -1){
24498                 v.onRowSelect(i);
24499             }else{
24500                 s.remove(r);
24501             }
24502         });
24503     },
24504
24505     // private
24506     onRemove : function(v, index, r){
24507         this.selections.remove(r);
24508     },
24509
24510     // private
24511     onRowUpdated : function(v, index, r){
24512         if(this.isSelected(r)){
24513             v.onRowSelect(index);
24514         }
24515     },
24516
24517     /**
24518      * Select records.
24519      * @param {Array} records The records to select
24520      * @param {Boolean} keepExisting (optional) True to keep existing selections
24521      */
24522     selectRecords : function(records, keepExisting)
24523     {
24524         if(!keepExisting){
24525             this.clearSelections();
24526         }
24527             var ds = this.grid.store;
24528         for(var i = 0, len = records.length; i < len; i++){
24529             this.selectRow(ds.indexOf(records[i]), true);
24530         }
24531     },
24532
24533     /**
24534      * Gets the number of selected rows.
24535      * @return {Number}
24536      */
24537     getCount : function(){
24538         return this.selections.length;
24539     },
24540
24541     /**
24542      * Selects the first row in the grid.
24543      */
24544     selectFirstRow : function(){
24545         this.selectRow(0);
24546     },
24547
24548     /**
24549      * Select the last row.
24550      * @param {Boolean} keepExisting (optional) True to keep existing selections
24551      */
24552     selectLastRow : function(keepExisting){
24553         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24554         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24555     },
24556
24557     /**
24558      * Selects the row immediately following the last selected row.
24559      * @param {Boolean} keepExisting (optional) True to keep existing selections
24560      */
24561     selectNext : function(keepExisting)
24562     {
24563             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24564             this.selectRow(this.last+1, keepExisting);
24565             this.grid.getView().focusRow(this.last);
24566         }
24567     },
24568
24569     /**
24570      * Selects the row that precedes the last selected row.
24571      * @param {Boolean} keepExisting (optional) True to keep existing selections
24572      */
24573     selectPrevious : function(keepExisting){
24574         if(this.last){
24575             this.selectRow(this.last-1, keepExisting);
24576             this.grid.getView().focusRow(this.last);
24577         }
24578     },
24579
24580     /**
24581      * Returns the selected records
24582      * @return {Array} Array of selected records
24583      */
24584     getSelections : function(){
24585         return [].concat(this.selections.items);
24586     },
24587
24588     /**
24589      * Returns the first selected record.
24590      * @return {Record}
24591      */
24592     getSelected : function(){
24593         return this.selections.itemAt(0);
24594     },
24595
24596
24597     /**
24598      * Clears all selections.
24599      */
24600     clearSelections : function(fast)
24601     {
24602         if(this.locked) {
24603             return;
24604         }
24605         if(fast !== true){
24606                 var ds = this.grid.store;
24607             var s = this.selections;
24608             s.each(function(r){
24609                 this.deselectRow(ds.indexOfId(r.id));
24610             }, this);
24611             s.clear();
24612         }else{
24613             this.selections.clear();
24614         }
24615         this.last = false;
24616     },
24617
24618
24619     /**
24620      * Selects all rows.
24621      */
24622     selectAll : function(){
24623         if(this.locked) {
24624             return;
24625         }
24626         this.selections.clear();
24627         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24628             this.selectRow(i, true);
24629         }
24630     },
24631
24632     /**
24633      * Returns True if there is a selection.
24634      * @return {Boolean}
24635      */
24636     hasSelection : function(){
24637         return this.selections.length > 0;
24638     },
24639
24640     /**
24641      * Returns True if the specified row is selected.
24642      * @param {Number/Record} record The record or index of the record to check
24643      * @return {Boolean}
24644      */
24645     isSelected : function(index){
24646             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24647         return (r && this.selections.key(r.id) ? true : false);
24648     },
24649
24650     /**
24651      * Returns True if the specified record id is selected.
24652      * @param {String} id The id of record to check
24653      * @return {Boolean}
24654      */
24655     isIdSelected : function(id){
24656         return (this.selections.key(id) ? true : false);
24657     },
24658
24659
24660     // private
24661     handleMouseDBClick : function(e, t){
24662         
24663     },
24664     // private
24665     handleMouseDown : function(e, t)
24666     {
24667             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24668         if(this.isLocked() || rowIndex < 0 ){
24669             return;
24670         };
24671         if(e.shiftKey && this.last !== false){
24672             var last = this.last;
24673             this.selectRange(last, rowIndex, e.ctrlKey);
24674             this.last = last; // reset the last
24675             t.focus();
24676     
24677         }else{
24678             var isSelected = this.isSelected(rowIndex);
24679             //Roo.log("select row:" + rowIndex);
24680             if(isSelected){
24681                 this.deselectRow(rowIndex);
24682             } else {
24683                         this.selectRow(rowIndex, true);
24684             }
24685     
24686             /*
24687                 if(e.button !== 0 && isSelected){
24688                 alert('rowIndex 2: ' + rowIndex);
24689                     view.focusRow(rowIndex);
24690                 }else if(e.ctrlKey && isSelected){
24691                     this.deselectRow(rowIndex);
24692                 }else if(!isSelected){
24693                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24694                     view.focusRow(rowIndex);
24695                 }
24696             */
24697         }
24698         this.fireEvent("afterselectionchange", this);
24699     },
24700     // private
24701     handleDragableRowClick :  function(grid, rowIndex, e) 
24702     {
24703         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24704             this.selectRow(rowIndex, false);
24705             grid.view.focusRow(rowIndex);
24706              this.fireEvent("afterselectionchange", this);
24707         }
24708     },
24709     
24710     /**
24711      * Selects multiple rows.
24712      * @param {Array} rows Array of the indexes of the row to select
24713      * @param {Boolean} keepExisting (optional) True to keep existing selections
24714      */
24715     selectRows : function(rows, keepExisting){
24716         if(!keepExisting){
24717             this.clearSelections();
24718         }
24719         for(var i = 0, len = rows.length; i < len; i++){
24720             this.selectRow(rows[i], true);
24721         }
24722     },
24723
24724     /**
24725      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24726      * @param {Number} startRow The index of the first row in the range
24727      * @param {Number} endRow The index of the last row in the range
24728      * @param {Boolean} keepExisting (optional) True to retain existing selections
24729      */
24730     selectRange : function(startRow, endRow, keepExisting){
24731         if(this.locked) {
24732             return;
24733         }
24734         if(!keepExisting){
24735             this.clearSelections();
24736         }
24737         if(startRow <= endRow){
24738             for(var i = startRow; i <= endRow; i++){
24739                 this.selectRow(i, true);
24740             }
24741         }else{
24742             for(var i = startRow; i >= endRow; i--){
24743                 this.selectRow(i, true);
24744             }
24745         }
24746     },
24747
24748     /**
24749      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24750      * @param {Number} startRow The index of the first row in the range
24751      * @param {Number} endRow The index of the last row in the range
24752      */
24753     deselectRange : function(startRow, endRow, preventViewNotify){
24754         if(this.locked) {
24755             return;
24756         }
24757         for(var i = startRow; i <= endRow; i++){
24758             this.deselectRow(i, preventViewNotify);
24759         }
24760     },
24761
24762     /**
24763      * Selects a row.
24764      * @param {Number} row The index of the row to select
24765      * @param {Boolean} keepExisting (optional) True to keep existing selections
24766      */
24767     selectRow : function(index, keepExisting, preventViewNotify)
24768     {
24769             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24770             return;
24771         }
24772         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24773             if(!keepExisting || this.singleSelect){
24774                 this.clearSelections();
24775             }
24776             
24777             var r = this.grid.store.getAt(index);
24778             //console.log('selectRow - record id :' + r.id);
24779             
24780             this.selections.add(r);
24781             this.last = this.lastActive = index;
24782             if(!preventViewNotify){
24783                 var proxy = new Roo.Element(
24784                                 this.grid.getRowDom(index)
24785                 );
24786                 proxy.addClass('bg-info info');
24787             }
24788             this.fireEvent("rowselect", this, index, r);
24789             this.fireEvent("selectionchange", this);
24790         }
24791     },
24792
24793     /**
24794      * Deselects a row.
24795      * @param {Number} row The index of the row to deselect
24796      */
24797     deselectRow : function(index, preventViewNotify)
24798     {
24799         if(this.locked) {
24800             return;
24801         }
24802         if(this.last == index){
24803             this.last = false;
24804         }
24805         if(this.lastActive == index){
24806             this.lastActive = false;
24807         }
24808         
24809         var r = this.grid.store.getAt(index);
24810         if (!r) {
24811             return;
24812         }
24813         
24814         this.selections.remove(r);
24815         //.console.log('deselectRow - record id :' + r.id);
24816         if(!preventViewNotify){
24817         
24818             var proxy = new Roo.Element(
24819                 this.grid.getRowDom(index)
24820             );
24821             proxy.removeClass('bg-info info');
24822         }
24823         this.fireEvent("rowdeselect", this, index);
24824         this.fireEvent("selectionchange", this);
24825     },
24826
24827     // private
24828     restoreLast : function(){
24829         if(this._last){
24830             this.last = this._last;
24831         }
24832     },
24833
24834     // private
24835     acceptsNav : function(row, col, cm){
24836         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24837     },
24838
24839     // private
24840     onEditorKey : function(field, e){
24841         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24842         if(k == e.TAB){
24843             e.stopEvent();
24844             ed.completeEdit();
24845             if(e.shiftKey){
24846                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24847             }else{
24848                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24849             }
24850         }else if(k == e.ENTER && !e.ctrlKey){
24851             e.stopEvent();
24852             ed.completeEdit();
24853             if(e.shiftKey){
24854                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24855             }else{
24856                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24857             }
24858         }else if(k == e.ESC){
24859             ed.cancelEdit();
24860         }
24861         if(newCell){
24862             g.startEditing(newCell[0], newCell[1]);
24863         }
24864     }
24865 });
24866 /*
24867  * Based on:
24868  * Ext JS Library 1.1.1
24869  * Copyright(c) 2006-2007, Ext JS, LLC.
24870  *
24871  * Originally Released Under LGPL - original licence link has changed is not relivant.
24872  *
24873  * Fork - LGPL
24874  * <script type="text/javascript">
24875  */
24876  
24877 /**
24878  * @class Roo.bootstrap.PagingToolbar
24879  * @extends Roo.bootstrap.NavSimplebar
24880  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24881  * @constructor
24882  * Create a new PagingToolbar
24883  * @param {Object} config The config object
24884  * @param {Roo.data.Store} store
24885  */
24886 Roo.bootstrap.PagingToolbar = function(config)
24887 {
24888     // old args format still supported... - xtype is prefered..
24889         // created from xtype...
24890     
24891     this.ds = config.dataSource;
24892     
24893     if (config.store && !this.ds) {
24894         this.store= Roo.factory(config.store, Roo.data);
24895         this.ds = this.store;
24896         this.ds.xmodule = this.xmodule || false;
24897     }
24898     
24899     this.toolbarItems = [];
24900     if (config.items) {
24901         this.toolbarItems = config.items;
24902     }
24903     
24904     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24905     
24906     this.cursor = 0;
24907     
24908     if (this.ds) { 
24909         this.bind(this.ds);
24910     }
24911     
24912     if (Roo.bootstrap.version == 4) {
24913         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24914     } else {
24915         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24916     }
24917     
24918 };
24919
24920 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24921     /**
24922      * @cfg {Roo.data.Store} dataSource
24923      * The underlying data store providing the paged data
24924      */
24925     /**
24926      * @cfg {String/HTMLElement/Element} container
24927      * container The id or element that will contain the toolbar
24928      */
24929     /**
24930      * @cfg {Boolean} displayInfo
24931      * True to display the displayMsg (defaults to false)
24932      */
24933     /**
24934      * @cfg {Number} pageSize
24935      * The number of records to display per page (defaults to 20)
24936      */
24937     pageSize: 20,
24938     /**
24939      * @cfg {String} displayMsg
24940      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24941      */
24942     displayMsg : 'Displaying {0} - {1} of {2}',
24943     /**
24944      * @cfg {String} emptyMsg
24945      * The message to display when no records are found (defaults to "No data to display")
24946      */
24947     emptyMsg : 'No data to display',
24948     /**
24949      * Customizable piece of the default paging text (defaults to "Page")
24950      * @type String
24951      */
24952     beforePageText : "Page",
24953     /**
24954      * Customizable piece of the default paging text (defaults to "of %0")
24955      * @type String
24956      */
24957     afterPageText : "of {0}",
24958     /**
24959      * Customizable piece of the default paging text (defaults to "First Page")
24960      * @type String
24961      */
24962     firstText : "First Page",
24963     /**
24964      * Customizable piece of the default paging text (defaults to "Previous Page")
24965      * @type String
24966      */
24967     prevText : "Previous Page",
24968     /**
24969      * Customizable piece of the default paging text (defaults to "Next Page")
24970      * @type String
24971      */
24972     nextText : "Next Page",
24973     /**
24974      * Customizable piece of the default paging text (defaults to "Last Page")
24975      * @type String
24976      */
24977     lastText : "Last Page",
24978     /**
24979      * Customizable piece of the default paging text (defaults to "Refresh")
24980      * @type String
24981      */
24982     refreshText : "Refresh",
24983
24984     buttons : false,
24985     // private
24986     onRender : function(ct, position) 
24987     {
24988         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24989         this.navgroup.parentId = this.id;
24990         this.navgroup.onRender(this.el, null);
24991         // add the buttons to the navgroup
24992         
24993         if(this.displayInfo){
24994             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24995             this.displayEl = this.el.select('.x-paging-info', true).first();
24996 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24997 //            this.displayEl = navel.el.select('span',true).first();
24998         }
24999         
25000         var _this = this;
25001         
25002         if(this.buttons){
25003             Roo.each(_this.buttons, function(e){ // this might need to use render????
25004                Roo.factory(e).render(_this.el);
25005             });
25006         }
25007             
25008         Roo.each(_this.toolbarItems, function(e) {
25009             _this.navgroup.addItem(e);
25010         });
25011         
25012         
25013         this.first = this.navgroup.addItem({
25014             tooltip: this.firstText,
25015             cls: "prev btn-outline-secondary",
25016             html : ' <i class="fa fa-step-backward"></i>',
25017             disabled: true,
25018             preventDefault: true,
25019             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25020         });
25021         
25022         this.prev =  this.navgroup.addItem({
25023             tooltip: this.prevText,
25024             cls: "prev btn-outline-secondary",
25025             html : ' <i class="fa fa-backward"></i>',
25026             disabled: true,
25027             preventDefault: true,
25028             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
25029         });
25030     //this.addSeparator();
25031         
25032         
25033         var field = this.navgroup.addItem( {
25034             tagtype : 'span',
25035             cls : 'x-paging-position  btn-outline-secondary',
25036              disabled: true,
25037             html : this.beforePageText  +
25038                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25039                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
25040          } ); //?? escaped?
25041         
25042         this.field = field.el.select('input', true).first();
25043         this.field.on("keydown", this.onPagingKeydown, this);
25044         this.field.on("focus", function(){this.dom.select();});
25045     
25046     
25047         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
25048         //this.field.setHeight(18);
25049         //this.addSeparator();
25050         this.next = this.navgroup.addItem({
25051             tooltip: this.nextText,
25052             cls: "next btn-outline-secondary",
25053             html : ' <i class="fa fa-forward"></i>',
25054             disabled: true,
25055             preventDefault: true,
25056             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25057         });
25058         this.last = this.navgroup.addItem({
25059             tooltip: this.lastText,
25060             html : ' <i class="fa fa-step-forward"></i>',
25061             cls: "next btn-outline-secondary",
25062             disabled: true,
25063             preventDefault: true,
25064             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25065         });
25066     //this.addSeparator();
25067         this.loading = this.navgroup.addItem({
25068             tooltip: this.refreshText,
25069             cls: "btn-outline-secondary",
25070             html : ' <i class="fa fa-refresh"></i>',
25071             preventDefault: true,
25072             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25073         });
25074         
25075     },
25076
25077     // private
25078     updateInfo : function(){
25079         if(this.displayEl){
25080             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25081             var msg = count == 0 ?
25082                 this.emptyMsg :
25083                 String.format(
25084                     this.displayMsg,
25085                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25086                 );
25087             this.displayEl.update(msg);
25088         }
25089     },
25090
25091     // private
25092     onLoad : function(ds, r, o)
25093     {
25094         this.cursor = o.params.start ? o.params.start : 0;
25095         
25096         var d = this.getPageData(),
25097             ap = d.activePage,
25098             ps = d.pages;
25099         
25100         
25101         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25102         this.field.dom.value = ap;
25103         this.first.setDisabled(ap == 1);
25104         this.prev.setDisabled(ap == 1);
25105         this.next.setDisabled(ap == ps);
25106         this.last.setDisabled(ap == ps);
25107         this.loading.enable();
25108         this.updateInfo();
25109     },
25110
25111     // private
25112     getPageData : function(){
25113         var total = this.ds.getTotalCount();
25114         return {
25115             total : total,
25116             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25117             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25118         };
25119     },
25120
25121     // private
25122     onLoadError : function(){
25123         this.loading.enable();
25124     },
25125
25126     // private
25127     onPagingKeydown : function(e){
25128         var k = e.getKey();
25129         var d = this.getPageData();
25130         if(k == e.RETURN){
25131             var v = this.field.dom.value, pageNum;
25132             if(!v || isNaN(pageNum = parseInt(v, 10))){
25133                 this.field.dom.value = d.activePage;
25134                 return;
25135             }
25136             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25137             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25138             e.stopEvent();
25139         }
25140         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))
25141         {
25142           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25143           this.field.dom.value = pageNum;
25144           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25145           e.stopEvent();
25146         }
25147         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25148         {
25149           var v = this.field.dom.value, pageNum; 
25150           var increment = (e.shiftKey) ? 10 : 1;
25151           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25152                 increment *= -1;
25153           }
25154           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25155             this.field.dom.value = d.activePage;
25156             return;
25157           }
25158           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25159           {
25160             this.field.dom.value = parseInt(v, 10) + increment;
25161             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25162             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25163           }
25164           e.stopEvent();
25165         }
25166     },
25167
25168     // private
25169     beforeLoad : function(){
25170         if(this.loading){
25171             this.loading.disable();
25172         }
25173     },
25174
25175     // private
25176     onClick : function(which){
25177         
25178         var ds = this.ds;
25179         if (!ds) {
25180             return;
25181         }
25182         
25183         switch(which){
25184             case "first":
25185                 ds.load({params:{start: 0, limit: this.pageSize}});
25186             break;
25187             case "prev":
25188                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25189             break;
25190             case "next":
25191                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25192             break;
25193             case "last":
25194                 var total = ds.getTotalCount();
25195                 var extra = total % this.pageSize;
25196                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25197                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25198             break;
25199             case "refresh":
25200                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25201             break;
25202         }
25203     },
25204
25205     /**
25206      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25207      * @param {Roo.data.Store} store The data store to unbind
25208      */
25209     unbind : function(ds){
25210         ds.un("beforeload", this.beforeLoad, this);
25211         ds.un("load", this.onLoad, this);
25212         ds.un("loadexception", this.onLoadError, this);
25213         ds.un("remove", this.updateInfo, this);
25214         ds.un("add", this.updateInfo, this);
25215         this.ds = undefined;
25216     },
25217
25218     /**
25219      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25220      * @param {Roo.data.Store} store The data store to bind
25221      */
25222     bind : function(ds){
25223         ds.on("beforeload", this.beforeLoad, this);
25224         ds.on("load", this.onLoad, this);
25225         ds.on("loadexception", this.onLoadError, this);
25226         ds.on("remove", this.updateInfo, this);
25227         ds.on("add", this.updateInfo, this);
25228         this.ds = ds;
25229     }
25230 });/*
25231  * - LGPL
25232  *
25233  * element
25234  * 
25235  */
25236
25237 /**
25238  * @class Roo.bootstrap.MessageBar
25239  * @extends Roo.bootstrap.Component
25240  * Bootstrap MessageBar class
25241  * @cfg {String} html contents of the MessageBar
25242  * @cfg {String} weight (info | success | warning | danger) default info
25243  * @cfg {String} beforeClass insert the bar before the given class
25244  * @cfg {Boolean} closable (true | false) default false
25245  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25246  * 
25247  * @constructor
25248  * Create a new Element
25249  * @param {Object} config The config object
25250  */
25251
25252 Roo.bootstrap.MessageBar = function(config){
25253     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25254 };
25255
25256 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25257     
25258     html: '',
25259     weight: 'info',
25260     closable: false,
25261     fixed: false,
25262     beforeClass: 'bootstrap-sticky-wrap',
25263     
25264     getAutoCreate : function(){
25265         
25266         var cfg = {
25267             tag: 'div',
25268             cls: 'alert alert-dismissable alert-' + this.weight,
25269             cn: [
25270                 {
25271                     tag: 'span',
25272                     cls: 'message',
25273                     html: this.html || ''
25274                 }
25275             ]
25276         };
25277         
25278         if(this.fixed){
25279             cfg.cls += ' alert-messages-fixed';
25280         }
25281         
25282         if(this.closable){
25283             cfg.cn.push({
25284                 tag: 'button',
25285                 cls: 'close',
25286                 html: 'x'
25287             });
25288         }
25289         
25290         return cfg;
25291     },
25292     
25293     onRender : function(ct, position)
25294     {
25295         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25296         
25297         if(!this.el){
25298             var cfg = Roo.apply({},  this.getAutoCreate());
25299             cfg.id = Roo.id();
25300             
25301             if (this.cls) {
25302                 cfg.cls += ' ' + this.cls;
25303             }
25304             if (this.style) {
25305                 cfg.style = this.style;
25306             }
25307             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25308             
25309             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25310         }
25311         
25312         this.el.select('>button.close').on('click', this.hide, this);
25313         
25314     },
25315     
25316     show : function()
25317     {
25318         if (!this.rendered) {
25319             this.render();
25320         }
25321         
25322         this.el.show();
25323         
25324         this.fireEvent('show', this);
25325         
25326     },
25327     
25328     hide : function()
25329     {
25330         if (!this.rendered) {
25331             this.render();
25332         }
25333         
25334         this.el.hide();
25335         
25336         this.fireEvent('hide', this);
25337     },
25338     
25339     update : function()
25340     {
25341 //        var e = this.el.dom.firstChild;
25342 //        
25343 //        if(this.closable){
25344 //            e = e.nextSibling;
25345 //        }
25346 //        
25347 //        e.data = this.html || '';
25348
25349         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25350     }
25351    
25352 });
25353
25354  
25355
25356      /*
25357  * - LGPL
25358  *
25359  * Graph
25360  * 
25361  */
25362
25363
25364 /**
25365  * @class Roo.bootstrap.Graph
25366  * @extends Roo.bootstrap.Component
25367  * Bootstrap Graph class
25368 > Prameters
25369  -sm {number} sm 4
25370  -md {number} md 5
25371  @cfg {String} graphtype  bar | vbar | pie
25372  @cfg {number} g_x coodinator | centre x (pie)
25373  @cfg {number} g_y coodinator | centre y (pie)
25374  @cfg {number} g_r radius (pie)
25375  @cfg {number} g_height height of the chart (respected by all elements in the set)
25376  @cfg {number} g_width width of the chart (respected by all elements in the set)
25377  @cfg {Object} title The title of the chart
25378     
25379  -{Array}  values
25380  -opts (object) options for the chart 
25381      o {
25382      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25383      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25384      o vgutter (number)
25385      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.
25386      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25387      o to
25388      o stretch (boolean)
25389      o }
25390  -opts (object) options for the pie
25391      o{
25392      o cut
25393      o startAngle (number)
25394      o endAngle (number)
25395      } 
25396  *
25397  * @constructor
25398  * Create a new Input
25399  * @param {Object} config The config object
25400  */
25401
25402 Roo.bootstrap.Graph = function(config){
25403     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25404     
25405     this.addEvents({
25406         // img events
25407         /**
25408          * @event click
25409          * The img click event for the img.
25410          * @param {Roo.EventObject} e
25411          */
25412         "click" : true
25413     });
25414 };
25415
25416 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25417     
25418     sm: 4,
25419     md: 5,
25420     graphtype: 'bar',
25421     g_height: 250,
25422     g_width: 400,
25423     g_x: 50,
25424     g_y: 50,
25425     g_r: 30,
25426     opts:{
25427         //g_colors: this.colors,
25428         g_type: 'soft',
25429         g_gutter: '20%'
25430
25431     },
25432     title : false,
25433
25434     getAutoCreate : function(){
25435         
25436         var cfg = {
25437             tag: 'div',
25438             html : null
25439         };
25440         
25441         
25442         return  cfg;
25443     },
25444
25445     onRender : function(ct,position){
25446         
25447         
25448         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25449         
25450         if (typeof(Raphael) == 'undefined') {
25451             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25452             return;
25453         }
25454         
25455         this.raphael = Raphael(this.el.dom);
25456         
25457                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25458                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25459                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25460                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25461                 /*
25462                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25463                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25464                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25465                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25466                 
25467                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25468                 r.barchart(330, 10, 300, 220, data1);
25469                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25470                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25471                 */
25472                 
25473                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25474                 // r.barchart(30, 30, 560, 250,  xdata, {
25475                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25476                 //     axis : "0 0 1 1",
25477                 //     axisxlabels :  xdata
25478                 //     //yvalues : cols,
25479                    
25480                 // });
25481 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25482 //        
25483 //        this.load(null,xdata,{
25484 //                axis : "0 0 1 1",
25485 //                axisxlabels :  xdata
25486 //                });
25487
25488     },
25489
25490     load : function(graphtype,xdata,opts)
25491     {
25492         this.raphael.clear();
25493         if(!graphtype) {
25494             graphtype = this.graphtype;
25495         }
25496         if(!opts){
25497             opts = this.opts;
25498         }
25499         var r = this.raphael,
25500             fin = function () {
25501                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25502             },
25503             fout = function () {
25504                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25505             },
25506             pfin = function() {
25507                 this.sector.stop();
25508                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25509
25510                 if (this.label) {
25511                     this.label[0].stop();
25512                     this.label[0].attr({ r: 7.5 });
25513                     this.label[1].attr({ "font-weight": 800 });
25514                 }
25515             },
25516             pfout = function() {
25517                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25518
25519                 if (this.label) {
25520                     this.label[0].animate({ r: 5 }, 500, "bounce");
25521                     this.label[1].attr({ "font-weight": 400 });
25522                 }
25523             };
25524
25525         switch(graphtype){
25526             case 'bar':
25527                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25528                 break;
25529             case 'hbar':
25530                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25531                 break;
25532             case 'pie':
25533 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25534 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25535 //            
25536                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25537                 
25538                 break;
25539
25540         }
25541         
25542         if(this.title){
25543             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25544         }
25545         
25546     },
25547     
25548     setTitle: function(o)
25549     {
25550         this.title = o;
25551     },
25552     
25553     initEvents: function() {
25554         
25555         if(!this.href){
25556             this.el.on('click', this.onClick, this);
25557         }
25558     },
25559     
25560     onClick : function(e)
25561     {
25562         Roo.log('img onclick');
25563         this.fireEvent('click', this, e);
25564     }
25565    
25566 });
25567
25568  
25569 /*
25570  * - LGPL
25571  *
25572  * numberBox
25573  * 
25574  */
25575 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25576
25577 /**
25578  * @class Roo.bootstrap.dash.NumberBox
25579  * @extends Roo.bootstrap.Component
25580  * Bootstrap NumberBox class
25581  * @cfg {String} headline Box headline
25582  * @cfg {String} content Box content
25583  * @cfg {String} icon Box icon
25584  * @cfg {String} footer Footer text
25585  * @cfg {String} fhref Footer href
25586  * 
25587  * @constructor
25588  * Create a new NumberBox
25589  * @param {Object} config The config object
25590  */
25591
25592
25593 Roo.bootstrap.dash.NumberBox = function(config){
25594     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25595     
25596 };
25597
25598 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25599     
25600     headline : '',
25601     content : '',
25602     icon : '',
25603     footer : '',
25604     fhref : '',
25605     ficon : '',
25606     
25607     getAutoCreate : function(){
25608         
25609         var cfg = {
25610             tag : 'div',
25611             cls : 'small-box ',
25612             cn : [
25613                 {
25614                     tag : 'div',
25615                     cls : 'inner',
25616                     cn :[
25617                         {
25618                             tag : 'h3',
25619                             cls : 'roo-headline',
25620                             html : this.headline
25621                         },
25622                         {
25623                             tag : 'p',
25624                             cls : 'roo-content',
25625                             html : this.content
25626                         }
25627                     ]
25628                 }
25629             ]
25630         };
25631         
25632         if(this.icon){
25633             cfg.cn.push({
25634                 tag : 'div',
25635                 cls : 'icon',
25636                 cn :[
25637                     {
25638                         tag : 'i',
25639                         cls : 'ion ' + this.icon
25640                     }
25641                 ]
25642             });
25643         }
25644         
25645         if(this.footer){
25646             var footer = {
25647                 tag : 'a',
25648                 cls : 'small-box-footer',
25649                 href : this.fhref || '#',
25650                 html : this.footer
25651             };
25652             
25653             cfg.cn.push(footer);
25654             
25655         }
25656         
25657         return  cfg;
25658     },
25659
25660     onRender : function(ct,position){
25661         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25662
25663
25664        
25665                 
25666     },
25667
25668     setHeadline: function (value)
25669     {
25670         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25671     },
25672     
25673     setFooter: function (value, href)
25674     {
25675         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25676         
25677         if(href){
25678             this.el.select('a.small-box-footer',true).first().attr('href', href);
25679         }
25680         
25681     },
25682
25683     setContent: function (value)
25684     {
25685         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25686     },
25687
25688     initEvents: function() 
25689     {   
25690         
25691     }
25692     
25693 });
25694
25695  
25696 /*
25697  * - LGPL
25698  *
25699  * TabBox
25700  * 
25701  */
25702 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25703
25704 /**
25705  * @class Roo.bootstrap.dash.TabBox
25706  * @extends Roo.bootstrap.Component
25707  * Bootstrap TabBox class
25708  * @cfg {String} title Title of the TabBox
25709  * @cfg {String} icon Icon of the TabBox
25710  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25711  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25712  * 
25713  * @constructor
25714  * Create a new TabBox
25715  * @param {Object} config The config object
25716  */
25717
25718
25719 Roo.bootstrap.dash.TabBox = function(config){
25720     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25721     this.addEvents({
25722         // raw events
25723         /**
25724          * @event addpane
25725          * When a pane is added
25726          * @param {Roo.bootstrap.dash.TabPane} pane
25727          */
25728         "addpane" : true,
25729         /**
25730          * @event activatepane
25731          * When a pane is activated
25732          * @param {Roo.bootstrap.dash.TabPane} pane
25733          */
25734         "activatepane" : true
25735         
25736          
25737     });
25738     
25739     this.panes = [];
25740 };
25741
25742 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25743
25744     title : '',
25745     icon : false,
25746     showtabs : true,
25747     tabScrollable : false,
25748     
25749     getChildContainer : function()
25750     {
25751         return this.el.select('.tab-content', true).first();
25752     },
25753     
25754     getAutoCreate : function(){
25755         
25756         var header = {
25757             tag: 'li',
25758             cls: 'pull-left header',
25759             html: this.title,
25760             cn : []
25761         };
25762         
25763         if(this.icon){
25764             header.cn.push({
25765                 tag: 'i',
25766                 cls: 'fa ' + this.icon
25767             });
25768         }
25769         
25770         var h = {
25771             tag: 'ul',
25772             cls: 'nav nav-tabs pull-right',
25773             cn: [
25774                 header
25775             ]
25776         };
25777         
25778         if(this.tabScrollable){
25779             h = {
25780                 tag: 'div',
25781                 cls: 'tab-header',
25782                 cn: [
25783                     {
25784                         tag: 'ul',
25785                         cls: 'nav nav-tabs pull-right',
25786                         cn: [
25787                             header
25788                         ]
25789                     }
25790                 ]
25791             };
25792         }
25793         
25794         var cfg = {
25795             tag: 'div',
25796             cls: 'nav-tabs-custom',
25797             cn: [
25798                 h,
25799                 {
25800                     tag: 'div',
25801                     cls: 'tab-content no-padding',
25802                     cn: []
25803                 }
25804             ]
25805         };
25806
25807         return  cfg;
25808     },
25809     initEvents : function()
25810     {
25811         //Roo.log('add add pane handler');
25812         this.on('addpane', this.onAddPane, this);
25813     },
25814      /**
25815      * Updates the box title
25816      * @param {String} html to set the title to.
25817      */
25818     setTitle : function(value)
25819     {
25820         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25821     },
25822     onAddPane : function(pane)
25823     {
25824         this.panes.push(pane);
25825         //Roo.log('addpane');
25826         //Roo.log(pane);
25827         // tabs are rendere left to right..
25828         if(!this.showtabs){
25829             return;
25830         }
25831         
25832         var ctr = this.el.select('.nav-tabs', true).first();
25833          
25834          
25835         var existing = ctr.select('.nav-tab',true);
25836         var qty = existing.getCount();;
25837         
25838         
25839         var tab = ctr.createChild({
25840             tag : 'li',
25841             cls : 'nav-tab' + (qty ? '' : ' active'),
25842             cn : [
25843                 {
25844                     tag : 'a',
25845                     href:'#',
25846                     html : pane.title
25847                 }
25848             ]
25849         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25850         pane.tab = tab;
25851         
25852         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25853         if (!qty) {
25854             pane.el.addClass('active');
25855         }
25856         
25857                 
25858     },
25859     onTabClick : function(ev,un,ob,pane)
25860     {
25861         //Roo.log('tab - prev default');
25862         ev.preventDefault();
25863         
25864         
25865         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25866         pane.tab.addClass('active');
25867         //Roo.log(pane.title);
25868         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25869         // technically we should have a deactivate event.. but maybe add later.
25870         // and it should not de-activate the selected tab...
25871         this.fireEvent('activatepane', pane);
25872         pane.el.addClass('active');
25873         pane.fireEvent('activate');
25874         
25875         
25876     },
25877     
25878     getActivePane : function()
25879     {
25880         var r = false;
25881         Roo.each(this.panes, function(p) {
25882             if(p.el.hasClass('active')){
25883                 r = p;
25884                 return false;
25885             }
25886             
25887             return;
25888         });
25889         
25890         return r;
25891     }
25892     
25893     
25894 });
25895
25896  
25897 /*
25898  * - LGPL
25899  *
25900  * Tab pane
25901  * 
25902  */
25903 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25904 /**
25905  * @class Roo.bootstrap.TabPane
25906  * @extends Roo.bootstrap.Component
25907  * Bootstrap TabPane class
25908  * @cfg {Boolean} active (false | true) Default false
25909  * @cfg {String} title title of panel
25910
25911  * 
25912  * @constructor
25913  * Create a new TabPane
25914  * @param {Object} config The config object
25915  */
25916
25917 Roo.bootstrap.dash.TabPane = function(config){
25918     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25919     
25920     this.addEvents({
25921         // raw events
25922         /**
25923          * @event activate
25924          * When a pane is activated
25925          * @param {Roo.bootstrap.dash.TabPane} pane
25926          */
25927         "activate" : true
25928          
25929     });
25930 };
25931
25932 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25933     
25934     active : false,
25935     title : '',
25936     
25937     // the tabBox that this is attached to.
25938     tab : false,
25939      
25940     getAutoCreate : function() 
25941     {
25942         var cfg = {
25943             tag: 'div',
25944             cls: 'tab-pane'
25945         };
25946         
25947         if(this.active){
25948             cfg.cls += ' active';
25949         }
25950         
25951         return cfg;
25952     },
25953     initEvents  : function()
25954     {
25955         //Roo.log('trigger add pane handler');
25956         this.parent().fireEvent('addpane', this)
25957     },
25958     
25959      /**
25960      * Updates the tab title 
25961      * @param {String} html to set the title to.
25962      */
25963     setTitle: function(str)
25964     {
25965         if (!this.tab) {
25966             return;
25967         }
25968         this.title = str;
25969         this.tab.select('a', true).first().dom.innerHTML = str;
25970         
25971     }
25972     
25973     
25974     
25975 });
25976
25977  
25978
25979
25980  /*
25981  * - LGPL
25982  *
25983  * menu
25984  * 
25985  */
25986 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25987
25988 /**
25989  * @class Roo.bootstrap.menu.Menu
25990  * @extends Roo.bootstrap.Component
25991  * Bootstrap Menu class - container for Menu
25992  * @cfg {String} html Text of the menu
25993  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25994  * @cfg {String} icon Font awesome icon
25995  * @cfg {String} pos Menu align to (top | bottom) default bottom
25996  * 
25997  * 
25998  * @constructor
25999  * Create a new Menu
26000  * @param {Object} config The config object
26001  */
26002
26003
26004 Roo.bootstrap.menu.Menu = function(config){
26005     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26006     
26007     this.addEvents({
26008         /**
26009          * @event beforeshow
26010          * Fires before this menu is displayed
26011          * @param {Roo.bootstrap.menu.Menu} this
26012          */
26013         beforeshow : true,
26014         /**
26015          * @event beforehide
26016          * Fires before this menu is hidden
26017          * @param {Roo.bootstrap.menu.Menu} this
26018          */
26019         beforehide : true,
26020         /**
26021          * @event show
26022          * Fires after this menu is displayed
26023          * @param {Roo.bootstrap.menu.Menu} this
26024          */
26025         show : true,
26026         /**
26027          * @event hide
26028          * Fires after this menu is hidden
26029          * @param {Roo.bootstrap.menu.Menu} this
26030          */
26031         hide : true,
26032         /**
26033          * @event click
26034          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26035          * @param {Roo.bootstrap.menu.Menu} this
26036          * @param {Roo.EventObject} e
26037          */
26038         click : true
26039     });
26040     
26041 };
26042
26043 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
26044     
26045     submenu : false,
26046     html : '',
26047     weight : 'default',
26048     icon : false,
26049     pos : 'bottom',
26050     
26051     
26052     getChildContainer : function() {
26053         if(this.isSubMenu){
26054             return this.el;
26055         }
26056         
26057         return this.el.select('ul.dropdown-menu', true).first();  
26058     },
26059     
26060     getAutoCreate : function()
26061     {
26062         var text = [
26063             {
26064                 tag : 'span',
26065                 cls : 'roo-menu-text',
26066                 html : this.html
26067             }
26068         ];
26069         
26070         if(this.icon){
26071             text.unshift({
26072                 tag : 'i',
26073                 cls : 'fa ' + this.icon
26074             })
26075         }
26076         
26077         
26078         var cfg = {
26079             tag : 'div',
26080             cls : 'btn-group',
26081             cn : [
26082                 {
26083                     tag : 'button',
26084                     cls : 'dropdown-button btn btn-' + this.weight,
26085                     cn : text
26086                 },
26087                 {
26088                     tag : 'button',
26089                     cls : 'dropdown-toggle btn btn-' + this.weight,
26090                     cn : [
26091                         {
26092                             tag : 'span',
26093                             cls : 'caret'
26094                         }
26095                     ]
26096                 },
26097                 {
26098                     tag : 'ul',
26099                     cls : 'dropdown-menu'
26100                 }
26101             ]
26102             
26103         };
26104         
26105         if(this.pos == 'top'){
26106             cfg.cls += ' dropup';
26107         }
26108         
26109         if(this.isSubMenu){
26110             cfg = {
26111                 tag : 'ul',
26112                 cls : 'dropdown-menu'
26113             }
26114         }
26115         
26116         return cfg;
26117     },
26118     
26119     onRender : function(ct, position)
26120     {
26121         this.isSubMenu = ct.hasClass('dropdown-submenu');
26122         
26123         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26124     },
26125     
26126     initEvents : function() 
26127     {
26128         if(this.isSubMenu){
26129             return;
26130         }
26131         
26132         this.hidden = true;
26133         
26134         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26135         this.triggerEl.on('click', this.onTriggerPress, this);
26136         
26137         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26138         this.buttonEl.on('click', this.onClick, this);
26139         
26140     },
26141     
26142     list : function()
26143     {
26144         if(this.isSubMenu){
26145             return this.el;
26146         }
26147         
26148         return this.el.select('ul.dropdown-menu', true).first();
26149     },
26150     
26151     onClick : function(e)
26152     {
26153         this.fireEvent("click", this, e);
26154     },
26155     
26156     onTriggerPress  : function(e)
26157     {   
26158         if (this.isVisible()) {
26159             this.hide();
26160         } else {
26161             this.show();
26162         }
26163     },
26164     
26165     isVisible : function(){
26166         return !this.hidden;
26167     },
26168     
26169     show : function()
26170     {
26171         this.fireEvent("beforeshow", this);
26172         
26173         this.hidden = false;
26174         this.el.addClass('open');
26175         
26176         Roo.get(document).on("mouseup", this.onMouseUp, this);
26177         
26178         this.fireEvent("show", this);
26179         
26180         
26181     },
26182     
26183     hide : function()
26184     {
26185         this.fireEvent("beforehide", this);
26186         
26187         this.hidden = true;
26188         this.el.removeClass('open');
26189         
26190         Roo.get(document).un("mouseup", this.onMouseUp);
26191         
26192         this.fireEvent("hide", this);
26193     },
26194     
26195     onMouseUp : function()
26196     {
26197         this.hide();
26198     }
26199     
26200 });
26201
26202  
26203  /*
26204  * - LGPL
26205  *
26206  * menu item
26207  * 
26208  */
26209 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26210
26211 /**
26212  * @class Roo.bootstrap.menu.Item
26213  * @extends Roo.bootstrap.Component
26214  * Bootstrap MenuItem class
26215  * @cfg {Boolean} submenu (true | false) default false
26216  * @cfg {String} html text of the item
26217  * @cfg {String} href the link
26218  * @cfg {Boolean} disable (true | false) default false
26219  * @cfg {Boolean} preventDefault (true | false) default true
26220  * @cfg {String} icon Font awesome icon
26221  * @cfg {String} pos Submenu align to (left | right) default right 
26222  * 
26223  * 
26224  * @constructor
26225  * Create a new Item
26226  * @param {Object} config The config object
26227  */
26228
26229
26230 Roo.bootstrap.menu.Item = function(config){
26231     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26232     this.addEvents({
26233         /**
26234          * @event mouseover
26235          * Fires when the mouse is hovering over this menu
26236          * @param {Roo.bootstrap.menu.Item} this
26237          * @param {Roo.EventObject} e
26238          */
26239         mouseover : true,
26240         /**
26241          * @event mouseout
26242          * Fires when the mouse exits this menu
26243          * @param {Roo.bootstrap.menu.Item} this
26244          * @param {Roo.EventObject} e
26245          */
26246         mouseout : true,
26247         // raw events
26248         /**
26249          * @event click
26250          * The raw click event for the entire grid.
26251          * @param {Roo.EventObject} e
26252          */
26253         click : true
26254     });
26255 };
26256
26257 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26258     
26259     submenu : false,
26260     href : '',
26261     html : '',
26262     preventDefault: true,
26263     disable : false,
26264     icon : false,
26265     pos : 'right',
26266     
26267     getAutoCreate : function()
26268     {
26269         var text = [
26270             {
26271                 tag : 'span',
26272                 cls : 'roo-menu-item-text',
26273                 html : this.html
26274             }
26275         ];
26276         
26277         if(this.icon){
26278             text.unshift({
26279                 tag : 'i',
26280                 cls : 'fa ' + this.icon
26281             })
26282         }
26283         
26284         var cfg = {
26285             tag : 'li',
26286             cn : [
26287                 {
26288                     tag : 'a',
26289                     href : this.href || '#',
26290                     cn : text
26291                 }
26292             ]
26293         };
26294         
26295         if(this.disable){
26296             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26297         }
26298         
26299         if(this.submenu){
26300             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26301             
26302             if(this.pos == 'left'){
26303                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26304             }
26305         }
26306         
26307         return cfg;
26308     },
26309     
26310     initEvents : function() 
26311     {
26312         this.el.on('mouseover', this.onMouseOver, this);
26313         this.el.on('mouseout', this.onMouseOut, this);
26314         
26315         this.el.select('a', true).first().on('click', this.onClick, this);
26316         
26317     },
26318     
26319     onClick : function(e)
26320     {
26321         if(this.preventDefault){
26322             e.preventDefault();
26323         }
26324         
26325         this.fireEvent("click", this, e);
26326     },
26327     
26328     onMouseOver : function(e)
26329     {
26330         if(this.submenu && this.pos == 'left'){
26331             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26332         }
26333         
26334         this.fireEvent("mouseover", this, e);
26335     },
26336     
26337     onMouseOut : function(e)
26338     {
26339         this.fireEvent("mouseout", this, e);
26340     }
26341 });
26342
26343  
26344
26345  /*
26346  * - LGPL
26347  *
26348  * menu separator
26349  * 
26350  */
26351 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26352
26353 /**
26354  * @class Roo.bootstrap.menu.Separator
26355  * @extends Roo.bootstrap.Component
26356  * Bootstrap Separator class
26357  * 
26358  * @constructor
26359  * Create a new Separator
26360  * @param {Object} config The config object
26361  */
26362
26363
26364 Roo.bootstrap.menu.Separator = function(config){
26365     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26366 };
26367
26368 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26369     
26370     getAutoCreate : function(){
26371         var cfg = {
26372             tag : 'li',
26373             cls: 'divider'
26374         };
26375         
26376         return cfg;
26377     }
26378    
26379 });
26380
26381  
26382
26383  /*
26384  * - LGPL
26385  *
26386  * Tooltip
26387  * 
26388  */
26389
26390 /**
26391  * @class Roo.bootstrap.Tooltip
26392  * Bootstrap Tooltip class
26393  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26394  * to determine which dom element triggers the tooltip.
26395  * 
26396  * It needs to add support for additional attributes like tooltip-position
26397  * 
26398  * @constructor
26399  * Create a new Toolti
26400  * @param {Object} config The config object
26401  */
26402
26403 Roo.bootstrap.Tooltip = function(config){
26404     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26405     
26406     this.alignment = Roo.bootstrap.Tooltip.alignment;
26407     
26408     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26409         this.alignment = config.alignment;
26410     }
26411     
26412 };
26413
26414 Roo.apply(Roo.bootstrap.Tooltip, {
26415     /**
26416      * @function init initialize tooltip monitoring.
26417      * @static
26418      */
26419     currentEl : false,
26420     currentTip : false,
26421     currentRegion : false,
26422     
26423     //  init : delay?
26424     
26425     init : function()
26426     {
26427         Roo.get(document).on('mouseover', this.enter ,this);
26428         Roo.get(document).on('mouseout', this.leave, this);
26429          
26430         
26431         this.currentTip = new Roo.bootstrap.Tooltip();
26432     },
26433     
26434     enter : function(ev)
26435     {
26436         var dom = ev.getTarget();
26437         
26438         //Roo.log(['enter',dom]);
26439         var el = Roo.fly(dom);
26440         if (this.currentEl) {
26441             //Roo.log(dom);
26442             //Roo.log(this.currentEl);
26443             //Roo.log(this.currentEl.contains(dom));
26444             if (this.currentEl == el) {
26445                 return;
26446             }
26447             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26448                 return;
26449             }
26450
26451         }
26452         
26453         if (this.currentTip.el) {
26454             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26455         }    
26456         //Roo.log(ev);
26457         
26458         if(!el || el.dom == document){
26459             return;
26460         }
26461         
26462         var bindEl = el;
26463         
26464         // you can not look for children, as if el is the body.. then everythign is the child..
26465         if (!el.attr('tooltip')) { //
26466             if (!el.select("[tooltip]").elements.length) {
26467                 return;
26468             }
26469             // is the mouse over this child...?
26470             bindEl = el.select("[tooltip]").first();
26471             var xy = ev.getXY();
26472             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26473                 //Roo.log("not in region.");
26474                 return;
26475             }
26476             //Roo.log("child element over..");
26477             
26478         }
26479         this.currentEl = bindEl;
26480         this.currentTip.bind(bindEl);
26481         this.currentRegion = Roo.lib.Region.getRegion(dom);
26482         this.currentTip.enter();
26483         
26484     },
26485     leave : function(ev)
26486     {
26487         var dom = ev.getTarget();
26488         //Roo.log(['leave',dom]);
26489         if (!this.currentEl) {
26490             return;
26491         }
26492         
26493         
26494         if (dom != this.currentEl.dom) {
26495             return;
26496         }
26497         var xy = ev.getXY();
26498         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26499             return;
26500         }
26501         // only activate leave if mouse cursor is outside... bounding box..
26502         
26503         
26504         
26505         
26506         if (this.currentTip) {
26507             this.currentTip.leave();
26508         }
26509         //Roo.log('clear currentEl');
26510         this.currentEl = false;
26511         
26512         
26513     },
26514     alignment : {
26515         'left' : ['r-l', [-2,0], 'right'],
26516         'right' : ['l-r', [2,0], 'left'],
26517         'bottom' : ['t-b', [0,2], 'top'],
26518         'top' : [ 'b-t', [0,-2], 'bottom']
26519     }
26520     
26521 });
26522
26523
26524 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26525     
26526     
26527     bindEl : false,
26528     
26529     delay : null, // can be { show : 300 , hide: 500}
26530     
26531     timeout : null,
26532     
26533     hoverState : null, //???
26534     
26535     placement : 'bottom', 
26536     
26537     alignment : false,
26538     
26539     getAutoCreate : function(){
26540     
26541         var cfg = {
26542            cls : 'tooltip',
26543            role : 'tooltip',
26544            cn : [
26545                 {
26546                     cls : 'tooltip-arrow'
26547                 },
26548                 {
26549                     cls : 'tooltip-inner'
26550                 }
26551            ]
26552         };
26553         
26554         return cfg;
26555     },
26556     bind : function(el)
26557     {
26558         this.bindEl = el;
26559     },
26560       
26561     
26562     enter : function () {
26563        
26564         if (this.timeout != null) {
26565             clearTimeout(this.timeout);
26566         }
26567         
26568         this.hoverState = 'in';
26569          //Roo.log("enter - show");
26570         if (!this.delay || !this.delay.show) {
26571             this.show();
26572             return;
26573         }
26574         var _t = this;
26575         this.timeout = setTimeout(function () {
26576             if (_t.hoverState == 'in') {
26577                 _t.show();
26578             }
26579         }, this.delay.show);
26580     },
26581     leave : function()
26582     {
26583         clearTimeout(this.timeout);
26584     
26585         this.hoverState = 'out';
26586          if (!this.delay || !this.delay.hide) {
26587             this.hide();
26588             return;
26589         }
26590        
26591         var _t = this;
26592         this.timeout = setTimeout(function () {
26593             //Roo.log("leave - timeout");
26594             
26595             if (_t.hoverState == 'out') {
26596                 _t.hide();
26597                 Roo.bootstrap.Tooltip.currentEl = false;
26598             }
26599         }, delay);
26600     },
26601     
26602     show : function (msg)
26603     {
26604         if (!this.el) {
26605             this.render(document.body);
26606         }
26607         // set content.
26608         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26609         
26610         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26611         
26612         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26613         
26614         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26615         
26616         var placement = typeof this.placement == 'function' ?
26617             this.placement.call(this, this.el, on_el) :
26618             this.placement;
26619             
26620         var autoToken = /\s?auto?\s?/i;
26621         var autoPlace = autoToken.test(placement);
26622         if (autoPlace) {
26623             placement = placement.replace(autoToken, '') || 'top';
26624         }
26625         
26626         //this.el.detach()
26627         //this.el.setXY([0,0]);
26628         this.el.show();
26629         //this.el.dom.style.display='block';
26630         
26631         //this.el.appendTo(on_el);
26632         
26633         var p = this.getPosition();
26634         var box = this.el.getBox();
26635         
26636         if (autoPlace) {
26637             // fixme..
26638         }
26639         
26640         var align = this.alignment[placement];
26641         
26642         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26643         
26644         if(placement == 'top' || placement == 'bottom'){
26645             if(xy[0] < 0){
26646                 placement = 'right';
26647             }
26648             
26649             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26650                 placement = 'left';
26651             }
26652             
26653             var scroll = Roo.select('body', true).first().getScroll();
26654             
26655             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26656                 placement = 'top';
26657             }
26658             
26659             align = this.alignment[placement];
26660         }
26661         
26662         this.el.alignTo(this.bindEl, align[0],align[1]);
26663         //var arrow = this.el.select('.arrow',true).first();
26664         //arrow.set(align[2], 
26665         
26666         this.el.addClass(placement);
26667         
26668         this.el.addClass('in fade');
26669         
26670         this.hoverState = null;
26671         
26672         if (this.el.hasClass('fade')) {
26673             // fade it?
26674         }
26675         
26676     },
26677     hide : function()
26678     {
26679          
26680         if (!this.el) {
26681             return;
26682         }
26683         //this.el.setXY([0,0]);
26684         this.el.removeClass('in');
26685         //this.el.hide();
26686         
26687     }
26688     
26689 });
26690  
26691
26692  /*
26693  * - LGPL
26694  *
26695  * Location Picker
26696  * 
26697  */
26698
26699 /**
26700  * @class Roo.bootstrap.LocationPicker
26701  * @extends Roo.bootstrap.Component
26702  * Bootstrap LocationPicker class
26703  * @cfg {Number} latitude Position when init default 0
26704  * @cfg {Number} longitude Position when init default 0
26705  * @cfg {Number} zoom default 15
26706  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26707  * @cfg {Boolean} mapTypeControl default false
26708  * @cfg {Boolean} disableDoubleClickZoom default false
26709  * @cfg {Boolean} scrollwheel default true
26710  * @cfg {Boolean} streetViewControl default false
26711  * @cfg {Number} radius default 0
26712  * @cfg {String} locationName
26713  * @cfg {Boolean} draggable default true
26714  * @cfg {Boolean} enableAutocomplete default false
26715  * @cfg {Boolean} enableReverseGeocode default true
26716  * @cfg {String} markerTitle
26717  * 
26718  * @constructor
26719  * Create a new LocationPicker
26720  * @param {Object} config The config object
26721  */
26722
26723
26724 Roo.bootstrap.LocationPicker = function(config){
26725     
26726     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26727     
26728     this.addEvents({
26729         /**
26730          * @event initial
26731          * Fires when the picker initialized.
26732          * @param {Roo.bootstrap.LocationPicker} this
26733          * @param {Google Location} location
26734          */
26735         initial : true,
26736         /**
26737          * @event positionchanged
26738          * Fires when the picker position changed.
26739          * @param {Roo.bootstrap.LocationPicker} this
26740          * @param {Google Location} location
26741          */
26742         positionchanged : true,
26743         /**
26744          * @event resize
26745          * Fires when the map resize.
26746          * @param {Roo.bootstrap.LocationPicker} this
26747          */
26748         resize : true,
26749         /**
26750          * @event show
26751          * Fires when the map show.
26752          * @param {Roo.bootstrap.LocationPicker} this
26753          */
26754         show : true,
26755         /**
26756          * @event hide
26757          * Fires when the map hide.
26758          * @param {Roo.bootstrap.LocationPicker} this
26759          */
26760         hide : true,
26761         /**
26762          * @event mapClick
26763          * Fires when click the map.
26764          * @param {Roo.bootstrap.LocationPicker} this
26765          * @param {Map event} e
26766          */
26767         mapClick : true,
26768         /**
26769          * @event mapRightClick
26770          * Fires when right click the map.
26771          * @param {Roo.bootstrap.LocationPicker} this
26772          * @param {Map event} e
26773          */
26774         mapRightClick : true,
26775         /**
26776          * @event markerClick
26777          * Fires when click the marker.
26778          * @param {Roo.bootstrap.LocationPicker} this
26779          * @param {Map event} e
26780          */
26781         markerClick : true,
26782         /**
26783          * @event markerRightClick
26784          * Fires when right click the marker.
26785          * @param {Roo.bootstrap.LocationPicker} this
26786          * @param {Map event} e
26787          */
26788         markerRightClick : true,
26789         /**
26790          * @event OverlayViewDraw
26791          * Fires when OverlayView Draw
26792          * @param {Roo.bootstrap.LocationPicker} this
26793          */
26794         OverlayViewDraw : true,
26795         /**
26796          * @event OverlayViewOnAdd
26797          * Fires when OverlayView Draw
26798          * @param {Roo.bootstrap.LocationPicker} this
26799          */
26800         OverlayViewOnAdd : true,
26801         /**
26802          * @event OverlayViewOnRemove
26803          * Fires when OverlayView Draw
26804          * @param {Roo.bootstrap.LocationPicker} this
26805          */
26806         OverlayViewOnRemove : true,
26807         /**
26808          * @event OverlayViewShow
26809          * Fires when OverlayView Draw
26810          * @param {Roo.bootstrap.LocationPicker} this
26811          * @param {Pixel} cpx
26812          */
26813         OverlayViewShow : true,
26814         /**
26815          * @event OverlayViewHide
26816          * Fires when OverlayView Draw
26817          * @param {Roo.bootstrap.LocationPicker} this
26818          */
26819         OverlayViewHide : true,
26820         /**
26821          * @event loadexception
26822          * Fires when load google lib failed.
26823          * @param {Roo.bootstrap.LocationPicker} this
26824          */
26825         loadexception : true
26826     });
26827         
26828 };
26829
26830 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26831     
26832     gMapContext: false,
26833     
26834     latitude: 0,
26835     longitude: 0,
26836     zoom: 15,
26837     mapTypeId: false,
26838     mapTypeControl: false,
26839     disableDoubleClickZoom: false,
26840     scrollwheel: true,
26841     streetViewControl: false,
26842     radius: 0,
26843     locationName: '',
26844     draggable: true,
26845     enableAutocomplete: false,
26846     enableReverseGeocode: true,
26847     markerTitle: '',
26848     
26849     getAutoCreate: function()
26850     {
26851
26852         var cfg = {
26853             tag: 'div',
26854             cls: 'roo-location-picker'
26855         };
26856         
26857         return cfg
26858     },
26859     
26860     initEvents: function(ct, position)
26861     {       
26862         if(!this.el.getWidth() || this.isApplied()){
26863             return;
26864         }
26865         
26866         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26867         
26868         this.initial();
26869     },
26870     
26871     initial: function()
26872     {
26873         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26874             this.fireEvent('loadexception', this);
26875             return;
26876         }
26877         
26878         if(!this.mapTypeId){
26879             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26880         }
26881         
26882         this.gMapContext = this.GMapContext();
26883         
26884         this.initOverlayView();
26885         
26886         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26887         
26888         var _this = this;
26889                 
26890         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26891             _this.setPosition(_this.gMapContext.marker.position);
26892         });
26893         
26894         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26895             _this.fireEvent('mapClick', this, event);
26896             
26897         });
26898
26899         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26900             _this.fireEvent('mapRightClick', this, event);
26901             
26902         });
26903         
26904         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26905             _this.fireEvent('markerClick', this, event);
26906             
26907         });
26908
26909         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26910             _this.fireEvent('markerRightClick', this, event);
26911             
26912         });
26913         
26914         this.setPosition(this.gMapContext.location);
26915         
26916         this.fireEvent('initial', this, this.gMapContext.location);
26917     },
26918     
26919     initOverlayView: function()
26920     {
26921         var _this = this;
26922         
26923         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26924             
26925             draw: function()
26926             {
26927                 _this.fireEvent('OverlayViewDraw', _this);
26928             },
26929             
26930             onAdd: function()
26931             {
26932                 _this.fireEvent('OverlayViewOnAdd', _this);
26933             },
26934             
26935             onRemove: function()
26936             {
26937                 _this.fireEvent('OverlayViewOnRemove', _this);
26938             },
26939             
26940             show: function(cpx)
26941             {
26942                 _this.fireEvent('OverlayViewShow', _this, cpx);
26943             },
26944             
26945             hide: function()
26946             {
26947                 _this.fireEvent('OverlayViewHide', _this);
26948             }
26949             
26950         });
26951     },
26952     
26953     fromLatLngToContainerPixel: function(event)
26954     {
26955         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26956     },
26957     
26958     isApplied: function() 
26959     {
26960         return this.getGmapContext() == false ? false : true;
26961     },
26962     
26963     getGmapContext: function() 
26964     {
26965         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26966     },
26967     
26968     GMapContext: function() 
26969     {
26970         var position = new google.maps.LatLng(this.latitude, this.longitude);
26971         
26972         var _map = new google.maps.Map(this.el.dom, {
26973             center: position,
26974             zoom: this.zoom,
26975             mapTypeId: this.mapTypeId,
26976             mapTypeControl: this.mapTypeControl,
26977             disableDoubleClickZoom: this.disableDoubleClickZoom,
26978             scrollwheel: this.scrollwheel,
26979             streetViewControl: this.streetViewControl,
26980             locationName: this.locationName,
26981             draggable: this.draggable,
26982             enableAutocomplete: this.enableAutocomplete,
26983             enableReverseGeocode: this.enableReverseGeocode
26984         });
26985         
26986         var _marker = new google.maps.Marker({
26987             position: position,
26988             map: _map,
26989             title: this.markerTitle,
26990             draggable: this.draggable
26991         });
26992         
26993         return {
26994             map: _map,
26995             marker: _marker,
26996             circle: null,
26997             location: position,
26998             radius: this.radius,
26999             locationName: this.locationName,
27000             addressComponents: {
27001                 formatted_address: null,
27002                 addressLine1: null,
27003                 addressLine2: null,
27004                 streetName: null,
27005                 streetNumber: null,
27006                 city: null,
27007                 district: null,
27008                 state: null,
27009                 stateOrProvince: null
27010             },
27011             settings: this,
27012             domContainer: this.el.dom,
27013             geodecoder: new google.maps.Geocoder()
27014         };
27015     },
27016     
27017     drawCircle: function(center, radius, options) 
27018     {
27019         if (this.gMapContext.circle != null) {
27020             this.gMapContext.circle.setMap(null);
27021         }
27022         if (radius > 0) {
27023             radius *= 1;
27024             options = Roo.apply({}, options, {
27025                 strokeColor: "#0000FF",
27026                 strokeOpacity: .35,
27027                 strokeWeight: 2,
27028                 fillColor: "#0000FF",
27029                 fillOpacity: .2
27030             });
27031             
27032             options.map = this.gMapContext.map;
27033             options.radius = radius;
27034             options.center = center;
27035             this.gMapContext.circle = new google.maps.Circle(options);
27036             return this.gMapContext.circle;
27037         }
27038         
27039         return null;
27040     },
27041     
27042     setPosition: function(location) 
27043     {
27044         this.gMapContext.location = location;
27045         this.gMapContext.marker.setPosition(location);
27046         this.gMapContext.map.panTo(location);
27047         this.drawCircle(location, this.gMapContext.radius, {});
27048         
27049         var _this = this;
27050         
27051         if (this.gMapContext.settings.enableReverseGeocode) {
27052             this.gMapContext.geodecoder.geocode({
27053                 latLng: this.gMapContext.location
27054             }, function(results, status) {
27055                 
27056                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27057                     _this.gMapContext.locationName = results[0].formatted_address;
27058                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27059                     
27060                     _this.fireEvent('positionchanged', this, location);
27061                 }
27062             });
27063             
27064             return;
27065         }
27066         
27067         this.fireEvent('positionchanged', this, location);
27068     },
27069     
27070     resize: function()
27071     {
27072         google.maps.event.trigger(this.gMapContext.map, "resize");
27073         
27074         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27075         
27076         this.fireEvent('resize', this);
27077     },
27078     
27079     setPositionByLatLng: function(latitude, longitude)
27080     {
27081         this.setPosition(new google.maps.LatLng(latitude, longitude));
27082     },
27083     
27084     getCurrentPosition: function() 
27085     {
27086         return {
27087             latitude: this.gMapContext.location.lat(),
27088             longitude: this.gMapContext.location.lng()
27089         };
27090     },
27091     
27092     getAddressName: function() 
27093     {
27094         return this.gMapContext.locationName;
27095     },
27096     
27097     getAddressComponents: function() 
27098     {
27099         return this.gMapContext.addressComponents;
27100     },
27101     
27102     address_component_from_google_geocode: function(address_components) 
27103     {
27104         var result = {};
27105         
27106         for (var i = 0; i < address_components.length; i++) {
27107             var component = address_components[i];
27108             if (component.types.indexOf("postal_code") >= 0) {
27109                 result.postalCode = component.short_name;
27110             } else if (component.types.indexOf("street_number") >= 0) {
27111                 result.streetNumber = component.short_name;
27112             } else if (component.types.indexOf("route") >= 0) {
27113                 result.streetName = component.short_name;
27114             } else if (component.types.indexOf("neighborhood") >= 0) {
27115                 result.city = component.short_name;
27116             } else if (component.types.indexOf("locality") >= 0) {
27117                 result.city = component.short_name;
27118             } else if (component.types.indexOf("sublocality") >= 0) {
27119                 result.district = component.short_name;
27120             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27121                 result.stateOrProvince = component.short_name;
27122             } else if (component.types.indexOf("country") >= 0) {
27123                 result.country = component.short_name;
27124             }
27125         }
27126         
27127         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27128         result.addressLine2 = "";
27129         return result;
27130     },
27131     
27132     setZoomLevel: function(zoom)
27133     {
27134         this.gMapContext.map.setZoom(zoom);
27135     },
27136     
27137     show: function()
27138     {
27139         if(!this.el){
27140             return;
27141         }
27142         
27143         this.el.show();
27144         
27145         this.resize();
27146         
27147         this.fireEvent('show', this);
27148     },
27149     
27150     hide: function()
27151     {
27152         if(!this.el){
27153             return;
27154         }
27155         
27156         this.el.hide();
27157         
27158         this.fireEvent('hide', this);
27159     }
27160     
27161 });
27162
27163 Roo.apply(Roo.bootstrap.LocationPicker, {
27164     
27165     OverlayView : function(map, options)
27166     {
27167         options = options || {};
27168         
27169         this.setMap(map);
27170     }
27171     
27172     
27173 });/*
27174  * - LGPL
27175  *
27176  * Alert
27177  * 
27178  */
27179
27180 /**
27181  * @class Roo.bootstrap.Alert
27182  * @extends Roo.bootstrap.Component
27183  * Bootstrap Alert class
27184  * @cfg {String} title The title of alert
27185  * @cfg {String} html The content of alert
27186  * @cfg {String} weight (  success | info | warning | danger )
27187  * @cfg {String} faicon font-awesomeicon
27188  * 
27189  * @constructor
27190  * Create a new alert
27191  * @param {Object} config The config object
27192  */
27193
27194
27195 Roo.bootstrap.Alert = function(config){
27196     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27197     
27198 };
27199
27200 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27201     
27202     title: '',
27203     html: '',
27204     weight: false,
27205     faicon: false,
27206     
27207     getAutoCreate : function()
27208     {
27209         
27210         var cfg = {
27211             tag : 'div',
27212             cls : 'alert',
27213             cn : [
27214                 {
27215                     tag : 'i',
27216                     cls : 'roo-alert-icon'
27217                     
27218                 },
27219                 {
27220                     tag : 'b',
27221                     cls : 'roo-alert-title',
27222                     html : this.title
27223                 },
27224                 {
27225                     tag : 'span',
27226                     cls : 'roo-alert-text',
27227                     html : this.html
27228                 }
27229             ]
27230         };
27231         
27232         if(this.faicon){
27233             cfg.cn[0].cls += ' fa ' + this.faicon;
27234         }
27235         
27236         if(this.weight){
27237             cfg.cls += ' alert-' + this.weight;
27238         }
27239         
27240         return cfg;
27241     },
27242     
27243     initEvents: function() 
27244     {
27245         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27246     },
27247     
27248     setTitle : function(str)
27249     {
27250         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27251     },
27252     
27253     setText : function(str)
27254     {
27255         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27256     },
27257     
27258     setWeight : function(weight)
27259     {
27260         if(this.weight){
27261             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27262         }
27263         
27264         this.weight = weight;
27265         
27266         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27267     },
27268     
27269     setIcon : function(icon)
27270     {
27271         if(this.faicon){
27272             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27273         }
27274         
27275         this.faicon = icon;
27276         
27277         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27278     },
27279     
27280     hide: function() 
27281     {
27282         this.el.hide();   
27283     },
27284     
27285     show: function() 
27286     {  
27287         this.el.show();   
27288     }
27289     
27290 });
27291
27292  
27293 /*
27294 * Licence: LGPL
27295 */
27296
27297 /**
27298  * @class Roo.bootstrap.UploadCropbox
27299  * @extends Roo.bootstrap.Component
27300  * Bootstrap UploadCropbox class
27301  * @cfg {String} emptyText show when image has been loaded
27302  * @cfg {String} rotateNotify show when image too small to rotate
27303  * @cfg {Number} errorTimeout default 3000
27304  * @cfg {Number} minWidth default 300
27305  * @cfg {Number} minHeight default 300
27306  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27307  * @cfg {Boolean} isDocument (true|false) default false
27308  * @cfg {String} url action url
27309  * @cfg {String} paramName default 'imageUpload'
27310  * @cfg {String} method default POST
27311  * @cfg {Boolean} loadMask (true|false) default true
27312  * @cfg {Boolean} loadingText default 'Loading...'
27313  * 
27314  * @constructor
27315  * Create a new UploadCropbox
27316  * @param {Object} config The config object
27317  */
27318
27319 Roo.bootstrap.UploadCropbox = function(config){
27320     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27321     
27322     this.addEvents({
27323         /**
27324          * @event beforeselectfile
27325          * Fire before select file
27326          * @param {Roo.bootstrap.UploadCropbox} this
27327          */
27328         "beforeselectfile" : true,
27329         /**
27330          * @event initial
27331          * Fire after initEvent
27332          * @param {Roo.bootstrap.UploadCropbox} this
27333          */
27334         "initial" : true,
27335         /**
27336          * @event crop
27337          * Fire after initEvent
27338          * @param {Roo.bootstrap.UploadCropbox} this
27339          * @param {String} data
27340          */
27341         "crop" : true,
27342         /**
27343          * @event prepare
27344          * Fire when preparing the file data
27345          * @param {Roo.bootstrap.UploadCropbox} this
27346          * @param {Object} file
27347          */
27348         "prepare" : true,
27349         /**
27350          * @event exception
27351          * Fire when get exception
27352          * @param {Roo.bootstrap.UploadCropbox} this
27353          * @param {XMLHttpRequest} xhr
27354          */
27355         "exception" : true,
27356         /**
27357          * @event beforeloadcanvas
27358          * Fire before load the canvas
27359          * @param {Roo.bootstrap.UploadCropbox} this
27360          * @param {String} src
27361          */
27362         "beforeloadcanvas" : true,
27363         /**
27364          * @event trash
27365          * Fire when trash image
27366          * @param {Roo.bootstrap.UploadCropbox} this
27367          */
27368         "trash" : true,
27369         /**
27370          * @event download
27371          * Fire when download the image
27372          * @param {Roo.bootstrap.UploadCropbox} this
27373          */
27374         "download" : true,
27375         /**
27376          * @event footerbuttonclick
27377          * Fire when footerbuttonclick
27378          * @param {Roo.bootstrap.UploadCropbox} this
27379          * @param {String} type
27380          */
27381         "footerbuttonclick" : true,
27382         /**
27383          * @event resize
27384          * Fire when resize
27385          * @param {Roo.bootstrap.UploadCropbox} this
27386          */
27387         "resize" : true,
27388         /**
27389          * @event rotate
27390          * Fire when rotate the image
27391          * @param {Roo.bootstrap.UploadCropbox} this
27392          * @param {String} pos
27393          */
27394         "rotate" : true,
27395         /**
27396          * @event inspect
27397          * Fire when inspect the file
27398          * @param {Roo.bootstrap.UploadCropbox} this
27399          * @param {Object} file
27400          */
27401         "inspect" : true,
27402         /**
27403          * @event upload
27404          * Fire when xhr upload the file
27405          * @param {Roo.bootstrap.UploadCropbox} this
27406          * @param {Object} data
27407          */
27408         "upload" : true,
27409         /**
27410          * @event arrange
27411          * Fire when arrange the file data
27412          * @param {Roo.bootstrap.UploadCropbox} this
27413          * @param {Object} formData
27414          */
27415         "arrange" : true
27416     });
27417     
27418     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27419 };
27420
27421 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27422     
27423     emptyText : 'Click to upload image',
27424     rotateNotify : 'Image is too small to rotate',
27425     errorTimeout : 3000,
27426     scale : 0,
27427     baseScale : 1,
27428     rotate : 0,
27429     dragable : false,
27430     pinching : false,
27431     mouseX : 0,
27432     mouseY : 0,
27433     cropData : false,
27434     minWidth : 300,
27435     minHeight : 300,
27436     file : false,
27437     exif : {},
27438     baseRotate : 1,
27439     cropType : 'image/jpeg',
27440     buttons : false,
27441     canvasLoaded : false,
27442     isDocument : false,
27443     method : 'POST',
27444     paramName : 'imageUpload',
27445     loadMask : true,
27446     loadingText : 'Loading...',
27447     maskEl : false,
27448     
27449     getAutoCreate : function()
27450     {
27451         var cfg = {
27452             tag : 'div',
27453             cls : 'roo-upload-cropbox',
27454             cn : [
27455                 {
27456                     tag : 'input',
27457                     cls : 'roo-upload-cropbox-selector',
27458                     type : 'file'
27459                 },
27460                 {
27461                     tag : 'div',
27462                     cls : 'roo-upload-cropbox-body',
27463                     style : 'cursor:pointer',
27464                     cn : [
27465                         {
27466                             tag : 'div',
27467                             cls : 'roo-upload-cropbox-preview'
27468                         },
27469                         {
27470                             tag : 'div',
27471                             cls : 'roo-upload-cropbox-thumb'
27472                         },
27473                         {
27474                             tag : 'div',
27475                             cls : 'roo-upload-cropbox-empty-notify',
27476                             html : this.emptyText
27477                         },
27478                         {
27479                             tag : 'div',
27480                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27481                             html : this.rotateNotify
27482                         }
27483                     ]
27484                 },
27485                 {
27486                     tag : 'div',
27487                     cls : 'roo-upload-cropbox-footer',
27488                     cn : {
27489                         tag : 'div',
27490                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27491                         cn : []
27492                     }
27493                 }
27494             ]
27495         };
27496         
27497         return cfg;
27498     },
27499     
27500     onRender : function(ct, position)
27501     {
27502         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27503         
27504         if (this.buttons.length) {
27505             
27506             Roo.each(this.buttons, function(bb) {
27507                 
27508                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27509                 
27510                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27511                 
27512             }, this);
27513         }
27514         
27515         if(this.loadMask){
27516             this.maskEl = this.el;
27517         }
27518     },
27519     
27520     initEvents : function()
27521     {
27522         this.urlAPI = (window.createObjectURL && window) || 
27523                                 (window.URL && URL.revokeObjectURL && URL) || 
27524                                 (window.webkitURL && webkitURL);
27525                         
27526         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27527         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27528         
27529         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27530         this.selectorEl.hide();
27531         
27532         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27533         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27534         
27535         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27536         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27537         this.thumbEl.hide();
27538         
27539         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27540         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27541         
27542         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27543         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27544         this.errorEl.hide();
27545         
27546         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27547         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27548         this.footerEl.hide();
27549         
27550         this.setThumbBoxSize();
27551         
27552         this.bind();
27553         
27554         this.resize();
27555         
27556         this.fireEvent('initial', this);
27557     },
27558
27559     bind : function()
27560     {
27561         var _this = this;
27562         
27563         window.addEventListener("resize", function() { _this.resize(); } );
27564         
27565         this.bodyEl.on('click', this.beforeSelectFile, this);
27566         
27567         if(Roo.isTouch){
27568             this.bodyEl.on('touchstart', this.onTouchStart, this);
27569             this.bodyEl.on('touchmove', this.onTouchMove, this);
27570             this.bodyEl.on('touchend', this.onTouchEnd, this);
27571         }
27572         
27573         if(!Roo.isTouch){
27574             this.bodyEl.on('mousedown', this.onMouseDown, this);
27575             this.bodyEl.on('mousemove', this.onMouseMove, this);
27576             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27577             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27578             Roo.get(document).on('mouseup', this.onMouseUp, this);
27579         }
27580         
27581         this.selectorEl.on('change', this.onFileSelected, this);
27582     },
27583     
27584     reset : function()
27585     {    
27586         this.scale = 0;
27587         this.baseScale = 1;
27588         this.rotate = 0;
27589         this.baseRotate = 1;
27590         this.dragable = false;
27591         this.pinching = false;
27592         this.mouseX = 0;
27593         this.mouseY = 0;
27594         this.cropData = false;
27595         this.notifyEl.dom.innerHTML = this.emptyText;
27596         
27597         this.selectorEl.dom.value = '';
27598         
27599     },
27600     
27601     resize : function()
27602     {
27603         if(this.fireEvent('resize', this) != false){
27604             this.setThumbBoxPosition();
27605             this.setCanvasPosition();
27606         }
27607     },
27608     
27609     onFooterButtonClick : function(e, el, o, type)
27610     {
27611         switch (type) {
27612             case 'rotate-left' :
27613                 this.onRotateLeft(e);
27614                 break;
27615             case 'rotate-right' :
27616                 this.onRotateRight(e);
27617                 break;
27618             case 'picture' :
27619                 this.beforeSelectFile(e);
27620                 break;
27621             case 'trash' :
27622                 this.trash(e);
27623                 break;
27624             case 'crop' :
27625                 this.crop(e);
27626                 break;
27627             case 'download' :
27628                 this.download(e);
27629                 break;
27630             default :
27631                 break;
27632         }
27633         
27634         this.fireEvent('footerbuttonclick', this, type);
27635     },
27636     
27637     beforeSelectFile : function(e)
27638     {
27639         e.preventDefault();
27640         
27641         if(this.fireEvent('beforeselectfile', this) != false){
27642             this.selectorEl.dom.click();
27643         }
27644     },
27645     
27646     onFileSelected : function(e)
27647     {
27648         e.preventDefault();
27649         
27650         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27651             return;
27652         }
27653         
27654         var file = this.selectorEl.dom.files[0];
27655         
27656         if(this.fireEvent('inspect', this, file) != false){
27657             this.prepare(file);
27658         }
27659         
27660     },
27661     
27662     trash : function(e)
27663     {
27664         this.fireEvent('trash', this);
27665     },
27666     
27667     download : function(e)
27668     {
27669         this.fireEvent('download', this);
27670     },
27671     
27672     loadCanvas : function(src)
27673     {   
27674         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27675             
27676             this.reset();
27677             
27678             this.imageEl = document.createElement('img');
27679             
27680             var _this = this;
27681             
27682             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27683             
27684             this.imageEl.src = src;
27685         }
27686     },
27687     
27688     onLoadCanvas : function()
27689     {   
27690         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27691         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27692         
27693         this.bodyEl.un('click', this.beforeSelectFile, this);
27694         
27695         this.notifyEl.hide();
27696         this.thumbEl.show();
27697         this.footerEl.show();
27698         
27699         this.baseRotateLevel();
27700         
27701         if(this.isDocument){
27702             this.setThumbBoxSize();
27703         }
27704         
27705         this.setThumbBoxPosition();
27706         
27707         this.baseScaleLevel();
27708         
27709         this.draw();
27710         
27711         this.resize();
27712         
27713         this.canvasLoaded = true;
27714         
27715         if(this.loadMask){
27716             this.maskEl.unmask();
27717         }
27718         
27719     },
27720     
27721     setCanvasPosition : function()
27722     {   
27723         if(!this.canvasEl){
27724             return;
27725         }
27726         
27727         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27728         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27729         
27730         this.previewEl.setLeft(pw);
27731         this.previewEl.setTop(ph);
27732         
27733     },
27734     
27735     onMouseDown : function(e)
27736     {   
27737         e.stopEvent();
27738         
27739         this.dragable = true;
27740         this.pinching = false;
27741         
27742         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27743             this.dragable = false;
27744             return;
27745         }
27746         
27747         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27748         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27749         
27750     },
27751     
27752     onMouseMove : function(e)
27753     {   
27754         e.stopEvent();
27755         
27756         if(!this.canvasLoaded){
27757             return;
27758         }
27759         
27760         if (!this.dragable){
27761             return;
27762         }
27763         
27764         var minX = Math.ceil(this.thumbEl.getLeft(true));
27765         var minY = Math.ceil(this.thumbEl.getTop(true));
27766         
27767         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27768         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27769         
27770         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27771         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27772         
27773         x = x - this.mouseX;
27774         y = y - this.mouseY;
27775         
27776         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27777         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27778         
27779         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27780         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27781         
27782         this.previewEl.setLeft(bgX);
27783         this.previewEl.setTop(bgY);
27784         
27785         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27786         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27787     },
27788     
27789     onMouseUp : function(e)
27790     {   
27791         e.stopEvent();
27792         
27793         this.dragable = false;
27794     },
27795     
27796     onMouseWheel : function(e)
27797     {   
27798         e.stopEvent();
27799         
27800         this.startScale = this.scale;
27801         
27802         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27803         
27804         if(!this.zoomable()){
27805             this.scale = this.startScale;
27806             return;
27807         }
27808         
27809         this.draw();
27810         
27811         return;
27812     },
27813     
27814     zoomable : function()
27815     {
27816         var minScale = this.thumbEl.getWidth() / this.minWidth;
27817         
27818         if(this.minWidth < this.minHeight){
27819             minScale = this.thumbEl.getHeight() / this.minHeight;
27820         }
27821         
27822         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27823         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27824         
27825         if(
27826                 this.isDocument &&
27827                 (this.rotate == 0 || this.rotate == 180) && 
27828                 (
27829                     width > this.imageEl.OriginWidth || 
27830                     height > this.imageEl.OriginHeight ||
27831                     (width < this.minWidth && height < this.minHeight)
27832                 )
27833         ){
27834             return false;
27835         }
27836         
27837         if(
27838                 this.isDocument &&
27839                 (this.rotate == 90 || this.rotate == 270) && 
27840                 (
27841                     width > this.imageEl.OriginWidth || 
27842                     height > this.imageEl.OriginHeight ||
27843                     (width < this.minHeight && height < this.minWidth)
27844                 )
27845         ){
27846             return false;
27847         }
27848         
27849         if(
27850                 !this.isDocument &&
27851                 (this.rotate == 0 || this.rotate == 180) && 
27852                 (
27853                     width < this.minWidth || 
27854                     width > this.imageEl.OriginWidth || 
27855                     height < this.minHeight || 
27856                     height > this.imageEl.OriginHeight
27857                 )
27858         ){
27859             return false;
27860         }
27861         
27862         if(
27863                 !this.isDocument &&
27864                 (this.rotate == 90 || this.rotate == 270) && 
27865                 (
27866                     width < this.minHeight || 
27867                     width > this.imageEl.OriginWidth || 
27868                     height < this.minWidth || 
27869                     height > this.imageEl.OriginHeight
27870                 )
27871         ){
27872             return false;
27873         }
27874         
27875         return true;
27876         
27877     },
27878     
27879     onRotateLeft : function(e)
27880     {   
27881         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27882             
27883             var minScale = this.thumbEl.getWidth() / this.minWidth;
27884             
27885             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27886             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27887             
27888             this.startScale = this.scale;
27889             
27890             while (this.getScaleLevel() < minScale){
27891             
27892                 this.scale = this.scale + 1;
27893                 
27894                 if(!this.zoomable()){
27895                     break;
27896                 }
27897                 
27898                 if(
27899                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27900                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27901                 ){
27902                     continue;
27903                 }
27904                 
27905                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27906
27907                 this.draw();
27908                 
27909                 return;
27910             }
27911             
27912             this.scale = this.startScale;
27913             
27914             this.onRotateFail();
27915             
27916             return false;
27917         }
27918         
27919         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27920
27921         if(this.isDocument){
27922             this.setThumbBoxSize();
27923             this.setThumbBoxPosition();
27924             this.setCanvasPosition();
27925         }
27926         
27927         this.draw();
27928         
27929         this.fireEvent('rotate', this, 'left');
27930         
27931     },
27932     
27933     onRotateRight : function(e)
27934     {
27935         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27936             
27937             var minScale = this.thumbEl.getWidth() / this.minWidth;
27938         
27939             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27940             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27941             
27942             this.startScale = this.scale;
27943             
27944             while (this.getScaleLevel() < minScale){
27945             
27946                 this.scale = this.scale + 1;
27947                 
27948                 if(!this.zoomable()){
27949                     break;
27950                 }
27951                 
27952                 if(
27953                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27954                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27955                 ){
27956                     continue;
27957                 }
27958                 
27959                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27960
27961                 this.draw();
27962                 
27963                 return;
27964             }
27965             
27966             this.scale = this.startScale;
27967             
27968             this.onRotateFail();
27969             
27970             return false;
27971         }
27972         
27973         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27974
27975         if(this.isDocument){
27976             this.setThumbBoxSize();
27977             this.setThumbBoxPosition();
27978             this.setCanvasPosition();
27979         }
27980         
27981         this.draw();
27982         
27983         this.fireEvent('rotate', this, 'right');
27984     },
27985     
27986     onRotateFail : function()
27987     {
27988         this.errorEl.show(true);
27989         
27990         var _this = this;
27991         
27992         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27993     },
27994     
27995     draw : function()
27996     {
27997         this.previewEl.dom.innerHTML = '';
27998         
27999         var canvasEl = document.createElement("canvas");
28000         
28001         var contextEl = canvasEl.getContext("2d");
28002         
28003         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28004         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28005         var center = this.imageEl.OriginWidth / 2;
28006         
28007         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28008             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28009             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28010             center = this.imageEl.OriginHeight / 2;
28011         }
28012         
28013         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28014         
28015         contextEl.translate(center, center);
28016         contextEl.rotate(this.rotate * Math.PI / 180);
28017
28018         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28019         
28020         this.canvasEl = document.createElement("canvas");
28021         
28022         this.contextEl = this.canvasEl.getContext("2d");
28023         
28024         switch (this.rotate) {
28025             case 0 :
28026                 
28027                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28028                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28029                 
28030                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28031                 
28032                 break;
28033             case 90 : 
28034                 
28035                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28036                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28037                 
28038                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28039                     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);
28040                     break;
28041                 }
28042                 
28043                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28044                 
28045                 break;
28046             case 180 :
28047                 
28048                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28049                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28050                 
28051                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28052                     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);
28053                     break;
28054                 }
28055                 
28056                 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);
28057                 
28058                 break;
28059             case 270 :
28060                 
28061                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28062                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28063         
28064                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28065                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28066                     break;
28067                 }
28068                 
28069                 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);
28070                 
28071                 break;
28072             default : 
28073                 break;
28074         }
28075         
28076         this.previewEl.appendChild(this.canvasEl);
28077         
28078         this.setCanvasPosition();
28079     },
28080     
28081     crop : function()
28082     {
28083         if(!this.canvasLoaded){
28084             return;
28085         }
28086         
28087         var imageCanvas = document.createElement("canvas");
28088         
28089         var imageContext = imageCanvas.getContext("2d");
28090         
28091         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28092         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28093         
28094         var center = imageCanvas.width / 2;
28095         
28096         imageContext.translate(center, center);
28097         
28098         imageContext.rotate(this.rotate * Math.PI / 180);
28099         
28100         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28101         
28102         var canvas = document.createElement("canvas");
28103         
28104         var context = canvas.getContext("2d");
28105                 
28106         canvas.width = this.minWidth;
28107         canvas.height = this.minHeight;
28108
28109         switch (this.rotate) {
28110             case 0 :
28111                 
28112                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28113                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28114                 
28115                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28116                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28117                 
28118                 var targetWidth = this.minWidth - 2 * x;
28119                 var targetHeight = this.minHeight - 2 * y;
28120                 
28121                 var scale = 1;
28122                 
28123                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28124                     scale = targetWidth / width;
28125                 }
28126                 
28127                 if(x > 0 && y == 0){
28128                     scale = targetHeight / height;
28129                 }
28130                 
28131                 if(x > 0 && y > 0){
28132                     scale = targetWidth / width;
28133                     
28134                     if(width < height){
28135                         scale = targetHeight / height;
28136                     }
28137                 }
28138                 
28139                 context.scale(scale, scale);
28140                 
28141                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28142                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28143
28144                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28145                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28146
28147                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28148                 
28149                 break;
28150             case 90 : 
28151                 
28152                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28153                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28154                 
28155                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28156                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28157                 
28158                 var targetWidth = this.minWidth - 2 * x;
28159                 var targetHeight = this.minHeight - 2 * y;
28160                 
28161                 var scale = 1;
28162                 
28163                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28164                     scale = targetWidth / width;
28165                 }
28166                 
28167                 if(x > 0 && y == 0){
28168                     scale = targetHeight / height;
28169                 }
28170                 
28171                 if(x > 0 && y > 0){
28172                     scale = targetWidth / width;
28173                     
28174                     if(width < height){
28175                         scale = targetHeight / height;
28176                     }
28177                 }
28178                 
28179                 context.scale(scale, scale);
28180                 
28181                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28182                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28183
28184                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28185                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28186                 
28187                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28188                 
28189                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28190                 
28191                 break;
28192             case 180 :
28193                 
28194                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28195                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28196                 
28197                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28198                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28199                 
28200                 var targetWidth = this.minWidth - 2 * x;
28201                 var targetHeight = this.minHeight - 2 * y;
28202                 
28203                 var scale = 1;
28204                 
28205                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28206                     scale = targetWidth / width;
28207                 }
28208                 
28209                 if(x > 0 && y == 0){
28210                     scale = targetHeight / height;
28211                 }
28212                 
28213                 if(x > 0 && y > 0){
28214                     scale = targetWidth / width;
28215                     
28216                     if(width < height){
28217                         scale = targetHeight / height;
28218                     }
28219                 }
28220                 
28221                 context.scale(scale, scale);
28222                 
28223                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28224                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28225
28226                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28227                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28228
28229                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28230                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28231                 
28232                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28233                 
28234                 break;
28235             case 270 :
28236                 
28237                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28238                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28239                 
28240                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28241                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28242                 
28243                 var targetWidth = this.minWidth - 2 * x;
28244                 var targetHeight = this.minHeight - 2 * y;
28245                 
28246                 var scale = 1;
28247                 
28248                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28249                     scale = targetWidth / width;
28250                 }
28251                 
28252                 if(x > 0 && y == 0){
28253                     scale = targetHeight / height;
28254                 }
28255                 
28256                 if(x > 0 && y > 0){
28257                     scale = targetWidth / width;
28258                     
28259                     if(width < height){
28260                         scale = targetHeight / height;
28261                     }
28262                 }
28263                 
28264                 context.scale(scale, scale);
28265                 
28266                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28267                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28268
28269                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28270                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28271                 
28272                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28273                 
28274                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28275                 
28276                 break;
28277             default : 
28278                 break;
28279         }
28280         
28281         this.cropData = canvas.toDataURL(this.cropType);
28282         
28283         if(this.fireEvent('crop', this, this.cropData) !== false){
28284             this.process(this.file, this.cropData);
28285         }
28286         
28287         return;
28288         
28289     },
28290     
28291     setThumbBoxSize : function()
28292     {
28293         var width, height;
28294         
28295         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28296             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28297             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28298             
28299             this.minWidth = width;
28300             this.minHeight = height;
28301             
28302             if(this.rotate == 90 || this.rotate == 270){
28303                 this.minWidth = height;
28304                 this.minHeight = width;
28305             }
28306         }
28307         
28308         height = 300;
28309         width = Math.ceil(this.minWidth * height / this.minHeight);
28310         
28311         if(this.minWidth > this.minHeight){
28312             width = 300;
28313             height = Math.ceil(this.minHeight * width / this.minWidth);
28314         }
28315         
28316         this.thumbEl.setStyle({
28317             width : width + 'px',
28318             height : height + 'px'
28319         });
28320
28321         return;
28322             
28323     },
28324     
28325     setThumbBoxPosition : function()
28326     {
28327         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28328         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28329         
28330         this.thumbEl.setLeft(x);
28331         this.thumbEl.setTop(y);
28332         
28333     },
28334     
28335     baseRotateLevel : function()
28336     {
28337         this.baseRotate = 1;
28338         
28339         if(
28340                 typeof(this.exif) != 'undefined' &&
28341                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28342                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28343         ){
28344             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28345         }
28346         
28347         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28348         
28349     },
28350     
28351     baseScaleLevel : function()
28352     {
28353         var width, height;
28354         
28355         if(this.isDocument){
28356             
28357             if(this.baseRotate == 6 || this.baseRotate == 8){
28358             
28359                 height = this.thumbEl.getHeight();
28360                 this.baseScale = height / this.imageEl.OriginWidth;
28361
28362                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28363                     width = this.thumbEl.getWidth();
28364                     this.baseScale = width / this.imageEl.OriginHeight;
28365                 }
28366
28367                 return;
28368             }
28369
28370             height = this.thumbEl.getHeight();
28371             this.baseScale = height / this.imageEl.OriginHeight;
28372
28373             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28374                 width = this.thumbEl.getWidth();
28375                 this.baseScale = width / this.imageEl.OriginWidth;
28376             }
28377
28378             return;
28379         }
28380         
28381         if(this.baseRotate == 6 || this.baseRotate == 8){
28382             
28383             width = this.thumbEl.getHeight();
28384             this.baseScale = width / this.imageEl.OriginHeight;
28385             
28386             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28387                 height = this.thumbEl.getWidth();
28388                 this.baseScale = height / this.imageEl.OriginHeight;
28389             }
28390             
28391             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28392                 height = this.thumbEl.getWidth();
28393                 this.baseScale = height / this.imageEl.OriginHeight;
28394                 
28395                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28396                     width = this.thumbEl.getHeight();
28397                     this.baseScale = width / this.imageEl.OriginWidth;
28398                 }
28399             }
28400             
28401             return;
28402         }
28403         
28404         width = this.thumbEl.getWidth();
28405         this.baseScale = width / this.imageEl.OriginWidth;
28406         
28407         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28408             height = this.thumbEl.getHeight();
28409             this.baseScale = height / this.imageEl.OriginHeight;
28410         }
28411         
28412         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28413             
28414             height = this.thumbEl.getHeight();
28415             this.baseScale = height / this.imageEl.OriginHeight;
28416             
28417             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28418                 width = this.thumbEl.getWidth();
28419                 this.baseScale = width / this.imageEl.OriginWidth;
28420             }
28421             
28422         }
28423         
28424         return;
28425     },
28426     
28427     getScaleLevel : function()
28428     {
28429         return this.baseScale * Math.pow(1.1, this.scale);
28430     },
28431     
28432     onTouchStart : function(e)
28433     {
28434         if(!this.canvasLoaded){
28435             this.beforeSelectFile(e);
28436             return;
28437         }
28438         
28439         var touches = e.browserEvent.touches;
28440         
28441         if(!touches){
28442             return;
28443         }
28444         
28445         if(touches.length == 1){
28446             this.onMouseDown(e);
28447             return;
28448         }
28449         
28450         if(touches.length != 2){
28451             return;
28452         }
28453         
28454         var coords = [];
28455         
28456         for(var i = 0, finger; finger = touches[i]; i++){
28457             coords.push(finger.pageX, finger.pageY);
28458         }
28459         
28460         var x = Math.pow(coords[0] - coords[2], 2);
28461         var y = Math.pow(coords[1] - coords[3], 2);
28462         
28463         this.startDistance = Math.sqrt(x + y);
28464         
28465         this.startScale = this.scale;
28466         
28467         this.pinching = true;
28468         this.dragable = false;
28469         
28470     },
28471     
28472     onTouchMove : function(e)
28473     {
28474         if(!this.pinching && !this.dragable){
28475             return;
28476         }
28477         
28478         var touches = e.browserEvent.touches;
28479         
28480         if(!touches){
28481             return;
28482         }
28483         
28484         if(this.dragable){
28485             this.onMouseMove(e);
28486             return;
28487         }
28488         
28489         var coords = [];
28490         
28491         for(var i = 0, finger; finger = touches[i]; i++){
28492             coords.push(finger.pageX, finger.pageY);
28493         }
28494         
28495         var x = Math.pow(coords[0] - coords[2], 2);
28496         var y = Math.pow(coords[1] - coords[3], 2);
28497         
28498         this.endDistance = Math.sqrt(x + y);
28499         
28500         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28501         
28502         if(!this.zoomable()){
28503             this.scale = this.startScale;
28504             return;
28505         }
28506         
28507         this.draw();
28508         
28509     },
28510     
28511     onTouchEnd : function(e)
28512     {
28513         this.pinching = false;
28514         this.dragable = false;
28515         
28516     },
28517     
28518     process : function(file, crop)
28519     {
28520         if(this.loadMask){
28521             this.maskEl.mask(this.loadingText);
28522         }
28523         
28524         this.xhr = new XMLHttpRequest();
28525         
28526         file.xhr = this.xhr;
28527
28528         this.xhr.open(this.method, this.url, true);
28529         
28530         var headers = {
28531             "Accept": "application/json",
28532             "Cache-Control": "no-cache",
28533             "X-Requested-With": "XMLHttpRequest"
28534         };
28535         
28536         for (var headerName in headers) {
28537             var headerValue = headers[headerName];
28538             if (headerValue) {
28539                 this.xhr.setRequestHeader(headerName, headerValue);
28540             }
28541         }
28542         
28543         var _this = this;
28544         
28545         this.xhr.onload = function()
28546         {
28547             _this.xhrOnLoad(_this.xhr);
28548         }
28549         
28550         this.xhr.onerror = function()
28551         {
28552             _this.xhrOnError(_this.xhr);
28553         }
28554         
28555         var formData = new FormData();
28556
28557         formData.append('returnHTML', 'NO');
28558         
28559         if(crop){
28560             formData.append('crop', crop);
28561         }
28562         
28563         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28564             formData.append(this.paramName, file, file.name);
28565         }
28566         
28567         if(typeof(file.filename) != 'undefined'){
28568             formData.append('filename', file.filename);
28569         }
28570         
28571         if(typeof(file.mimetype) != 'undefined'){
28572             formData.append('mimetype', file.mimetype);
28573         }
28574         
28575         if(this.fireEvent('arrange', this, formData) != false){
28576             this.xhr.send(formData);
28577         };
28578     },
28579     
28580     xhrOnLoad : function(xhr)
28581     {
28582         if(this.loadMask){
28583             this.maskEl.unmask();
28584         }
28585         
28586         if (xhr.readyState !== 4) {
28587             this.fireEvent('exception', this, xhr);
28588             return;
28589         }
28590
28591         var response = Roo.decode(xhr.responseText);
28592         
28593         if(!response.success){
28594             this.fireEvent('exception', this, xhr);
28595             return;
28596         }
28597         
28598         var response = Roo.decode(xhr.responseText);
28599         
28600         this.fireEvent('upload', this, response);
28601         
28602     },
28603     
28604     xhrOnError : function()
28605     {
28606         if(this.loadMask){
28607             this.maskEl.unmask();
28608         }
28609         
28610         Roo.log('xhr on error');
28611         
28612         var response = Roo.decode(xhr.responseText);
28613           
28614         Roo.log(response);
28615         
28616     },
28617     
28618     prepare : function(file)
28619     {   
28620         if(this.loadMask){
28621             this.maskEl.mask(this.loadingText);
28622         }
28623         
28624         this.file = false;
28625         this.exif = {};
28626         
28627         if(typeof(file) === 'string'){
28628             this.loadCanvas(file);
28629             return;
28630         }
28631         
28632         if(!file || !this.urlAPI){
28633             return;
28634         }
28635         
28636         this.file = file;
28637         this.cropType = file.type;
28638         
28639         var _this = this;
28640         
28641         if(this.fireEvent('prepare', this, this.file) != false){
28642             
28643             var reader = new FileReader();
28644             
28645             reader.onload = function (e) {
28646                 if (e.target.error) {
28647                     Roo.log(e.target.error);
28648                     return;
28649                 }
28650                 
28651                 var buffer = e.target.result,
28652                     dataView = new DataView(buffer),
28653                     offset = 2,
28654                     maxOffset = dataView.byteLength - 4,
28655                     markerBytes,
28656                     markerLength;
28657                 
28658                 if (dataView.getUint16(0) === 0xffd8) {
28659                     while (offset < maxOffset) {
28660                         markerBytes = dataView.getUint16(offset);
28661                         
28662                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28663                             markerLength = dataView.getUint16(offset + 2) + 2;
28664                             if (offset + markerLength > dataView.byteLength) {
28665                                 Roo.log('Invalid meta data: Invalid segment size.');
28666                                 break;
28667                             }
28668                             
28669                             if(markerBytes == 0xffe1){
28670                                 _this.parseExifData(
28671                                     dataView,
28672                                     offset,
28673                                     markerLength
28674                                 );
28675                             }
28676                             
28677                             offset += markerLength;
28678                             
28679                             continue;
28680                         }
28681                         
28682                         break;
28683                     }
28684                     
28685                 }
28686                 
28687                 var url = _this.urlAPI.createObjectURL(_this.file);
28688                 
28689                 _this.loadCanvas(url);
28690                 
28691                 return;
28692             }
28693             
28694             reader.readAsArrayBuffer(this.file);
28695             
28696         }
28697         
28698     },
28699     
28700     parseExifData : function(dataView, offset, length)
28701     {
28702         var tiffOffset = offset + 10,
28703             littleEndian,
28704             dirOffset;
28705     
28706         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28707             // No Exif data, might be XMP data instead
28708             return;
28709         }
28710         
28711         // Check for the ASCII code for "Exif" (0x45786966):
28712         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28713             // No Exif data, might be XMP data instead
28714             return;
28715         }
28716         if (tiffOffset + 8 > dataView.byteLength) {
28717             Roo.log('Invalid Exif data: Invalid segment size.');
28718             return;
28719         }
28720         // Check for the two null bytes:
28721         if (dataView.getUint16(offset + 8) !== 0x0000) {
28722             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28723             return;
28724         }
28725         // Check the byte alignment:
28726         switch (dataView.getUint16(tiffOffset)) {
28727         case 0x4949:
28728             littleEndian = true;
28729             break;
28730         case 0x4D4D:
28731             littleEndian = false;
28732             break;
28733         default:
28734             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28735             return;
28736         }
28737         // Check for the TIFF tag marker (0x002A):
28738         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28739             Roo.log('Invalid Exif data: Missing TIFF marker.');
28740             return;
28741         }
28742         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28743         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28744         
28745         this.parseExifTags(
28746             dataView,
28747             tiffOffset,
28748             tiffOffset + dirOffset,
28749             littleEndian
28750         );
28751     },
28752     
28753     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28754     {
28755         var tagsNumber,
28756             dirEndOffset,
28757             i;
28758         if (dirOffset + 6 > dataView.byteLength) {
28759             Roo.log('Invalid Exif data: Invalid directory offset.');
28760             return;
28761         }
28762         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28763         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28764         if (dirEndOffset + 4 > dataView.byteLength) {
28765             Roo.log('Invalid Exif data: Invalid directory size.');
28766             return;
28767         }
28768         for (i = 0; i < tagsNumber; i += 1) {
28769             this.parseExifTag(
28770                 dataView,
28771                 tiffOffset,
28772                 dirOffset + 2 + 12 * i, // tag offset
28773                 littleEndian
28774             );
28775         }
28776         // Return the offset to the next directory:
28777         return dataView.getUint32(dirEndOffset, littleEndian);
28778     },
28779     
28780     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28781     {
28782         var tag = dataView.getUint16(offset, littleEndian);
28783         
28784         this.exif[tag] = this.getExifValue(
28785             dataView,
28786             tiffOffset,
28787             offset,
28788             dataView.getUint16(offset + 2, littleEndian), // tag type
28789             dataView.getUint32(offset + 4, littleEndian), // tag length
28790             littleEndian
28791         );
28792     },
28793     
28794     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28795     {
28796         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28797             tagSize,
28798             dataOffset,
28799             values,
28800             i,
28801             str,
28802             c;
28803     
28804         if (!tagType) {
28805             Roo.log('Invalid Exif data: Invalid tag type.');
28806             return;
28807         }
28808         
28809         tagSize = tagType.size * length;
28810         // Determine if the value is contained in the dataOffset bytes,
28811         // or if the value at the dataOffset is a pointer to the actual data:
28812         dataOffset = tagSize > 4 ?
28813                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28814         if (dataOffset + tagSize > dataView.byteLength) {
28815             Roo.log('Invalid Exif data: Invalid data offset.');
28816             return;
28817         }
28818         if (length === 1) {
28819             return tagType.getValue(dataView, dataOffset, littleEndian);
28820         }
28821         values = [];
28822         for (i = 0; i < length; i += 1) {
28823             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28824         }
28825         
28826         if (tagType.ascii) {
28827             str = '';
28828             // Concatenate the chars:
28829             for (i = 0; i < values.length; i += 1) {
28830                 c = values[i];
28831                 // Ignore the terminating NULL byte(s):
28832                 if (c === '\u0000') {
28833                     break;
28834                 }
28835                 str += c;
28836             }
28837             return str;
28838         }
28839         return values;
28840     }
28841     
28842 });
28843
28844 Roo.apply(Roo.bootstrap.UploadCropbox, {
28845     tags : {
28846         'Orientation': 0x0112
28847     },
28848     
28849     Orientation: {
28850             1: 0, //'top-left',
28851 //            2: 'top-right',
28852             3: 180, //'bottom-right',
28853 //            4: 'bottom-left',
28854 //            5: 'left-top',
28855             6: 90, //'right-top',
28856 //            7: 'right-bottom',
28857             8: 270 //'left-bottom'
28858     },
28859     
28860     exifTagTypes : {
28861         // byte, 8-bit unsigned int:
28862         1: {
28863             getValue: function (dataView, dataOffset) {
28864                 return dataView.getUint8(dataOffset);
28865             },
28866             size: 1
28867         },
28868         // ascii, 8-bit byte:
28869         2: {
28870             getValue: function (dataView, dataOffset) {
28871                 return String.fromCharCode(dataView.getUint8(dataOffset));
28872             },
28873             size: 1,
28874             ascii: true
28875         },
28876         // short, 16 bit int:
28877         3: {
28878             getValue: function (dataView, dataOffset, littleEndian) {
28879                 return dataView.getUint16(dataOffset, littleEndian);
28880             },
28881             size: 2
28882         },
28883         // long, 32 bit int:
28884         4: {
28885             getValue: function (dataView, dataOffset, littleEndian) {
28886                 return dataView.getUint32(dataOffset, littleEndian);
28887             },
28888             size: 4
28889         },
28890         // rational = two long values, first is numerator, second is denominator:
28891         5: {
28892             getValue: function (dataView, dataOffset, littleEndian) {
28893                 return dataView.getUint32(dataOffset, littleEndian) /
28894                     dataView.getUint32(dataOffset + 4, littleEndian);
28895             },
28896             size: 8
28897         },
28898         // slong, 32 bit signed int:
28899         9: {
28900             getValue: function (dataView, dataOffset, littleEndian) {
28901                 return dataView.getInt32(dataOffset, littleEndian);
28902             },
28903             size: 4
28904         },
28905         // srational, two slongs, first is numerator, second is denominator:
28906         10: {
28907             getValue: function (dataView, dataOffset, littleEndian) {
28908                 return dataView.getInt32(dataOffset, littleEndian) /
28909                     dataView.getInt32(dataOffset + 4, littleEndian);
28910             },
28911             size: 8
28912         }
28913     },
28914     
28915     footer : {
28916         STANDARD : [
28917             {
28918                 tag : 'div',
28919                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28920                 action : 'rotate-left',
28921                 cn : [
28922                     {
28923                         tag : 'button',
28924                         cls : 'btn btn-default',
28925                         html : '<i class="fa fa-undo"></i>'
28926                     }
28927                 ]
28928             },
28929             {
28930                 tag : 'div',
28931                 cls : 'btn-group roo-upload-cropbox-picture',
28932                 action : 'picture',
28933                 cn : [
28934                     {
28935                         tag : 'button',
28936                         cls : 'btn btn-default',
28937                         html : '<i class="fa fa-picture-o"></i>'
28938                     }
28939                 ]
28940             },
28941             {
28942                 tag : 'div',
28943                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28944                 action : 'rotate-right',
28945                 cn : [
28946                     {
28947                         tag : 'button',
28948                         cls : 'btn btn-default',
28949                         html : '<i class="fa fa-repeat"></i>'
28950                     }
28951                 ]
28952             }
28953         ],
28954         DOCUMENT : [
28955             {
28956                 tag : 'div',
28957                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28958                 action : 'rotate-left',
28959                 cn : [
28960                     {
28961                         tag : 'button',
28962                         cls : 'btn btn-default',
28963                         html : '<i class="fa fa-undo"></i>'
28964                     }
28965                 ]
28966             },
28967             {
28968                 tag : 'div',
28969                 cls : 'btn-group roo-upload-cropbox-download',
28970                 action : 'download',
28971                 cn : [
28972                     {
28973                         tag : 'button',
28974                         cls : 'btn btn-default',
28975                         html : '<i class="fa fa-download"></i>'
28976                     }
28977                 ]
28978             },
28979             {
28980                 tag : 'div',
28981                 cls : 'btn-group roo-upload-cropbox-crop',
28982                 action : 'crop',
28983                 cn : [
28984                     {
28985                         tag : 'button',
28986                         cls : 'btn btn-default',
28987                         html : '<i class="fa fa-crop"></i>'
28988                     }
28989                 ]
28990             },
28991             {
28992                 tag : 'div',
28993                 cls : 'btn-group roo-upload-cropbox-trash',
28994                 action : 'trash',
28995                 cn : [
28996                     {
28997                         tag : 'button',
28998                         cls : 'btn btn-default',
28999                         html : '<i class="fa fa-trash"></i>'
29000                     }
29001                 ]
29002             },
29003             {
29004                 tag : 'div',
29005                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29006                 action : 'rotate-right',
29007                 cn : [
29008                     {
29009                         tag : 'button',
29010                         cls : 'btn btn-default',
29011                         html : '<i class="fa fa-repeat"></i>'
29012                     }
29013                 ]
29014             }
29015         ],
29016         ROTATOR : [
29017             {
29018                 tag : 'div',
29019                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29020                 action : 'rotate-left',
29021                 cn : [
29022                     {
29023                         tag : 'button',
29024                         cls : 'btn btn-default',
29025                         html : '<i class="fa fa-undo"></i>'
29026                     }
29027                 ]
29028             },
29029             {
29030                 tag : 'div',
29031                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29032                 action : 'rotate-right',
29033                 cn : [
29034                     {
29035                         tag : 'button',
29036                         cls : 'btn btn-default',
29037                         html : '<i class="fa fa-repeat"></i>'
29038                     }
29039                 ]
29040             }
29041         ]
29042     }
29043 });
29044
29045 /*
29046 * Licence: LGPL
29047 */
29048
29049 /**
29050  * @class Roo.bootstrap.DocumentManager
29051  * @extends Roo.bootstrap.Component
29052  * Bootstrap DocumentManager class
29053  * @cfg {String} paramName default 'imageUpload'
29054  * @cfg {String} toolTipName default 'filename'
29055  * @cfg {String} method default POST
29056  * @cfg {String} url action url
29057  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29058  * @cfg {Boolean} multiple multiple upload default true
29059  * @cfg {Number} thumbSize default 300
29060  * @cfg {String} fieldLabel
29061  * @cfg {Number} labelWidth default 4
29062  * @cfg {String} labelAlign (left|top) default left
29063  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29064 * @cfg {Number} labellg set the width of label (1-12)
29065  * @cfg {Number} labelmd set the width of label (1-12)
29066  * @cfg {Number} labelsm set the width of label (1-12)
29067  * @cfg {Number} labelxs set the width of label (1-12)
29068  * 
29069  * @constructor
29070  * Create a new DocumentManager
29071  * @param {Object} config The config object
29072  */
29073
29074 Roo.bootstrap.DocumentManager = function(config){
29075     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29076     
29077     this.files = [];
29078     this.delegates = [];
29079     
29080     this.addEvents({
29081         /**
29082          * @event initial
29083          * Fire when initial the DocumentManager
29084          * @param {Roo.bootstrap.DocumentManager} this
29085          */
29086         "initial" : true,
29087         /**
29088          * @event inspect
29089          * inspect selected file
29090          * @param {Roo.bootstrap.DocumentManager} this
29091          * @param {File} file
29092          */
29093         "inspect" : true,
29094         /**
29095          * @event exception
29096          * Fire when xhr load exception
29097          * @param {Roo.bootstrap.DocumentManager} this
29098          * @param {XMLHttpRequest} xhr
29099          */
29100         "exception" : true,
29101         /**
29102          * @event afterupload
29103          * Fire when xhr load exception
29104          * @param {Roo.bootstrap.DocumentManager} this
29105          * @param {XMLHttpRequest} xhr
29106          */
29107         "afterupload" : true,
29108         /**
29109          * @event prepare
29110          * prepare the form data
29111          * @param {Roo.bootstrap.DocumentManager} this
29112          * @param {Object} formData
29113          */
29114         "prepare" : true,
29115         /**
29116          * @event remove
29117          * Fire when remove the file
29118          * @param {Roo.bootstrap.DocumentManager} this
29119          * @param {Object} file
29120          */
29121         "remove" : true,
29122         /**
29123          * @event refresh
29124          * Fire after refresh the file
29125          * @param {Roo.bootstrap.DocumentManager} this
29126          */
29127         "refresh" : true,
29128         /**
29129          * @event click
29130          * Fire after click the image
29131          * @param {Roo.bootstrap.DocumentManager} this
29132          * @param {Object} file
29133          */
29134         "click" : true,
29135         /**
29136          * @event edit
29137          * Fire when upload a image and editable set to true
29138          * @param {Roo.bootstrap.DocumentManager} this
29139          * @param {Object} file
29140          */
29141         "edit" : true,
29142         /**
29143          * @event beforeselectfile
29144          * Fire before select file
29145          * @param {Roo.bootstrap.DocumentManager} this
29146          */
29147         "beforeselectfile" : true,
29148         /**
29149          * @event process
29150          * Fire before process file
29151          * @param {Roo.bootstrap.DocumentManager} this
29152          * @param {Object} file
29153          */
29154         "process" : true,
29155         /**
29156          * @event previewrendered
29157          * Fire when preview rendered
29158          * @param {Roo.bootstrap.DocumentManager} this
29159          * @param {Object} file
29160          */
29161         "previewrendered" : true,
29162         /**
29163          */
29164         "previewResize" : true
29165         
29166     });
29167 };
29168
29169 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29170     
29171     boxes : 0,
29172     inputName : '',
29173     thumbSize : 300,
29174     multiple : true,
29175     files : false,
29176     method : 'POST',
29177     url : '',
29178     paramName : 'imageUpload',
29179     toolTipName : 'filename',
29180     fieldLabel : '',
29181     labelWidth : 4,
29182     labelAlign : 'left',
29183     editable : true,
29184     delegates : false,
29185     xhr : false, 
29186     
29187     labellg : 0,
29188     labelmd : 0,
29189     labelsm : 0,
29190     labelxs : 0,
29191     
29192     getAutoCreate : function()
29193     {   
29194         var managerWidget = {
29195             tag : 'div',
29196             cls : 'roo-document-manager',
29197             cn : [
29198                 {
29199                     tag : 'input',
29200                     cls : 'roo-document-manager-selector',
29201                     type : 'file'
29202                 },
29203                 {
29204                     tag : 'div',
29205                     cls : 'roo-document-manager-uploader',
29206                     cn : [
29207                         {
29208                             tag : 'div',
29209                             cls : 'roo-document-manager-upload-btn',
29210                             html : '<i class="fa fa-plus"></i>'
29211                         }
29212                     ]
29213                     
29214                 }
29215             ]
29216         };
29217         
29218         var content = [
29219             {
29220                 tag : 'div',
29221                 cls : 'column col-md-12',
29222                 cn : managerWidget
29223             }
29224         ];
29225         
29226         if(this.fieldLabel.length){
29227             
29228             content = [
29229                 {
29230                     tag : 'div',
29231                     cls : 'column col-md-12',
29232                     html : this.fieldLabel
29233                 },
29234                 {
29235                     tag : 'div',
29236                     cls : 'column col-md-12',
29237                     cn : managerWidget
29238                 }
29239             ];
29240
29241             if(this.labelAlign == 'left'){
29242                 content = [
29243                     {
29244                         tag : 'div',
29245                         cls : 'column',
29246                         html : this.fieldLabel
29247                     },
29248                     {
29249                         tag : 'div',
29250                         cls : 'column',
29251                         cn : managerWidget
29252                     }
29253                 ];
29254                 
29255                 if(this.labelWidth > 12){
29256                     content[0].style = "width: " + this.labelWidth + 'px';
29257                 }
29258
29259                 if(this.labelWidth < 13 && this.labelmd == 0){
29260                     this.labelmd = this.labelWidth;
29261                 }
29262
29263                 if(this.labellg > 0){
29264                     content[0].cls += ' col-lg-' + this.labellg;
29265                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29266                 }
29267
29268                 if(this.labelmd > 0){
29269                     content[0].cls += ' col-md-' + this.labelmd;
29270                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29271                 }
29272
29273                 if(this.labelsm > 0){
29274                     content[0].cls += ' col-sm-' + this.labelsm;
29275                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29276                 }
29277
29278                 if(this.labelxs > 0){
29279                     content[0].cls += ' col-xs-' + this.labelxs;
29280                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29281                 }
29282                 
29283             }
29284         }
29285         
29286         var cfg = {
29287             tag : 'div',
29288             cls : 'row clearfix',
29289             cn : content
29290         };
29291         
29292         return cfg;
29293         
29294     },
29295     
29296     initEvents : function()
29297     {
29298         this.managerEl = this.el.select('.roo-document-manager', true).first();
29299         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29300         
29301         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29302         this.selectorEl.hide();
29303         
29304         if(this.multiple){
29305             this.selectorEl.attr('multiple', 'multiple');
29306         }
29307         
29308         this.selectorEl.on('change', this.onFileSelected, this);
29309         
29310         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29311         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29312         
29313         this.uploader.on('click', this.onUploaderClick, this);
29314         
29315         this.renderProgressDialog();
29316         
29317         var _this = this;
29318         
29319         window.addEventListener("resize", function() { _this.refresh(); } );
29320         
29321         this.fireEvent('initial', this);
29322     },
29323     
29324     renderProgressDialog : function()
29325     {
29326         var _this = this;
29327         
29328         this.progressDialog = new Roo.bootstrap.Modal({
29329             cls : 'roo-document-manager-progress-dialog',
29330             allow_close : false,
29331             animate : false,
29332             title : '',
29333             buttons : [
29334                 {
29335                     name  :'cancel',
29336                     weight : 'danger',
29337                     html : 'Cancel'
29338                 }
29339             ], 
29340             listeners : { 
29341                 btnclick : function() {
29342                     _this.uploadCancel();
29343                     this.hide();
29344                 }
29345             }
29346         });
29347          
29348         this.progressDialog.render(Roo.get(document.body));
29349          
29350         this.progress = new Roo.bootstrap.Progress({
29351             cls : 'roo-document-manager-progress',
29352             active : true,
29353             striped : true
29354         });
29355         
29356         this.progress.render(this.progressDialog.getChildContainer());
29357         
29358         this.progressBar = new Roo.bootstrap.ProgressBar({
29359             cls : 'roo-document-manager-progress-bar',
29360             aria_valuenow : 0,
29361             aria_valuemin : 0,
29362             aria_valuemax : 12,
29363             panel : 'success'
29364         });
29365         
29366         this.progressBar.render(this.progress.getChildContainer());
29367     },
29368     
29369     onUploaderClick : function(e)
29370     {
29371         e.preventDefault();
29372      
29373         if(this.fireEvent('beforeselectfile', this) != false){
29374             this.selectorEl.dom.click();
29375         }
29376         
29377     },
29378     
29379     onFileSelected : function(e)
29380     {
29381         e.preventDefault();
29382         
29383         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29384             return;
29385         }
29386         
29387         Roo.each(this.selectorEl.dom.files, function(file){
29388             if(this.fireEvent('inspect', this, file) != false){
29389                 this.files.push(file);
29390             }
29391         }, this);
29392         
29393         this.queue();
29394         
29395     },
29396     
29397     queue : function()
29398     {
29399         this.selectorEl.dom.value = '';
29400         
29401         if(!this.files || !this.files.length){
29402             return;
29403         }
29404         
29405         if(this.boxes > 0 && this.files.length > this.boxes){
29406             this.files = this.files.slice(0, this.boxes);
29407         }
29408         
29409         this.uploader.show();
29410         
29411         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29412             this.uploader.hide();
29413         }
29414         
29415         var _this = this;
29416         
29417         var files = [];
29418         
29419         var docs = [];
29420         
29421         Roo.each(this.files, function(file){
29422             
29423             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29424                 var f = this.renderPreview(file);
29425                 files.push(f);
29426                 return;
29427             }
29428             
29429             if(file.type.indexOf('image') != -1){
29430                 this.delegates.push(
29431                     (function(){
29432                         _this.process(file);
29433                     }).createDelegate(this)
29434                 );
29435         
29436                 return;
29437             }
29438             
29439             docs.push(
29440                 (function(){
29441                     _this.process(file);
29442                 }).createDelegate(this)
29443             );
29444             
29445         }, this);
29446         
29447         this.files = files;
29448         
29449         this.delegates = this.delegates.concat(docs);
29450         
29451         if(!this.delegates.length){
29452             this.refresh();
29453             return;
29454         }
29455         
29456         this.progressBar.aria_valuemax = this.delegates.length;
29457         
29458         this.arrange();
29459         
29460         return;
29461     },
29462     
29463     arrange : function()
29464     {
29465         if(!this.delegates.length){
29466             this.progressDialog.hide();
29467             this.refresh();
29468             return;
29469         }
29470         
29471         var delegate = this.delegates.shift();
29472         
29473         this.progressDialog.show();
29474         
29475         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29476         
29477         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29478         
29479         delegate();
29480     },
29481     
29482     refresh : function()
29483     {
29484         this.uploader.show();
29485         
29486         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29487             this.uploader.hide();
29488         }
29489         
29490         Roo.isTouch ? this.closable(false) : this.closable(true);
29491         
29492         this.fireEvent('refresh', this);
29493     },
29494     
29495     onRemove : function(e, el, o)
29496     {
29497         e.preventDefault();
29498         
29499         this.fireEvent('remove', this, o);
29500         
29501     },
29502     
29503     remove : function(o)
29504     {
29505         var files = [];
29506         
29507         Roo.each(this.files, function(file){
29508             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29509                 files.push(file);
29510                 return;
29511             }
29512
29513             o.target.remove();
29514
29515         }, this);
29516         
29517         this.files = files;
29518         
29519         this.refresh();
29520     },
29521     
29522     clear : function()
29523     {
29524         Roo.each(this.files, function(file){
29525             if(!file.target){
29526                 return;
29527             }
29528             
29529             file.target.remove();
29530
29531         }, this);
29532         
29533         this.files = [];
29534         
29535         this.refresh();
29536     },
29537     
29538     onClick : function(e, el, o)
29539     {
29540         e.preventDefault();
29541         
29542         this.fireEvent('click', this, o);
29543         
29544     },
29545     
29546     closable : function(closable)
29547     {
29548         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29549             
29550             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29551             
29552             if(closable){
29553                 el.show();
29554                 return;
29555             }
29556             
29557             el.hide();
29558             
29559         }, this);
29560     },
29561     
29562     xhrOnLoad : function(xhr)
29563     {
29564         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29565             el.remove();
29566         }, this);
29567         
29568         if (xhr.readyState !== 4) {
29569             this.arrange();
29570             this.fireEvent('exception', this, xhr);
29571             return;
29572         }
29573
29574         var response = Roo.decode(xhr.responseText);
29575         
29576         if(!response.success){
29577             this.arrange();
29578             this.fireEvent('exception', this, xhr);
29579             return;
29580         }
29581         
29582         var file = this.renderPreview(response.data);
29583         
29584         this.files.push(file);
29585         
29586         this.arrange();
29587         
29588         this.fireEvent('afterupload', this, xhr);
29589         
29590     },
29591     
29592     xhrOnError : function(xhr)
29593     {
29594         Roo.log('xhr on error');
29595         
29596         var response = Roo.decode(xhr.responseText);
29597           
29598         Roo.log(response);
29599         
29600         this.arrange();
29601     },
29602     
29603     process : function(file)
29604     {
29605         if(this.fireEvent('process', this, file) !== false){
29606             if(this.editable && file.type.indexOf('image') != -1){
29607                 this.fireEvent('edit', this, file);
29608                 return;
29609             }
29610
29611             this.uploadStart(file, false);
29612
29613             return;
29614         }
29615         
29616     },
29617     
29618     uploadStart : function(file, crop)
29619     {
29620         this.xhr = new XMLHttpRequest();
29621         
29622         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29623             this.arrange();
29624             return;
29625         }
29626         
29627         file.xhr = this.xhr;
29628             
29629         this.managerEl.createChild({
29630             tag : 'div',
29631             cls : 'roo-document-manager-loading',
29632             cn : [
29633                 {
29634                     tag : 'div',
29635                     tooltip : file.name,
29636                     cls : 'roo-document-manager-thumb',
29637                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29638                 }
29639             ]
29640
29641         });
29642
29643         this.xhr.open(this.method, this.url, true);
29644         
29645         var headers = {
29646             "Accept": "application/json",
29647             "Cache-Control": "no-cache",
29648             "X-Requested-With": "XMLHttpRequest"
29649         };
29650         
29651         for (var headerName in headers) {
29652             var headerValue = headers[headerName];
29653             if (headerValue) {
29654                 this.xhr.setRequestHeader(headerName, headerValue);
29655             }
29656         }
29657         
29658         var _this = this;
29659         
29660         this.xhr.onload = function()
29661         {
29662             _this.xhrOnLoad(_this.xhr);
29663         }
29664         
29665         this.xhr.onerror = function()
29666         {
29667             _this.xhrOnError(_this.xhr);
29668         }
29669         
29670         var formData = new FormData();
29671
29672         formData.append('returnHTML', 'NO');
29673         
29674         if(crop){
29675             formData.append('crop', crop);
29676         }
29677         
29678         formData.append(this.paramName, file, file.name);
29679         
29680         var options = {
29681             file : file, 
29682             manually : false
29683         };
29684         
29685         if(this.fireEvent('prepare', this, formData, options) != false){
29686             
29687             if(options.manually){
29688                 return;
29689             }
29690             
29691             this.xhr.send(formData);
29692             return;
29693         };
29694         
29695         this.uploadCancel();
29696     },
29697     
29698     uploadCancel : function()
29699     {
29700         if (this.xhr) {
29701             this.xhr.abort();
29702         }
29703         
29704         this.delegates = [];
29705         
29706         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29707             el.remove();
29708         }, this);
29709         
29710         this.arrange();
29711     },
29712     
29713     renderPreview : function(file)
29714     {
29715         if(typeof(file.target) != 'undefined' && file.target){
29716             return file;
29717         }
29718         
29719         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29720         
29721         var previewEl = this.managerEl.createChild({
29722             tag : 'div',
29723             cls : 'roo-document-manager-preview',
29724             cn : [
29725                 {
29726                     tag : 'div',
29727                     tooltip : file[this.toolTipName],
29728                     cls : 'roo-document-manager-thumb',
29729                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29730                 },
29731                 {
29732                     tag : 'button',
29733                     cls : 'close',
29734                     html : '<i class="fa fa-times-circle"></i>'
29735                 }
29736             ]
29737         });
29738
29739         var close = previewEl.select('button.close', true).first();
29740
29741         close.on('click', this.onRemove, this, file);
29742
29743         file.target = previewEl;
29744
29745         var image = previewEl.select('img', true).first();
29746         
29747         var _this = this;
29748         
29749         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29750         
29751         image.on('click', this.onClick, this, file);
29752         
29753         this.fireEvent('previewrendered', this, file);
29754         
29755         return file;
29756         
29757     },
29758     
29759     onPreviewLoad : function(file, image)
29760     {
29761         if(typeof(file.target) == 'undefined' || !file.target){
29762             return;
29763         }
29764         
29765         var width = image.dom.naturalWidth || image.dom.width;
29766         var height = image.dom.naturalHeight || image.dom.height;
29767         
29768         if(!this.previewResize) {
29769             return;
29770         }
29771         
29772         if(width > height){
29773             file.target.addClass('wide');
29774             return;
29775         }
29776         
29777         file.target.addClass('tall');
29778         return;
29779         
29780     },
29781     
29782     uploadFromSource : function(file, crop)
29783     {
29784         this.xhr = new XMLHttpRequest();
29785         
29786         this.managerEl.createChild({
29787             tag : 'div',
29788             cls : 'roo-document-manager-loading',
29789             cn : [
29790                 {
29791                     tag : 'div',
29792                     tooltip : file.name,
29793                     cls : 'roo-document-manager-thumb',
29794                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29795                 }
29796             ]
29797
29798         });
29799
29800         this.xhr.open(this.method, this.url, true);
29801         
29802         var headers = {
29803             "Accept": "application/json",
29804             "Cache-Control": "no-cache",
29805             "X-Requested-With": "XMLHttpRequest"
29806         };
29807         
29808         for (var headerName in headers) {
29809             var headerValue = headers[headerName];
29810             if (headerValue) {
29811                 this.xhr.setRequestHeader(headerName, headerValue);
29812             }
29813         }
29814         
29815         var _this = this;
29816         
29817         this.xhr.onload = function()
29818         {
29819             _this.xhrOnLoad(_this.xhr);
29820         }
29821         
29822         this.xhr.onerror = function()
29823         {
29824             _this.xhrOnError(_this.xhr);
29825         }
29826         
29827         var formData = new FormData();
29828
29829         formData.append('returnHTML', 'NO');
29830         
29831         formData.append('crop', crop);
29832         
29833         if(typeof(file.filename) != 'undefined'){
29834             formData.append('filename', file.filename);
29835         }
29836         
29837         if(typeof(file.mimetype) != 'undefined'){
29838             formData.append('mimetype', file.mimetype);
29839         }
29840         
29841         Roo.log(formData);
29842         
29843         if(this.fireEvent('prepare', this, formData) != false){
29844             this.xhr.send(formData);
29845         };
29846     }
29847 });
29848
29849 /*
29850 * Licence: LGPL
29851 */
29852
29853 /**
29854  * @class Roo.bootstrap.DocumentViewer
29855  * @extends Roo.bootstrap.Component
29856  * Bootstrap DocumentViewer class
29857  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29858  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29859  * 
29860  * @constructor
29861  * Create a new DocumentViewer
29862  * @param {Object} config The config object
29863  */
29864
29865 Roo.bootstrap.DocumentViewer = function(config){
29866     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29867     
29868     this.addEvents({
29869         /**
29870          * @event initial
29871          * Fire after initEvent
29872          * @param {Roo.bootstrap.DocumentViewer} this
29873          */
29874         "initial" : true,
29875         /**
29876          * @event click
29877          * Fire after click
29878          * @param {Roo.bootstrap.DocumentViewer} this
29879          */
29880         "click" : true,
29881         /**
29882          * @event download
29883          * Fire after download button
29884          * @param {Roo.bootstrap.DocumentViewer} this
29885          */
29886         "download" : true,
29887         /**
29888          * @event trash
29889          * Fire after trash button
29890          * @param {Roo.bootstrap.DocumentViewer} this
29891          */
29892         "trash" : true
29893         
29894     });
29895 };
29896
29897 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29898     
29899     showDownload : true,
29900     
29901     showTrash : true,
29902     
29903     getAutoCreate : function()
29904     {
29905         var cfg = {
29906             tag : 'div',
29907             cls : 'roo-document-viewer',
29908             cn : [
29909                 {
29910                     tag : 'div',
29911                     cls : 'roo-document-viewer-body',
29912                     cn : [
29913                         {
29914                             tag : 'div',
29915                             cls : 'roo-document-viewer-thumb',
29916                             cn : [
29917                                 {
29918                                     tag : 'img',
29919                                     cls : 'roo-document-viewer-image'
29920                                 }
29921                             ]
29922                         }
29923                     ]
29924                 },
29925                 {
29926                     tag : 'div',
29927                     cls : 'roo-document-viewer-footer',
29928                     cn : {
29929                         tag : 'div',
29930                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29931                         cn : [
29932                             {
29933                                 tag : 'div',
29934                                 cls : 'btn-group roo-document-viewer-download',
29935                                 cn : [
29936                                     {
29937                                         tag : 'button',
29938                                         cls : 'btn btn-default',
29939                                         html : '<i class="fa fa-download"></i>'
29940                                     }
29941                                 ]
29942                             },
29943                             {
29944                                 tag : 'div',
29945                                 cls : 'btn-group roo-document-viewer-trash',
29946                                 cn : [
29947                                     {
29948                                         tag : 'button',
29949                                         cls : 'btn btn-default',
29950                                         html : '<i class="fa fa-trash"></i>'
29951                                     }
29952                                 ]
29953                             }
29954                         ]
29955                     }
29956                 }
29957             ]
29958         };
29959         
29960         return cfg;
29961     },
29962     
29963     initEvents : function()
29964     {
29965         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29966         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29967         
29968         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29969         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29970         
29971         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29972         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29973         
29974         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29975         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29976         
29977         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29978         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29979         
29980         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29981         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29982         
29983         this.bodyEl.on('click', this.onClick, this);
29984         this.downloadBtn.on('click', this.onDownload, this);
29985         this.trashBtn.on('click', this.onTrash, this);
29986         
29987         this.downloadBtn.hide();
29988         this.trashBtn.hide();
29989         
29990         if(this.showDownload){
29991             this.downloadBtn.show();
29992         }
29993         
29994         if(this.showTrash){
29995             this.trashBtn.show();
29996         }
29997         
29998         if(!this.showDownload && !this.showTrash) {
29999             this.footerEl.hide();
30000         }
30001         
30002     },
30003     
30004     initial : function()
30005     {
30006         this.fireEvent('initial', this);
30007         
30008     },
30009     
30010     onClick : function(e)
30011     {
30012         e.preventDefault();
30013         
30014         this.fireEvent('click', this);
30015     },
30016     
30017     onDownload : function(e)
30018     {
30019         e.preventDefault();
30020         
30021         this.fireEvent('download', this);
30022     },
30023     
30024     onTrash : function(e)
30025     {
30026         e.preventDefault();
30027         
30028         this.fireEvent('trash', this);
30029     }
30030     
30031 });
30032 /*
30033  * - LGPL
30034  *
30035  * nav progress bar
30036  * 
30037  */
30038
30039 /**
30040  * @class Roo.bootstrap.NavProgressBar
30041  * @extends Roo.bootstrap.Component
30042  * Bootstrap NavProgressBar class
30043  * 
30044  * @constructor
30045  * Create a new nav progress bar
30046  * @param {Object} config The config object
30047  */
30048
30049 Roo.bootstrap.NavProgressBar = function(config){
30050     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30051
30052     this.bullets = this.bullets || [];
30053    
30054 //    Roo.bootstrap.NavProgressBar.register(this);
30055      this.addEvents({
30056         /**
30057              * @event changed
30058              * Fires when the active item changes
30059              * @param {Roo.bootstrap.NavProgressBar} this
30060              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30061              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30062          */
30063         'changed': true
30064      });
30065     
30066 };
30067
30068 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30069     
30070     bullets : [],
30071     barItems : [],
30072     
30073     getAutoCreate : function()
30074     {
30075         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30076         
30077         cfg = {
30078             tag : 'div',
30079             cls : 'roo-navigation-bar-group',
30080             cn : [
30081                 {
30082                     tag : 'div',
30083                     cls : 'roo-navigation-top-bar'
30084                 },
30085                 {
30086                     tag : 'div',
30087                     cls : 'roo-navigation-bullets-bar',
30088                     cn : [
30089                         {
30090                             tag : 'ul',
30091                             cls : 'roo-navigation-bar'
30092                         }
30093                     ]
30094                 },
30095                 
30096                 {
30097                     tag : 'div',
30098                     cls : 'roo-navigation-bottom-bar'
30099                 }
30100             ]
30101             
30102         };
30103         
30104         return cfg;
30105         
30106     },
30107     
30108     initEvents: function() 
30109     {
30110         
30111     },
30112     
30113     onRender : function(ct, position) 
30114     {
30115         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30116         
30117         if(this.bullets.length){
30118             Roo.each(this.bullets, function(b){
30119                this.addItem(b);
30120             }, this);
30121         }
30122         
30123         this.format();
30124         
30125     },
30126     
30127     addItem : function(cfg)
30128     {
30129         var item = new Roo.bootstrap.NavProgressItem(cfg);
30130         
30131         item.parentId = this.id;
30132         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30133         
30134         if(cfg.html){
30135             var top = new Roo.bootstrap.Element({
30136                 tag : 'div',
30137                 cls : 'roo-navigation-bar-text'
30138             });
30139             
30140             var bottom = new Roo.bootstrap.Element({
30141                 tag : 'div',
30142                 cls : 'roo-navigation-bar-text'
30143             });
30144             
30145             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30146             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30147             
30148             var topText = new Roo.bootstrap.Element({
30149                 tag : 'span',
30150                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30151             });
30152             
30153             var bottomText = new Roo.bootstrap.Element({
30154                 tag : 'span',
30155                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30156             });
30157             
30158             topText.onRender(top.el, null);
30159             bottomText.onRender(bottom.el, null);
30160             
30161             item.topEl = top;
30162             item.bottomEl = bottom;
30163         }
30164         
30165         this.barItems.push(item);
30166         
30167         return item;
30168     },
30169     
30170     getActive : function()
30171     {
30172         var active = false;
30173         
30174         Roo.each(this.barItems, function(v){
30175             
30176             if (!v.isActive()) {
30177                 return;
30178             }
30179             
30180             active = v;
30181             return false;
30182             
30183         });
30184         
30185         return active;
30186     },
30187     
30188     setActiveItem : function(item)
30189     {
30190         var prev = false;
30191         
30192         Roo.each(this.barItems, function(v){
30193             if (v.rid == item.rid) {
30194                 return ;
30195             }
30196             
30197             if (v.isActive()) {
30198                 v.setActive(false);
30199                 prev = v;
30200             }
30201         });
30202
30203         item.setActive(true);
30204         
30205         this.fireEvent('changed', this, item, prev);
30206     },
30207     
30208     getBarItem: function(rid)
30209     {
30210         var ret = false;
30211         
30212         Roo.each(this.barItems, function(e) {
30213             if (e.rid != rid) {
30214                 return;
30215             }
30216             
30217             ret =  e;
30218             return false;
30219         });
30220         
30221         return ret;
30222     },
30223     
30224     indexOfItem : function(item)
30225     {
30226         var index = false;
30227         
30228         Roo.each(this.barItems, function(v, i){
30229             
30230             if (v.rid != item.rid) {
30231                 return;
30232             }
30233             
30234             index = i;
30235             return false
30236         });
30237         
30238         return index;
30239     },
30240     
30241     setActiveNext : function()
30242     {
30243         var i = this.indexOfItem(this.getActive());
30244         
30245         if (i > this.barItems.length) {
30246             return;
30247         }
30248         
30249         this.setActiveItem(this.barItems[i+1]);
30250     },
30251     
30252     setActivePrev : function()
30253     {
30254         var i = this.indexOfItem(this.getActive());
30255         
30256         if (i  < 1) {
30257             return;
30258         }
30259         
30260         this.setActiveItem(this.barItems[i-1]);
30261     },
30262     
30263     format : function()
30264     {
30265         if(!this.barItems.length){
30266             return;
30267         }
30268      
30269         var width = 100 / this.barItems.length;
30270         
30271         Roo.each(this.barItems, function(i){
30272             i.el.setStyle('width', width + '%');
30273             i.topEl.el.setStyle('width', width + '%');
30274             i.bottomEl.el.setStyle('width', width + '%');
30275         }, this);
30276         
30277     }
30278     
30279 });
30280 /*
30281  * - LGPL
30282  *
30283  * Nav Progress Item
30284  * 
30285  */
30286
30287 /**
30288  * @class Roo.bootstrap.NavProgressItem
30289  * @extends Roo.bootstrap.Component
30290  * Bootstrap NavProgressItem class
30291  * @cfg {String} rid the reference id
30292  * @cfg {Boolean} active (true|false) Is item active default false
30293  * @cfg {Boolean} disabled (true|false) Is item active default false
30294  * @cfg {String} html
30295  * @cfg {String} position (top|bottom) text position default bottom
30296  * @cfg {String} icon show icon instead of number
30297  * 
30298  * @constructor
30299  * Create a new NavProgressItem
30300  * @param {Object} config The config object
30301  */
30302 Roo.bootstrap.NavProgressItem = function(config){
30303     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30304     this.addEvents({
30305         // raw events
30306         /**
30307          * @event click
30308          * The raw click event for the entire grid.
30309          * @param {Roo.bootstrap.NavProgressItem} this
30310          * @param {Roo.EventObject} e
30311          */
30312         "click" : true
30313     });
30314    
30315 };
30316
30317 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30318     
30319     rid : '',
30320     active : false,
30321     disabled : false,
30322     html : '',
30323     position : 'bottom',
30324     icon : false,
30325     
30326     getAutoCreate : function()
30327     {
30328         var iconCls = 'roo-navigation-bar-item-icon';
30329         
30330         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30331         
30332         var cfg = {
30333             tag: 'li',
30334             cls: 'roo-navigation-bar-item',
30335             cn : [
30336                 {
30337                     tag : 'i',
30338                     cls : iconCls
30339                 }
30340             ]
30341         };
30342         
30343         if(this.active){
30344             cfg.cls += ' active';
30345         }
30346         if(this.disabled){
30347             cfg.cls += ' disabled';
30348         }
30349         
30350         return cfg;
30351     },
30352     
30353     disable : function()
30354     {
30355         this.setDisabled(true);
30356     },
30357     
30358     enable : function()
30359     {
30360         this.setDisabled(false);
30361     },
30362     
30363     initEvents: function() 
30364     {
30365         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30366         
30367         this.iconEl.on('click', this.onClick, this);
30368     },
30369     
30370     onClick : function(e)
30371     {
30372         e.preventDefault();
30373         
30374         if(this.disabled){
30375             return;
30376         }
30377         
30378         if(this.fireEvent('click', this, e) === false){
30379             return;
30380         };
30381         
30382         this.parent().setActiveItem(this);
30383     },
30384     
30385     isActive: function () 
30386     {
30387         return this.active;
30388     },
30389     
30390     setActive : function(state)
30391     {
30392         if(this.active == state){
30393             return;
30394         }
30395         
30396         this.active = state;
30397         
30398         if (state) {
30399             this.el.addClass('active');
30400             return;
30401         }
30402         
30403         this.el.removeClass('active');
30404         
30405         return;
30406     },
30407     
30408     setDisabled : function(state)
30409     {
30410         if(this.disabled == state){
30411             return;
30412         }
30413         
30414         this.disabled = state;
30415         
30416         if (state) {
30417             this.el.addClass('disabled');
30418             return;
30419         }
30420         
30421         this.el.removeClass('disabled');
30422     },
30423     
30424     tooltipEl : function()
30425     {
30426         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30427     }
30428 });
30429  
30430
30431  /*
30432  * - LGPL
30433  *
30434  * FieldLabel
30435  * 
30436  */
30437
30438 /**
30439  * @class Roo.bootstrap.FieldLabel
30440  * @extends Roo.bootstrap.Component
30441  * Bootstrap FieldLabel class
30442  * @cfg {String} html contents of the element
30443  * @cfg {String} tag tag of the element default label
30444  * @cfg {String} cls class of the element
30445  * @cfg {String} target label target 
30446  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30447  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30448  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30449  * @cfg {String} iconTooltip default "This field is required"
30450  * @cfg {String} indicatorpos (left|right) default left
30451  * 
30452  * @constructor
30453  * Create a new FieldLabel
30454  * @param {Object} config The config object
30455  */
30456
30457 Roo.bootstrap.FieldLabel = function(config){
30458     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30459     
30460     this.addEvents({
30461             /**
30462              * @event invalid
30463              * Fires after the field has been marked as invalid.
30464              * @param {Roo.form.FieldLabel} this
30465              * @param {String} msg The validation message
30466              */
30467             invalid : true,
30468             /**
30469              * @event valid
30470              * Fires after the field has been validated with no errors.
30471              * @param {Roo.form.FieldLabel} this
30472              */
30473             valid : true
30474         });
30475 };
30476
30477 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30478     
30479     tag: 'label',
30480     cls: '',
30481     html: '',
30482     target: '',
30483     allowBlank : true,
30484     invalidClass : 'has-warning',
30485     validClass : 'has-success',
30486     iconTooltip : 'This field is required',
30487     indicatorpos : 'left',
30488     
30489     getAutoCreate : function(){
30490         
30491         var cls = "";
30492         if (!this.allowBlank) {
30493             cls  = "visible";
30494         }
30495         
30496         var cfg = {
30497             tag : this.tag,
30498             cls : 'roo-bootstrap-field-label ' + this.cls,
30499             for : this.target,
30500             cn : [
30501                 {
30502                     tag : 'i',
30503                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30504                     tooltip : this.iconTooltip
30505                 },
30506                 {
30507                     tag : 'span',
30508                     html : this.html
30509                 }
30510             ] 
30511         };
30512         
30513         if(this.indicatorpos == 'right'){
30514             var cfg = {
30515                 tag : this.tag,
30516                 cls : 'roo-bootstrap-field-label ' + this.cls,
30517                 for : this.target,
30518                 cn : [
30519                     {
30520                         tag : 'span',
30521                         html : this.html
30522                     },
30523                     {
30524                         tag : 'i',
30525                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30526                         tooltip : this.iconTooltip
30527                     }
30528                 ] 
30529             };
30530         }
30531         
30532         return cfg;
30533     },
30534     
30535     initEvents: function() 
30536     {
30537         Roo.bootstrap.Element.superclass.initEvents.call(this);
30538         
30539         this.indicator = this.indicatorEl();
30540         
30541         if(this.indicator){
30542             this.indicator.removeClass('visible');
30543             this.indicator.addClass('invisible');
30544         }
30545         
30546         Roo.bootstrap.FieldLabel.register(this);
30547     },
30548     
30549     indicatorEl : function()
30550     {
30551         var indicator = this.el.select('i.roo-required-indicator',true).first();
30552         
30553         if(!indicator){
30554             return false;
30555         }
30556         
30557         return indicator;
30558         
30559     },
30560     
30561     /**
30562      * Mark this field as valid
30563      */
30564     markValid : function()
30565     {
30566         if(this.indicator){
30567             this.indicator.removeClass('visible');
30568             this.indicator.addClass('invisible');
30569         }
30570         if (Roo.bootstrap.version == 3) {
30571             this.el.removeClass(this.invalidClass);
30572             this.el.addClass(this.validClass);
30573         } else {
30574             this.el.removeClass('is-invalid');
30575             this.el.addClass('is-valid');
30576         }
30577         
30578         
30579         this.fireEvent('valid', this);
30580     },
30581     
30582     /**
30583      * Mark this field as invalid
30584      * @param {String} msg The validation message
30585      */
30586     markInvalid : function(msg)
30587     {
30588         if(this.indicator){
30589             this.indicator.removeClass('invisible');
30590             this.indicator.addClass('visible');
30591         }
30592           if (Roo.bootstrap.version == 3) {
30593             this.el.removeClass(this.validClass);
30594             this.el.addClass(this.invalidClass);
30595         } else {
30596             this.el.removeClass('is-valid');
30597             this.el.addClass('is-invalid');
30598         }
30599         
30600         
30601         this.fireEvent('invalid', this, msg);
30602     }
30603     
30604    
30605 });
30606
30607 Roo.apply(Roo.bootstrap.FieldLabel, {
30608     
30609     groups: {},
30610     
30611      /**
30612     * register a FieldLabel Group
30613     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30614     */
30615     register : function(label)
30616     {
30617         if(this.groups.hasOwnProperty(label.target)){
30618             return;
30619         }
30620      
30621         this.groups[label.target] = label;
30622         
30623     },
30624     /**
30625     * fetch a FieldLabel Group based on the target
30626     * @param {string} target
30627     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30628     */
30629     get: function(target) {
30630         if (typeof(this.groups[target]) == 'undefined') {
30631             return false;
30632         }
30633         
30634         return this.groups[target] ;
30635     }
30636 });
30637
30638  
30639
30640  /*
30641  * - LGPL
30642  *
30643  * page DateSplitField.
30644  * 
30645  */
30646
30647
30648 /**
30649  * @class Roo.bootstrap.DateSplitField
30650  * @extends Roo.bootstrap.Component
30651  * Bootstrap DateSplitField class
30652  * @cfg {string} fieldLabel - the label associated
30653  * @cfg {Number} labelWidth set the width of label (0-12)
30654  * @cfg {String} labelAlign (top|left)
30655  * @cfg {Boolean} dayAllowBlank (true|false) default false
30656  * @cfg {Boolean} monthAllowBlank (true|false) default false
30657  * @cfg {Boolean} yearAllowBlank (true|false) default false
30658  * @cfg {string} dayPlaceholder 
30659  * @cfg {string} monthPlaceholder
30660  * @cfg {string} yearPlaceholder
30661  * @cfg {string} dayFormat default 'd'
30662  * @cfg {string} monthFormat default 'm'
30663  * @cfg {string} yearFormat default 'Y'
30664  * @cfg {Number} labellg set the width of label (1-12)
30665  * @cfg {Number} labelmd set the width of label (1-12)
30666  * @cfg {Number} labelsm set the width of label (1-12)
30667  * @cfg {Number} labelxs set the width of label (1-12)
30668
30669  *     
30670  * @constructor
30671  * Create a new DateSplitField
30672  * @param {Object} config The config object
30673  */
30674
30675 Roo.bootstrap.DateSplitField = function(config){
30676     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30677     
30678     this.addEvents({
30679         // raw events
30680          /**
30681          * @event years
30682          * getting the data of years
30683          * @param {Roo.bootstrap.DateSplitField} this
30684          * @param {Object} years
30685          */
30686         "years" : true,
30687         /**
30688          * @event days
30689          * getting the data of days
30690          * @param {Roo.bootstrap.DateSplitField} this
30691          * @param {Object} days
30692          */
30693         "days" : true,
30694         /**
30695          * @event invalid
30696          * Fires after the field has been marked as invalid.
30697          * @param {Roo.form.Field} this
30698          * @param {String} msg The validation message
30699          */
30700         invalid : true,
30701        /**
30702          * @event valid
30703          * Fires after the field has been validated with no errors.
30704          * @param {Roo.form.Field} this
30705          */
30706         valid : true
30707     });
30708 };
30709
30710 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30711     
30712     fieldLabel : '',
30713     labelAlign : 'top',
30714     labelWidth : 3,
30715     dayAllowBlank : false,
30716     monthAllowBlank : false,
30717     yearAllowBlank : false,
30718     dayPlaceholder : '',
30719     monthPlaceholder : '',
30720     yearPlaceholder : '',
30721     dayFormat : 'd',
30722     monthFormat : 'm',
30723     yearFormat : 'Y',
30724     isFormField : true,
30725     labellg : 0,
30726     labelmd : 0,
30727     labelsm : 0,
30728     labelxs : 0,
30729     
30730     getAutoCreate : function()
30731     {
30732         var cfg = {
30733             tag : 'div',
30734             cls : 'row roo-date-split-field-group',
30735             cn : [
30736                 {
30737                     tag : 'input',
30738                     type : 'hidden',
30739                     cls : 'form-hidden-field roo-date-split-field-group-value',
30740                     name : this.name
30741                 }
30742             ]
30743         };
30744         
30745         var labelCls = 'col-md-12';
30746         var contentCls = 'col-md-4';
30747         
30748         if(this.fieldLabel){
30749             
30750             var label = {
30751                 tag : 'div',
30752                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30753                 cn : [
30754                     {
30755                         tag : 'label',
30756                         html : this.fieldLabel
30757                     }
30758                 ]
30759             };
30760             
30761             if(this.labelAlign == 'left'){
30762             
30763                 if(this.labelWidth > 12){
30764                     label.style = "width: " + this.labelWidth + 'px';
30765                 }
30766
30767                 if(this.labelWidth < 13 && this.labelmd == 0){
30768                     this.labelmd = this.labelWidth;
30769                 }
30770
30771                 if(this.labellg > 0){
30772                     labelCls = ' col-lg-' + this.labellg;
30773                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30774                 }
30775
30776                 if(this.labelmd > 0){
30777                     labelCls = ' col-md-' + this.labelmd;
30778                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30779                 }
30780
30781                 if(this.labelsm > 0){
30782                     labelCls = ' col-sm-' + this.labelsm;
30783                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30784                 }
30785
30786                 if(this.labelxs > 0){
30787                     labelCls = ' col-xs-' + this.labelxs;
30788                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30789                 }
30790             }
30791             
30792             label.cls += ' ' + labelCls;
30793             
30794             cfg.cn.push(label);
30795         }
30796         
30797         Roo.each(['day', 'month', 'year'], function(t){
30798             cfg.cn.push({
30799                 tag : 'div',
30800                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30801             });
30802         }, this);
30803         
30804         return cfg;
30805     },
30806     
30807     inputEl: function ()
30808     {
30809         return this.el.select('.roo-date-split-field-group-value', true).first();
30810     },
30811     
30812     onRender : function(ct, position) 
30813     {
30814         var _this = this;
30815         
30816         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30817         
30818         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30819         
30820         this.dayField = new Roo.bootstrap.ComboBox({
30821             allowBlank : this.dayAllowBlank,
30822             alwaysQuery : true,
30823             displayField : 'value',
30824             editable : false,
30825             fieldLabel : '',
30826             forceSelection : true,
30827             mode : 'local',
30828             placeholder : this.dayPlaceholder,
30829             selectOnFocus : true,
30830             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30831             triggerAction : 'all',
30832             typeAhead : true,
30833             valueField : 'value',
30834             store : new Roo.data.SimpleStore({
30835                 data : (function() {    
30836                     var days = [];
30837                     _this.fireEvent('days', _this, days);
30838                     return days;
30839                 })(),
30840                 fields : [ 'value' ]
30841             }),
30842             listeners : {
30843                 select : function (_self, record, index)
30844                 {
30845                     _this.setValue(_this.getValue());
30846                 }
30847             }
30848         });
30849
30850         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30851         
30852         this.monthField = new Roo.bootstrap.MonthField({
30853             after : '<i class=\"fa fa-calendar\"></i>',
30854             allowBlank : this.monthAllowBlank,
30855             placeholder : this.monthPlaceholder,
30856             readOnly : true,
30857             listeners : {
30858                 render : function (_self)
30859                 {
30860                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30861                         e.preventDefault();
30862                         _self.focus();
30863                     });
30864                 },
30865                 select : function (_self, oldvalue, newvalue)
30866                 {
30867                     _this.setValue(_this.getValue());
30868                 }
30869             }
30870         });
30871         
30872         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30873         
30874         this.yearField = new Roo.bootstrap.ComboBox({
30875             allowBlank : this.yearAllowBlank,
30876             alwaysQuery : true,
30877             displayField : 'value',
30878             editable : false,
30879             fieldLabel : '',
30880             forceSelection : true,
30881             mode : 'local',
30882             placeholder : this.yearPlaceholder,
30883             selectOnFocus : true,
30884             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30885             triggerAction : 'all',
30886             typeAhead : true,
30887             valueField : 'value',
30888             store : new Roo.data.SimpleStore({
30889                 data : (function() {
30890                     var years = [];
30891                     _this.fireEvent('years', _this, years);
30892                     return years;
30893                 })(),
30894                 fields : [ 'value' ]
30895             }),
30896             listeners : {
30897                 select : function (_self, record, index)
30898                 {
30899                     _this.setValue(_this.getValue());
30900                 }
30901             }
30902         });
30903
30904         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30905     },
30906     
30907     setValue : function(v, format)
30908     {
30909         this.inputEl.dom.value = v;
30910         
30911         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30912         
30913         var d = Date.parseDate(v, f);
30914         
30915         if(!d){
30916             this.validate();
30917             return;
30918         }
30919         
30920         this.setDay(d.format(this.dayFormat));
30921         this.setMonth(d.format(this.monthFormat));
30922         this.setYear(d.format(this.yearFormat));
30923         
30924         this.validate();
30925         
30926         return;
30927     },
30928     
30929     setDay : function(v)
30930     {
30931         this.dayField.setValue(v);
30932         this.inputEl.dom.value = this.getValue();
30933         this.validate();
30934         return;
30935     },
30936     
30937     setMonth : function(v)
30938     {
30939         this.monthField.setValue(v, true);
30940         this.inputEl.dom.value = this.getValue();
30941         this.validate();
30942         return;
30943     },
30944     
30945     setYear : function(v)
30946     {
30947         this.yearField.setValue(v);
30948         this.inputEl.dom.value = this.getValue();
30949         this.validate();
30950         return;
30951     },
30952     
30953     getDay : function()
30954     {
30955         return this.dayField.getValue();
30956     },
30957     
30958     getMonth : function()
30959     {
30960         return this.monthField.getValue();
30961     },
30962     
30963     getYear : function()
30964     {
30965         return this.yearField.getValue();
30966     },
30967     
30968     getValue : function()
30969     {
30970         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30971         
30972         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30973         
30974         return date;
30975     },
30976     
30977     reset : function()
30978     {
30979         this.setDay('');
30980         this.setMonth('');
30981         this.setYear('');
30982         this.inputEl.dom.value = '';
30983         this.validate();
30984         return;
30985     },
30986     
30987     validate : function()
30988     {
30989         var d = this.dayField.validate();
30990         var m = this.monthField.validate();
30991         var y = this.yearField.validate();
30992         
30993         var valid = true;
30994         
30995         if(
30996                 (!this.dayAllowBlank && !d) ||
30997                 (!this.monthAllowBlank && !m) ||
30998                 (!this.yearAllowBlank && !y)
30999         ){
31000             valid = false;
31001         }
31002         
31003         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31004             return valid;
31005         }
31006         
31007         if(valid){
31008             this.markValid();
31009             return valid;
31010         }
31011         
31012         this.markInvalid();
31013         
31014         return valid;
31015     },
31016     
31017     markValid : function()
31018     {
31019         
31020         var label = this.el.select('label', true).first();
31021         var icon = this.el.select('i.fa-star', true).first();
31022
31023         if(label && icon){
31024             icon.remove();
31025         }
31026         
31027         this.fireEvent('valid', this);
31028     },
31029     
31030      /**
31031      * Mark this field as invalid
31032      * @param {String} msg The validation message
31033      */
31034     markInvalid : function(msg)
31035     {
31036         
31037         var label = this.el.select('label', true).first();
31038         var icon = this.el.select('i.fa-star', true).first();
31039
31040         if(label && !icon){
31041             this.el.select('.roo-date-split-field-label', true).createChild({
31042                 tag : 'i',
31043                 cls : 'text-danger fa fa-lg fa-star',
31044                 tooltip : 'This field is required',
31045                 style : 'margin-right:5px;'
31046             }, label, true);
31047         }
31048         
31049         this.fireEvent('invalid', this, msg);
31050     },
31051     
31052     clearInvalid : function()
31053     {
31054         var label = this.el.select('label', true).first();
31055         var icon = this.el.select('i.fa-star', true).first();
31056
31057         if(label && icon){
31058             icon.remove();
31059         }
31060         
31061         this.fireEvent('valid', this);
31062     },
31063     
31064     getName: function()
31065     {
31066         return this.name;
31067     }
31068     
31069 });
31070
31071  /**
31072  *
31073  * This is based on 
31074  * http://masonry.desandro.com
31075  *
31076  * The idea is to render all the bricks based on vertical width...
31077  *
31078  * The original code extends 'outlayer' - we might need to use that....
31079  * 
31080  */
31081
31082
31083 /**
31084  * @class Roo.bootstrap.LayoutMasonry
31085  * @extends Roo.bootstrap.Component
31086  * Bootstrap Layout Masonry class
31087  * 
31088  * @constructor
31089  * Create a new Element
31090  * @param {Object} config The config object
31091  */
31092
31093 Roo.bootstrap.LayoutMasonry = function(config){
31094     
31095     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31096     
31097     this.bricks = [];
31098     
31099     Roo.bootstrap.LayoutMasonry.register(this);
31100     
31101     this.addEvents({
31102         // raw events
31103         /**
31104          * @event layout
31105          * Fire after layout the items
31106          * @param {Roo.bootstrap.LayoutMasonry} this
31107          * @param {Roo.EventObject} e
31108          */
31109         "layout" : true
31110     });
31111     
31112 };
31113
31114 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31115     
31116     /**
31117      * @cfg {Boolean} isLayoutInstant = no animation?
31118      */   
31119     isLayoutInstant : false, // needed?
31120    
31121     /**
31122      * @cfg {Number} boxWidth  width of the columns
31123      */   
31124     boxWidth : 450,
31125     
31126       /**
31127      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31128      */   
31129     boxHeight : 0,
31130     
31131     /**
31132      * @cfg {Number} padWidth padding below box..
31133      */   
31134     padWidth : 10, 
31135     
31136     /**
31137      * @cfg {Number} gutter gutter width..
31138      */   
31139     gutter : 10,
31140     
31141      /**
31142      * @cfg {Number} maxCols maximum number of columns
31143      */   
31144     
31145     maxCols: 0,
31146     
31147     /**
31148      * @cfg {Boolean} isAutoInitial defalut true
31149      */   
31150     isAutoInitial : true, 
31151     
31152     containerWidth: 0,
31153     
31154     /**
31155      * @cfg {Boolean} isHorizontal defalut false
31156      */   
31157     isHorizontal : false, 
31158
31159     currentSize : null,
31160     
31161     tag: 'div',
31162     
31163     cls: '',
31164     
31165     bricks: null, //CompositeElement
31166     
31167     cols : 1,
31168     
31169     _isLayoutInited : false,
31170     
31171 //    isAlternative : false, // only use for vertical layout...
31172     
31173     /**
31174      * @cfg {Number} alternativePadWidth padding below box..
31175      */   
31176     alternativePadWidth : 50,
31177     
31178     selectedBrick : [],
31179     
31180     getAutoCreate : function(){
31181         
31182         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31183         
31184         var cfg = {
31185             tag: this.tag,
31186             cls: 'blog-masonary-wrapper ' + this.cls,
31187             cn : {
31188                 cls : 'mas-boxes masonary'
31189             }
31190         };
31191         
31192         return cfg;
31193     },
31194     
31195     getChildContainer: function( )
31196     {
31197         if (this.boxesEl) {
31198             return this.boxesEl;
31199         }
31200         
31201         this.boxesEl = this.el.select('.mas-boxes').first();
31202         
31203         return this.boxesEl;
31204     },
31205     
31206     
31207     initEvents : function()
31208     {
31209         var _this = this;
31210         
31211         if(this.isAutoInitial){
31212             Roo.log('hook children rendered');
31213             this.on('childrenrendered', function() {
31214                 Roo.log('children rendered');
31215                 _this.initial();
31216             } ,this);
31217         }
31218     },
31219     
31220     initial : function()
31221     {
31222         this.selectedBrick = [];
31223         
31224         this.currentSize = this.el.getBox(true);
31225         
31226         Roo.EventManager.onWindowResize(this.resize, this); 
31227
31228         if(!this.isAutoInitial){
31229             this.layout();
31230             return;
31231         }
31232         
31233         this.layout();
31234         
31235         return;
31236         //this.layout.defer(500,this);
31237         
31238     },
31239     
31240     resize : function()
31241     {
31242         var cs = this.el.getBox(true);
31243         
31244         if (
31245                 this.currentSize.width == cs.width && 
31246                 this.currentSize.x == cs.x && 
31247                 this.currentSize.height == cs.height && 
31248                 this.currentSize.y == cs.y 
31249         ) {
31250             Roo.log("no change in with or X or Y");
31251             return;
31252         }
31253         
31254         this.currentSize = cs;
31255         
31256         this.layout();
31257         
31258     },
31259     
31260     layout : function()
31261     {   
31262         this._resetLayout();
31263         
31264         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31265         
31266         this.layoutItems( isInstant );
31267       
31268         this._isLayoutInited = true;
31269         
31270         this.fireEvent('layout', this);
31271         
31272     },
31273     
31274     _resetLayout : function()
31275     {
31276         if(this.isHorizontal){
31277             this.horizontalMeasureColumns();
31278             return;
31279         }
31280         
31281         this.verticalMeasureColumns();
31282         
31283     },
31284     
31285     verticalMeasureColumns : function()
31286     {
31287         this.getContainerWidth();
31288         
31289 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31290 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31291 //            return;
31292 //        }
31293         
31294         var boxWidth = this.boxWidth + this.padWidth;
31295         
31296         if(this.containerWidth < this.boxWidth){
31297             boxWidth = this.containerWidth
31298         }
31299         
31300         var containerWidth = this.containerWidth;
31301         
31302         var cols = Math.floor(containerWidth / boxWidth);
31303         
31304         this.cols = Math.max( cols, 1 );
31305         
31306         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31307         
31308         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31309         
31310         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31311         
31312         this.colWidth = boxWidth + avail - this.padWidth;
31313         
31314         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31315         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31316     },
31317     
31318     horizontalMeasureColumns : function()
31319     {
31320         this.getContainerWidth();
31321         
31322         var boxWidth = this.boxWidth;
31323         
31324         if(this.containerWidth < boxWidth){
31325             boxWidth = this.containerWidth;
31326         }
31327         
31328         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31329         
31330         this.el.setHeight(boxWidth);
31331         
31332     },
31333     
31334     getContainerWidth : function()
31335     {
31336         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31337     },
31338     
31339     layoutItems : function( isInstant )
31340     {
31341         Roo.log(this.bricks);
31342         
31343         var items = Roo.apply([], this.bricks);
31344         
31345         if(this.isHorizontal){
31346             this._horizontalLayoutItems( items , isInstant );
31347             return;
31348         }
31349         
31350 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31351 //            this._verticalAlternativeLayoutItems( items , isInstant );
31352 //            return;
31353 //        }
31354         
31355         this._verticalLayoutItems( items , isInstant );
31356         
31357     },
31358     
31359     _verticalLayoutItems : function ( items , isInstant)
31360     {
31361         if ( !items || !items.length ) {
31362             return;
31363         }
31364         
31365         var standard = [
31366             ['xs', 'xs', 'xs', 'tall'],
31367             ['xs', 'xs', 'tall'],
31368             ['xs', 'xs', 'sm'],
31369             ['xs', 'xs', 'xs'],
31370             ['xs', 'tall'],
31371             ['xs', 'sm'],
31372             ['xs', 'xs'],
31373             ['xs'],
31374             
31375             ['sm', 'xs', 'xs'],
31376             ['sm', 'xs'],
31377             ['sm'],
31378             
31379             ['tall', 'xs', 'xs', 'xs'],
31380             ['tall', 'xs', 'xs'],
31381             ['tall', 'xs'],
31382             ['tall']
31383             
31384         ];
31385         
31386         var queue = [];
31387         
31388         var boxes = [];
31389         
31390         var box = [];
31391         
31392         Roo.each(items, function(item, k){
31393             
31394             switch (item.size) {
31395                 // these layouts take up a full box,
31396                 case 'md' :
31397                 case 'md-left' :
31398                 case 'md-right' :
31399                 case 'wide' :
31400                     
31401                     if(box.length){
31402                         boxes.push(box);
31403                         box = [];
31404                     }
31405                     
31406                     boxes.push([item]);
31407                     
31408                     break;
31409                     
31410                 case 'xs' :
31411                 case 'sm' :
31412                 case 'tall' :
31413                     
31414                     box.push(item);
31415                     
31416                     break;
31417                 default :
31418                     break;
31419                     
31420             }
31421             
31422         }, this);
31423         
31424         if(box.length){
31425             boxes.push(box);
31426             box = [];
31427         }
31428         
31429         var filterPattern = function(box, length)
31430         {
31431             if(!box.length){
31432                 return;
31433             }
31434             
31435             var match = false;
31436             
31437             var pattern = box.slice(0, length);
31438             
31439             var format = [];
31440             
31441             Roo.each(pattern, function(i){
31442                 format.push(i.size);
31443             }, this);
31444             
31445             Roo.each(standard, function(s){
31446                 
31447                 if(String(s) != String(format)){
31448                     return;
31449                 }
31450                 
31451                 match = true;
31452                 return false;
31453                 
31454             }, this);
31455             
31456             if(!match && length == 1){
31457                 return;
31458             }
31459             
31460             if(!match){
31461                 filterPattern(box, length - 1);
31462                 return;
31463             }
31464                 
31465             queue.push(pattern);
31466
31467             box = box.slice(length, box.length);
31468
31469             filterPattern(box, 4);
31470
31471             return;
31472             
31473         }
31474         
31475         Roo.each(boxes, function(box, k){
31476             
31477             if(!box.length){
31478                 return;
31479             }
31480             
31481             if(box.length == 1){
31482                 queue.push(box);
31483                 return;
31484             }
31485             
31486             filterPattern(box, 4);
31487             
31488         }, this);
31489         
31490         this._processVerticalLayoutQueue( queue, isInstant );
31491         
31492     },
31493     
31494 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31495 //    {
31496 //        if ( !items || !items.length ) {
31497 //            return;
31498 //        }
31499 //
31500 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31501 //        
31502 //    },
31503     
31504     _horizontalLayoutItems : function ( items , isInstant)
31505     {
31506         if ( !items || !items.length || items.length < 3) {
31507             return;
31508         }
31509         
31510         items.reverse();
31511         
31512         var eItems = items.slice(0, 3);
31513         
31514         items = items.slice(3, items.length);
31515         
31516         var standard = [
31517             ['xs', 'xs', 'xs', 'wide'],
31518             ['xs', 'xs', 'wide'],
31519             ['xs', 'xs', 'sm'],
31520             ['xs', 'xs', 'xs'],
31521             ['xs', 'wide'],
31522             ['xs', 'sm'],
31523             ['xs', 'xs'],
31524             ['xs'],
31525             
31526             ['sm', 'xs', 'xs'],
31527             ['sm', 'xs'],
31528             ['sm'],
31529             
31530             ['wide', 'xs', 'xs', 'xs'],
31531             ['wide', 'xs', 'xs'],
31532             ['wide', 'xs'],
31533             ['wide'],
31534             
31535             ['wide-thin']
31536         ];
31537         
31538         var queue = [];
31539         
31540         var boxes = [];
31541         
31542         var box = [];
31543         
31544         Roo.each(items, function(item, k){
31545             
31546             switch (item.size) {
31547                 case 'md' :
31548                 case 'md-left' :
31549                 case 'md-right' :
31550                 case 'tall' :
31551                     
31552                     if(box.length){
31553                         boxes.push(box);
31554                         box = [];
31555                     }
31556                     
31557                     boxes.push([item]);
31558                     
31559                     break;
31560                     
31561                 case 'xs' :
31562                 case 'sm' :
31563                 case 'wide' :
31564                 case 'wide-thin' :
31565                     
31566                     box.push(item);
31567                     
31568                     break;
31569                 default :
31570                     break;
31571                     
31572             }
31573             
31574         }, this);
31575         
31576         if(box.length){
31577             boxes.push(box);
31578             box = [];
31579         }
31580         
31581         var filterPattern = function(box, length)
31582         {
31583             if(!box.length){
31584                 return;
31585             }
31586             
31587             var match = false;
31588             
31589             var pattern = box.slice(0, length);
31590             
31591             var format = [];
31592             
31593             Roo.each(pattern, function(i){
31594                 format.push(i.size);
31595             }, this);
31596             
31597             Roo.each(standard, function(s){
31598                 
31599                 if(String(s) != String(format)){
31600                     return;
31601                 }
31602                 
31603                 match = true;
31604                 return false;
31605                 
31606             }, this);
31607             
31608             if(!match && length == 1){
31609                 return;
31610             }
31611             
31612             if(!match){
31613                 filterPattern(box, length - 1);
31614                 return;
31615             }
31616                 
31617             queue.push(pattern);
31618
31619             box = box.slice(length, box.length);
31620
31621             filterPattern(box, 4);
31622
31623             return;
31624             
31625         }
31626         
31627         Roo.each(boxes, function(box, k){
31628             
31629             if(!box.length){
31630                 return;
31631             }
31632             
31633             if(box.length == 1){
31634                 queue.push(box);
31635                 return;
31636             }
31637             
31638             filterPattern(box, 4);
31639             
31640         }, this);
31641         
31642         
31643         var prune = [];
31644         
31645         var pos = this.el.getBox(true);
31646         
31647         var minX = pos.x;
31648         
31649         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31650         
31651         var hit_end = false;
31652         
31653         Roo.each(queue, function(box){
31654             
31655             if(hit_end){
31656                 
31657                 Roo.each(box, function(b){
31658                 
31659                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31660                     b.el.hide();
31661
31662                 }, this);
31663
31664                 return;
31665             }
31666             
31667             var mx = 0;
31668             
31669             Roo.each(box, function(b){
31670                 
31671                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31672                 b.el.show();
31673
31674                 mx = Math.max(mx, b.x);
31675                 
31676             }, this);
31677             
31678             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31679             
31680             if(maxX < minX){
31681                 
31682                 Roo.each(box, function(b){
31683                 
31684                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31685                     b.el.hide();
31686                     
31687                 }, this);
31688                 
31689                 hit_end = true;
31690                 
31691                 return;
31692             }
31693             
31694             prune.push(box);
31695             
31696         }, this);
31697         
31698         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31699     },
31700     
31701     /** Sets position of item in DOM
31702     * @param {Element} item
31703     * @param {Number} x - horizontal position
31704     * @param {Number} y - vertical position
31705     * @param {Boolean} isInstant - disables transitions
31706     */
31707     _processVerticalLayoutQueue : function( queue, isInstant )
31708     {
31709         var pos = this.el.getBox(true);
31710         var x = pos.x;
31711         var y = pos.y;
31712         var maxY = [];
31713         
31714         for (var i = 0; i < this.cols; i++){
31715             maxY[i] = pos.y;
31716         }
31717         
31718         Roo.each(queue, function(box, k){
31719             
31720             var col = k % this.cols;
31721             
31722             Roo.each(box, function(b,kk){
31723                 
31724                 b.el.position('absolute');
31725                 
31726                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31727                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31728                 
31729                 if(b.size == 'md-left' || b.size == 'md-right'){
31730                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31731                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31732                 }
31733                 
31734                 b.el.setWidth(width);
31735                 b.el.setHeight(height);
31736                 // iframe?
31737                 b.el.select('iframe',true).setSize(width,height);
31738                 
31739             }, this);
31740             
31741             for (var i = 0; i < this.cols; i++){
31742                 
31743                 if(maxY[i] < maxY[col]){
31744                     col = i;
31745                     continue;
31746                 }
31747                 
31748                 col = Math.min(col, i);
31749                 
31750             }
31751             
31752             x = pos.x + col * (this.colWidth + this.padWidth);
31753             
31754             y = maxY[col];
31755             
31756             var positions = [];
31757             
31758             switch (box.length){
31759                 case 1 :
31760                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31761                     break;
31762                 case 2 :
31763                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31764                     break;
31765                 case 3 :
31766                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31767                     break;
31768                 case 4 :
31769                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31770                     break;
31771                 default :
31772                     break;
31773             }
31774             
31775             Roo.each(box, function(b,kk){
31776                 
31777                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31778                 
31779                 var sz = b.el.getSize();
31780                 
31781                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31782                 
31783             }, this);
31784             
31785         }, this);
31786         
31787         var mY = 0;
31788         
31789         for (var i = 0; i < this.cols; i++){
31790             mY = Math.max(mY, maxY[i]);
31791         }
31792         
31793         this.el.setHeight(mY - pos.y);
31794         
31795     },
31796     
31797 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31798 //    {
31799 //        var pos = this.el.getBox(true);
31800 //        var x = pos.x;
31801 //        var y = pos.y;
31802 //        var maxX = pos.right;
31803 //        
31804 //        var maxHeight = 0;
31805 //        
31806 //        Roo.each(items, function(item, k){
31807 //            
31808 //            var c = k % 2;
31809 //            
31810 //            item.el.position('absolute');
31811 //                
31812 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31813 //
31814 //            item.el.setWidth(width);
31815 //
31816 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31817 //
31818 //            item.el.setHeight(height);
31819 //            
31820 //            if(c == 0){
31821 //                item.el.setXY([x, y], isInstant ? false : true);
31822 //            } else {
31823 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31824 //            }
31825 //            
31826 //            y = y + height + this.alternativePadWidth;
31827 //            
31828 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31829 //            
31830 //        }, this);
31831 //        
31832 //        this.el.setHeight(maxHeight);
31833 //        
31834 //    },
31835     
31836     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31837     {
31838         var pos = this.el.getBox(true);
31839         
31840         var minX = pos.x;
31841         var minY = pos.y;
31842         
31843         var maxX = pos.right;
31844         
31845         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31846         
31847         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31848         
31849         Roo.each(queue, function(box, k){
31850             
31851             Roo.each(box, function(b, kk){
31852                 
31853                 b.el.position('absolute');
31854                 
31855                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31856                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31857                 
31858                 if(b.size == 'md-left' || b.size == 'md-right'){
31859                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31860                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31861                 }
31862                 
31863                 b.el.setWidth(width);
31864                 b.el.setHeight(height);
31865                 
31866             }, this);
31867             
31868             if(!box.length){
31869                 return;
31870             }
31871             
31872             var positions = [];
31873             
31874             switch (box.length){
31875                 case 1 :
31876                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31877                     break;
31878                 case 2 :
31879                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31880                     break;
31881                 case 3 :
31882                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31883                     break;
31884                 case 4 :
31885                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31886                     break;
31887                 default :
31888                     break;
31889             }
31890             
31891             Roo.each(box, function(b,kk){
31892                 
31893                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31894                 
31895                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31896                 
31897             }, this);
31898             
31899         }, this);
31900         
31901     },
31902     
31903     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31904     {
31905         Roo.each(eItems, function(b,k){
31906             
31907             b.size = (k == 0) ? 'sm' : 'xs';
31908             b.x = (k == 0) ? 2 : 1;
31909             b.y = (k == 0) ? 2 : 1;
31910             
31911             b.el.position('absolute');
31912             
31913             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31914                 
31915             b.el.setWidth(width);
31916             
31917             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31918             
31919             b.el.setHeight(height);
31920             
31921         }, this);
31922
31923         var positions = [];
31924         
31925         positions.push({
31926             x : maxX - this.unitWidth * 2 - this.gutter,
31927             y : minY
31928         });
31929         
31930         positions.push({
31931             x : maxX - this.unitWidth,
31932             y : minY + (this.unitWidth + this.gutter) * 2
31933         });
31934         
31935         positions.push({
31936             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31937             y : minY
31938         });
31939         
31940         Roo.each(eItems, function(b,k){
31941             
31942             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31943
31944         }, this);
31945         
31946     },
31947     
31948     getVerticalOneBoxColPositions : function(x, y, box)
31949     {
31950         var pos = [];
31951         
31952         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31953         
31954         if(box[0].size == 'md-left'){
31955             rand = 0;
31956         }
31957         
31958         if(box[0].size == 'md-right'){
31959             rand = 1;
31960         }
31961         
31962         pos.push({
31963             x : x + (this.unitWidth + this.gutter) * rand,
31964             y : y
31965         });
31966         
31967         return pos;
31968     },
31969     
31970     getVerticalTwoBoxColPositions : function(x, y, box)
31971     {
31972         var pos = [];
31973         
31974         if(box[0].size == 'xs'){
31975             
31976             pos.push({
31977                 x : x,
31978                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31979             });
31980
31981             pos.push({
31982                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
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 + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31998         });
31999         
32000         return pos;
32001         
32002     },
32003     
32004     getVerticalThreeBoxColPositions : function(x, y, box)
32005     {
32006         var pos = [];
32007         
32008         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32009             
32010             pos.push({
32011                 x : x,
32012                 y : y
32013             });
32014
32015             pos.push({
32016                 x : x + (this.unitWidth + this.gutter) * 1,
32017                 y : y
32018             });
32019             
32020             pos.push({
32021                 x : x + (this.unitWidth + this.gutter) * 2,
32022                 y : y
32023             });
32024             
32025             return pos;
32026             
32027         }
32028         
32029         if(box[0].size == 'xs' && box[1].size == 'xs'){
32030             
32031             pos.push({
32032                 x : x,
32033                 y : y
32034             });
32035
32036             pos.push({
32037                 x : x,
32038                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32039             });
32040             
32041             pos.push({
32042                 x : x + (this.unitWidth + this.gutter) * 1,
32043                 y : y
32044             });
32045             
32046             return pos;
32047             
32048         }
32049         
32050         pos.push({
32051             x : x,
32052             y : y
32053         });
32054
32055         pos.push({
32056             x : x + (this.unitWidth + this.gutter) * 2,
32057             y : y
32058         });
32059
32060         pos.push({
32061             x : x + (this.unitWidth + this.gutter) * 2,
32062             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32063         });
32064             
32065         return pos;
32066         
32067     },
32068     
32069     getVerticalFourBoxColPositions : function(x, y, box)
32070     {
32071         var pos = [];
32072         
32073         if(box[0].size == 'xs'){
32074             
32075             pos.push({
32076                 x : x,
32077                 y : y
32078             });
32079
32080             pos.push({
32081                 x : x,
32082                 y : y + (this.unitHeight + this.gutter) * 1
32083             });
32084             
32085             pos.push({
32086                 x : x,
32087                 y : y + (this.unitHeight + this.gutter) * 2
32088             });
32089             
32090             pos.push({
32091                 x : x + (this.unitWidth + this.gutter) * 1,
32092                 y : y
32093             });
32094             
32095             return pos;
32096             
32097         }
32098         
32099         pos.push({
32100             x : x,
32101             y : y
32102         });
32103
32104         pos.push({
32105             x : x + (this.unitWidth + this.gutter) * 2,
32106             y : y
32107         });
32108
32109         pos.push({
32110             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32111             y : y + (this.unitHeight + this.gutter) * 1
32112         });
32113
32114         pos.push({
32115             x : x + (this.unitWidth + this.gutter) * 2,
32116             y : y + (this.unitWidth + this.gutter) * 2
32117         });
32118
32119         return pos;
32120         
32121     },
32122     
32123     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32124     {
32125         var pos = [];
32126         
32127         if(box[0].size == 'md-left'){
32128             pos.push({
32129                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32130                 y : minY
32131             });
32132             
32133             return pos;
32134         }
32135         
32136         if(box[0].size == 'md-right'){
32137             pos.push({
32138                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32139                 y : minY + (this.unitWidth + this.gutter) * 1
32140             });
32141             
32142             return pos;
32143         }
32144         
32145         var rand = Math.floor(Math.random() * (4 - box[0].y));
32146         
32147         pos.push({
32148             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32149             y : minY + (this.unitWidth + this.gutter) * rand
32150         });
32151         
32152         return pos;
32153         
32154     },
32155     
32156     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32157     {
32158         var pos = [];
32159         
32160         if(box[0].size == 'xs'){
32161             
32162             pos.push({
32163                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32164                 y : minY
32165             });
32166
32167             pos.push({
32168                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32169                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
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         return pos;
32187         
32188     },
32189     
32190     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32191     {
32192         var pos = [];
32193         
32194         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32195             
32196             pos.push({
32197                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32198                 y : minY
32199             });
32200
32201             pos.push({
32202                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32203                 y : minY + (this.unitWidth + this.gutter) * 1
32204             });
32205             
32206             pos.push({
32207                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32208                 y : minY + (this.unitWidth + this.gutter) * 2
32209             });
32210             
32211             return pos;
32212             
32213         }
32214         
32215         if(box[0].size == 'xs' && box[1].size == 'xs'){
32216             
32217             pos.push({
32218                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32219                 y : minY
32220             });
32221
32222             pos.push({
32223                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32224                 y : minY
32225             });
32226             
32227             pos.push({
32228                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32229                 y : minY + (this.unitWidth + this.gutter) * 1
32230             });
32231             
32232             return pos;
32233             
32234         }
32235         
32236         pos.push({
32237             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32238             y : minY
32239         });
32240
32241         pos.push({
32242             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32243             y : minY + (this.unitWidth + this.gutter) * 2
32244         });
32245
32246         pos.push({
32247             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32248             y : minY + (this.unitWidth + this.gutter) * 2
32249         });
32250             
32251         return pos;
32252         
32253     },
32254     
32255     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32256     {
32257         var pos = [];
32258         
32259         if(box[0].size == 'xs'){
32260             
32261             pos.push({
32262                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32263                 y : minY
32264             });
32265
32266             pos.push({
32267                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32268                 y : minY
32269             });
32270             
32271             pos.push({
32272                 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),
32273                 y : minY
32274             });
32275             
32276             pos.push({
32277                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32278                 y : minY + (this.unitWidth + this.gutter) * 1
32279             });
32280             
32281             return pos;
32282             
32283         }
32284         
32285         pos.push({
32286             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32287             y : minY
32288         });
32289         
32290         pos.push({
32291             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32292             y : minY + (this.unitWidth + this.gutter) * 2
32293         });
32294         
32295         pos.push({
32296             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32297             y : minY + (this.unitWidth + this.gutter) * 2
32298         });
32299         
32300         pos.push({
32301             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),
32302             y : minY + (this.unitWidth + this.gutter) * 2
32303         });
32304
32305         return pos;
32306         
32307     },
32308     
32309     /**
32310     * remove a Masonry Brick
32311     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32312     */
32313     removeBrick : function(brick_id)
32314     {
32315         if (!brick_id) {
32316             return;
32317         }
32318         
32319         for (var i = 0; i<this.bricks.length; i++) {
32320             if (this.bricks[i].id == brick_id) {
32321                 this.bricks.splice(i,1);
32322                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32323                 this.initial();
32324             }
32325         }
32326     },
32327     
32328     /**
32329     * adds a Masonry Brick
32330     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32331     */
32332     addBrick : function(cfg)
32333     {
32334         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32335         //this.register(cn);
32336         cn.parentId = this.id;
32337         cn.render(this.el);
32338         return cn;
32339     },
32340     
32341     /**
32342     * register a Masonry Brick
32343     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32344     */
32345     
32346     register : function(brick)
32347     {
32348         this.bricks.push(brick);
32349         brick.masonryId = this.id;
32350     },
32351     
32352     /**
32353     * clear all the Masonry Brick
32354     */
32355     clearAll : function()
32356     {
32357         this.bricks = [];
32358         //this.getChildContainer().dom.innerHTML = "";
32359         this.el.dom.innerHTML = '';
32360     },
32361     
32362     getSelected : function()
32363     {
32364         if (!this.selectedBrick) {
32365             return false;
32366         }
32367         
32368         return this.selectedBrick;
32369     }
32370 });
32371
32372 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32373     
32374     groups: {},
32375      /**
32376     * register a Masonry Layout
32377     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32378     */
32379     
32380     register : function(layout)
32381     {
32382         this.groups[layout.id] = layout;
32383     },
32384     /**
32385     * fetch a  Masonry Layout based on the masonry layout ID
32386     * @param {string} the masonry layout to add
32387     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32388     */
32389     
32390     get: function(layout_id) {
32391         if (typeof(this.groups[layout_id]) == 'undefined') {
32392             return false;
32393         }
32394         return this.groups[layout_id] ;
32395     }
32396     
32397     
32398     
32399 });
32400
32401  
32402
32403  /**
32404  *
32405  * This is based on 
32406  * http://masonry.desandro.com
32407  *
32408  * The idea is to render all the bricks based on vertical width...
32409  *
32410  * The original code extends 'outlayer' - we might need to use that....
32411  * 
32412  */
32413
32414
32415 /**
32416  * @class Roo.bootstrap.LayoutMasonryAuto
32417  * @extends Roo.bootstrap.Component
32418  * Bootstrap Layout Masonry class
32419  * 
32420  * @constructor
32421  * Create a new Element
32422  * @param {Object} config The config object
32423  */
32424
32425 Roo.bootstrap.LayoutMasonryAuto = function(config){
32426     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32427 };
32428
32429 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32430     
32431       /**
32432      * @cfg {Boolean} isFitWidth  - resize the width..
32433      */   
32434     isFitWidth : false,  // options..
32435     /**
32436      * @cfg {Boolean} isOriginLeft = left align?
32437      */   
32438     isOriginLeft : true,
32439     /**
32440      * @cfg {Boolean} isOriginTop = top align?
32441      */   
32442     isOriginTop : false,
32443     /**
32444      * @cfg {Boolean} isLayoutInstant = no animation?
32445      */   
32446     isLayoutInstant : false, // needed?
32447     /**
32448      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32449      */   
32450     isResizingContainer : true,
32451     /**
32452      * @cfg {Number} columnWidth  width of the columns 
32453      */   
32454     
32455     columnWidth : 0,
32456     
32457     /**
32458      * @cfg {Number} maxCols maximum number of columns
32459      */   
32460     
32461     maxCols: 0,
32462     /**
32463      * @cfg {Number} padHeight padding below box..
32464      */   
32465     
32466     padHeight : 10, 
32467     
32468     /**
32469      * @cfg {Boolean} isAutoInitial defalut true
32470      */   
32471     
32472     isAutoInitial : true, 
32473     
32474     // private?
32475     gutter : 0,
32476     
32477     containerWidth: 0,
32478     initialColumnWidth : 0,
32479     currentSize : null,
32480     
32481     colYs : null, // array.
32482     maxY : 0,
32483     padWidth: 10,
32484     
32485     
32486     tag: 'div',
32487     cls: '',
32488     bricks: null, //CompositeElement
32489     cols : 0, // array?
32490     // element : null, // wrapped now this.el
32491     _isLayoutInited : null, 
32492     
32493     
32494     getAutoCreate : function(){
32495         
32496         var cfg = {
32497             tag: this.tag,
32498             cls: 'blog-masonary-wrapper ' + this.cls,
32499             cn : {
32500                 cls : 'mas-boxes masonary'
32501             }
32502         };
32503         
32504         return cfg;
32505     },
32506     
32507     getChildContainer: function( )
32508     {
32509         if (this.boxesEl) {
32510             return this.boxesEl;
32511         }
32512         
32513         this.boxesEl = this.el.select('.mas-boxes').first();
32514         
32515         return this.boxesEl;
32516     },
32517     
32518     
32519     initEvents : function()
32520     {
32521         var _this = this;
32522         
32523         if(this.isAutoInitial){
32524             Roo.log('hook children rendered');
32525             this.on('childrenrendered', function() {
32526                 Roo.log('children rendered');
32527                 _this.initial();
32528             } ,this);
32529         }
32530         
32531     },
32532     
32533     initial : function()
32534     {
32535         this.reloadItems();
32536
32537         this.currentSize = this.el.getBox(true);
32538
32539         /// was window resize... - let's see if this works..
32540         Roo.EventManager.onWindowResize(this.resize, this); 
32541
32542         if(!this.isAutoInitial){
32543             this.layout();
32544             return;
32545         }
32546         
32547         this.layout.defer(500,this);
32548     },
32549     
32550     reloadItems: function()
32551     {
32552         this.bricks = this.el.select('.masonry-brick', true);
32553         
32554         this.bricks.each(function(b) {
32555             //Roo.log(b.getSize());
32556             if (!b.attr('originalwidth')) {
32557                 b.attr('originalwidth',  b.getSize().width);
32558             }
32559             
32560         });
32561         
32562         Roo.log(this.bricks.elements.length);
32563     },
32564     
32565     resize : function()
32566     {
32567         Roo.log('resize');
32568         var cs = this.el.getBox(true);
32569         
32570         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32571             Roo.log("no change in with or X");
32572             return;
32573         }
32574         this.currentSize = cs;
32575         this.layout();
32576     },
32577     
32578     layout : function()
32579     {
32580          Roo.log('layout');
32581         this._resetLayout();
32582         //this._manageStamps();
32583       
32584         // don't animate first layout
32585         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32586         this.layoutItems( isInstant );
32587       
32588         // flag for initalized
32589         this._isLayoutInited = true;
32590     },
32591     
32592     layoutItems : function( isInstant )
32593     {
32594         //var items = this._getItemsForLayout( this.items );
32595         // original code supports filtering layout items.. we just ignore it..
32596         
32597         this._layoutItems( this.bricks , isInstant );
32598       
32599         this._postLayout();
32600     },
32601     _layoutItems : function ( items , isInstant)
32602     {
32603        //this.fireEvent( 'layout', this, items );
32604     
32605
32606         if ( !items || !items.elements.length ) {
32607           // no items, emit event with empty array
32608             return;
32609         }
32610
32611         var queue = [];
32612         items.each(function(item) {
32613             Roo.log("layout item");
32614             Roo.log(item);
32615             // get x/y object from method
32616             var position = this._getItemLayoutPosition( item );
32617             // enqueue
32618             position.item = item;
32619             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32620             queue.push( position );
32621         }, this);
32622       
32623         this._processLayoutQueue( queue );
32624     },
32625     /** Sets position of item in DOM
32626     * @param {Element} item
32627     * @param {Number} x - horizontal position
32628     * @param {Number} y - vertical position
32629     * @param {Boolean} isInstant - disables transitions
32630     */
32631     _processLayoutQueue : function( queue )
32632     {
32633         for ( var i=0, len = queue.length; i < len; i++ ) {
32634             var obj = queue[i];
32635             obj.item.position('absolute');
32636             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32637         }
32638     },
32639       
32640     
32641     /**
32642     * Any logic you want to do after each layout,
32643     * i.e. size the container
32644     */
32645     _postLayout : function()
32646     {
32647         this.resizeContainer();
32648     },
32649     
32650     resizeContainer : function()
32651     {
32652         if ( !this.isResizingContainer ) {
32653             return;
32654         }
32655         var size = this._getContainerSize();
32656         if ( size ) {
32657             this.el.setSize(size.width,size.height);
32658             this.boxesEl.setSize(size.width,size.height);
32659         }
32660     },
32661     
32662     
32663     
32664     _resetLayout : function()
32665     {
32666         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32667         this.colWidth = this.el.getWidth();
32668         //this.gutter = this.el.getWidth(); 
32669         
32670         this.measureColumns();
32671
32672         // reset column Y
32673         var i = this.cols;
32674         this.colYs = [];
32675         while (i--) {
32676             this.colYs.push( 0 );
32677         }
32678     
32679         this.maxY = 0;
32680     },
32681
32682     measureColumns : function()
32683     {
32684         this.getContainerWidth();
32685       // if columnWidth is 0, default to outerWidth of first item
32686         if ( !this.columnWidth ) {
32687             var firstItem = this.bricks.first();
32688             Roo.log(firstItem);
32689             this.columnWidth  = this.containerWidth;
32690             if (firstItem && firstItem.attr('originalwidth') ) {
32691                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32692             }
32693             // columnWidth fall back to item of first element
32694             Roo.log("set column width?");
32695                         this.initialColumnWidth = this.columnWidth  ;
32696
32697             // if first elem has no width, default to size of container
32698             
32699         }
32700         
32701         
32702         if (this.initialColumnWidth) {
32703             this.columnWidth = this.initialColumnWidth;
32704         }
32705         
32706         
32707             
32708         // column width is fixed at the top - however if container width get's smaller we should
32709         // reduce it...
32710         
32711         // this bit calcs how man columns..
32712             
32713         var columnWidth = this.columnWidth += this.gutter;
32714       
32715         // calculate columns
32716         var containerWidth = this.containerWidth + this.gutter;
32717         
32718         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32719         // fix rounding errors, typically with gutters
32720         var excess = columnWidth - containerWidth % columnWidth;
32721         
32722         
32723         // if overshoot is less than a pixel, round up, otherwise floor it
32724         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32725         cols = Math[ mathMethod ]( cols );
32726         this.cols = Math.max( cols, 1 );
32727         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32728         
32729          // padding positioning..
32730         var totalColWidth = this.cols * this.columnWidth;
32731         var padavail = this.containerWidth - totalColWidth;
32732         // so for 2 columns - we need 3 'pads'
32733         
32734         var padNeeded = (1+this.cols) * this.padWidth;
32735         
32736         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32737         
32738         this.columnWidth += padExtra
32739         //this.padWidth = Math.floor(padavail /  ( this.cols));
32740         
32741         // adjust colum width so that padding is fixed??
32742         
32743         // we have 3 columns ... total = width * 3
32744         // we have X left over... that should be used by 
32745         
32746         //if (this.expandC) {
32747             
32748         //}
32749         
32750         
32751         
32752     },
32753     
32754     getContainerWidth : function()
32755     {
32756        /* // container is parent if fit width
32757         var container = this.isFitWidth ? this.element.parentNode : this.element;
32758         // check that this.size and size are there
32759         // IE8 triggers resize on body size change, so they might not be
32760         
32761         var size = getSize( container );  //FIXME
32762         this.containerWidth = size && size.innerWidth; //FIXME
32763         */
32764          
32765         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32766         
32767     },
32768     
32769     _getItemLayoutPosition : function( item )  // what is item?
32770     {
32771         // we resize the item to our columnWidth..
32772       
32773         item.setWidth(this.columnWidth);
32774         item.autoBoxAdjust  = false;
32775         
32776         var sz = item.getSize();
32777  
32778         // how many columns does this brick span
32779         var remainder = this.containerWidth % this.columnWidth;
32780         
32781         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32782         // round if off by 1 pixel, otherwise use ceil
32783         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32784         colSpan = Math.min( colSpan, this.cols );
32785         
32786         // normally this should be '1' as we dont' currently allow multi width columns..
32787         
32788         var colGroup = this._getColGroup( colSpan );
32789         // get the minimum Y value from the columns
32790         var minimumY = Math.min.apply( Math, colGroup );
32791         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32792         
32793         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32794          
32795         // position the brick
32796         var position = {
32797             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32798             y: this.currentSize.y + minimumY + this.padHeight
32799         };
32800         
32801         Roo.log(position);
32802         // apply setHeight to necessary columns
32803         var setHeight = minimumY + sz.height + this.padHeight;
32804         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32805         
32806         var setSpan = this.cols + 1 - colGroup.length;
32807         for ( var i = 0; i < setSpan; i++ ) {
32808           this.colYs[ shortColIndex + i ] = setHeight ;
32809         }
32810       
32811         return position;
32812     },
32813     
32814     /**
32815      * @param {Number} colSpan - number of columns the element spans
32816      * @returns {Array} colGroup
32817      */
32818     _getColGroup : function( colSpan )
32819     {
32820         if ( colSpan < 2 ) {
32821           // if brick spans only one column, use all the column Ys
32822           return this.colYs;
32823         }
32824       
32825         var colGroup = [];
32826         // how many different places could this brick fit horizontally
32827         var groupCount = this.cols + 1 - colSpan;
32828         // for each group potential horizontal position
32829         for ( var i = 0; i < groupCount; i++ ) {
32830           // make an array of colY values for that one group
32831           var groupColYs = this.colYs.slice( i, i + colSpan );
32832           // and get the max value of the array
32833           colGroup[i] = Math.max.apply( Math, groupColYs );
32834         }
32835         return colGroup;
32836     },
32837     /*
32838     _manageStamp : function( stamp )
32839     {
32840         var stampSize =  stamp.getSize();
32841         var offset = stamp.getBox();
32842         // get the columns that this stamp affects
32843         var firstX = this.isOriginLeft ? offset.x : offset.right;
32844         var lastX = firstX + stampSize.width;
32845         var firstCol = Math.floor( firstX / this.columnWidth );
32846         firstCol = Math.max( 0, firstCol );
32847         
32848         var lastCol = Math.floor( lastX / this.columnWidth );
32849         // lastCol should not go over if multiple of columnWidth #425
32850         lastCol -= lastX % this.columnWidth ? 0 : 1;
32851         lastCol = Math.min( this.cols - 1, lastCol );
32852         
32853         // set colYs to bottom of the stamp
32854         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32855             stampSize.height;
32856             
32857         for ( var i = firstCol; i <= lastCol; i++ ) {
32858           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32859         }
32860     },
32861     */
32862     
32863     _getContainerSize : function()
32864     {
32865         this.maxY = Math.max.apply( Math, this.colYs );
32866         var size = {
32867             height: this.maxY
32868         };
32869       
32870         if ( this.isFitWidth ) {
32871             size.width = this._getContainerFitWidth();
32872         }
32873       
32874         return size;
32875     },
32876     
32877     _getContainerFitWidth : function()
32878     {
32879         var unusedCols = 0;
32880         // count unused columns
32881         var i = this.cols;
32882         while ( --i ) {
32883           if ( this.colYs[i] !== 0 ) {
32884             break;
32885           }
32886           unusedCols++;
32887         }
32888         // fit container to columns that have been used
32889         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32890     },
32891     
32892     needsResizeLayout : function()
32893     {
32894         var previousWidth = this.containerWidth;
32895         this.getContainerWidth();
32896         return previousWidth !== this.containerWidth;
32897     }
32898  
32899 });
32900
32901  
32902
32903  /*
32904  * - LGPL
32905  *
32906  * element
32907  * 
32908  */
32909
32910 /**
32911  * @class Roo.bootstrap.MasonryBrick
32912  * @extends Roo.bootstrap.Component
32913  * Bootstrap MasonryBrick class
32914  * 
32915  * @constructor
32916  * Create a new MasonryBrick
32917  * @param {Object} config The config object
32918  */
32919
32920 Roo.bootstrap.MasonryBrick = function(config){
32921     
32922     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32923     
32924     Roo.bootstrap.MasonryBrick.register(this);
32925     
32926     this.addEvents({
32927         // raw events
32928         /**
32929          * @event click
32930          * When a MasonryBrick is clcik
32931          * @param {Roo.bootstrap.MasonryBrick} this
32932          * @param {Roo.EventObject} e
32933          */
32934         "click" : true
32935     });
32936 };
32937
32938 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32939     
32940     /**
32941      * @cfg {String} title
32942      */   
32943     title : '',
32944     /**
32945      * @cfg {String} html
32946      */   
32947     html : '',
32948     /**
32949      * @cfg {String} bgimage
32950      */   
32951     bgimage : '',
32952     /**
32953      * @cfg {String} videourl
32954      */   
32955     videourl : '',
32956     /**
32957      * @cfg {String} cls
32958      */   
32959     cls : '',
32960     /**
32961      * @cfg {String} href
32962      */   
32963     href : '',
32964     /**
32965      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32966      */   
32967     size : 'xs',
32968     
32969     /**
32970      * @cfg {String} placetitle (center|bottom)
32971      */   
32972     placetitle : '',
32973     
32974     /**
32975      * @cfg {Boolean} isFitContainer defalut true
32976      */   
32977     isFitContainer : true, 
32978     
32979     /**
32980      * @cfg {Boolean} preventDefault defalut false
32981      */   
32982     preventDefault : false, 
32983     
32984     /**
32985      * @cfg {Boolean} inverse defalut false
32986      */   
32987     maskInverse : false, 
32988     
32989     getAutoCreate : function()
32990     {
32991         if(!this.isFitContainer){
32992             return this.getSplitAutoCreate();
32993         }
32994         
32995         var cls = 'masonry-brick masonry-brick-full';
32996         
32997         if(this.href.length){
32998             cls += ' masonry-brick-link';
32999         }
33000         
33001         if(this.bgimage.length){
33002             cls += ' masonry-brick-image';
33003         }
33004         
33005         if(this.maskInverse){
33006             cls += ' mask-inverse';
33007         }
33008         
33009         if(!this.html.length && !this.maskInverse && !this.videourl.length){
33010             cls += ' enable-mask';
33011         }
33012         
33013         if(this.size){
33014             cls += ' masonry-' + this.size + '-brick';
33015         }
33016         
33017         if(this.placetitle.length){
33018             
33019             switch (this.placetitle) {
33020                 case 'center' :
33021                     cls += ' masonry-center-title';
33022                     break;
33023                 case 'bottom' :
33024                     cls += ' masonry-bottom-title';
33025                     break;
33026                 default:
33027                     break;
33028             }
33029             
33030         } else {
33031             if(!this.html.length && !this.bgimage.length){
33032                 cls += ' masonry-center-title';
33033             }
33034
33035             if(!this.html.length && this.bgimage.length){
33036                 cls += ' masonry-bottom-title';
33037             }
33038         }
33039         
33040         if(this.cls){
33041             cls += ' ' + this.cls;
33042         }
33043         
33044         var cfg = {
33045             tag: (this.href.length) ? 'a' : 'div',
33046             cls: cls,
33047             cn: [
33048                 {
33049                     tag: 'div',
33050                     cls: 'masonry-brick-mask'
33051                 },
33052                 {
33053                     tag: 'div',
33054                     cls: 'masonry-brick-paragraph',
33055                     cn: []
33056                 }
33057             ]
33058         };
33059         
33060         if(this.href.length){
33061             cfg.href = this.href;
33062         }
33063         
33064         var cn = cfg.cn[1].cn;
33065         
33066         if(this.title.length){
33067             cn.push({
33068                 tag: 'h4',
33069                 cls: 'masonry-brick-title',
33070                 html: this.title
33071             });
33072         }
33073         
33074         if(this.html.length){
33075             cn.push({
33076                 tag: 'p',
33077                 cls: 'masonry-brick-text',
33078                 html: this.html
33079             });
33080         }
33081         
33082         if (!this.title.length && !this.html.length) {
33083             cfg.cn[1].cls += ' hide';
33084         }
33085         
33086         if(this.bgimage.length){
33087             cfg.cn.push({
33088                 tag: 'img',
33089                 cls: 'masonry-brick-image-view',
33090                 src: this.bgimage
33091             });
33092         }
33093         
33094         if(this.videourl.length){
33095             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33096             // youtube support only?
33097             cfg.cn.push({
33098                 tag: 'iframe',
33099                 cls: 'masonry-brick-image-view',
33100                 src: vurl,
33101                 frameborder : 0,
33102                 allowfullscreen : true
33103             });
33104         }
33105         
33106         return cfg;
33107         
33108     },
33109     
33110     getSplitAutoCreate : function()
33111     {
33112         var cls = 'masonry-brick masonry-brick-split';
33113         
33114         if(this.href.length){
33115             cls += ' masonry-brick-link';
33116         }
33117         
33118         if(this.bgimage.length){
33119             cls += ' masonry-brick-image';
33120         }
33121         
33122         if(this.size){
33123             cls += ' masonry-' + this.size + '-brick';
33124         }
33125         
33126         switch (this.placetitle) {
33127             case 'center' :
33128                 cls += ' masonry-center-title';
33129                 break;
33130             case 'bottom' :
33131                 cls += ' masonry-bottom-title';
33132                 break;
33133             default:
33134                 if(!this.bgimage.length){
33135                     cls += ' masonry-center-title';
33136                 }
33137
33138                 if(this.bgimage.length){
33139                     cls += ' masonry-bottom-title';
33140                 }
33141                 break;
33142         }
33143         
33144         if(this.cls){
33145             cls += ' ' + this.cls;
33146         }
33147         
33148         var cfg = {
33149             tag: (this.href.length) ? 'a' : 'div',
33150             cls: cls,
33151             cn: [
33152                 {
33153                     tag: 'div',
33154                     cls: 'masonry-brick-split-head',
33155                     cn: [
33156                         {
33157                             tag: 'div',
33158                             cls: 'masonry-brick-paragraph',
33159                             cn: []
33160                         }
33161                     ]
33162                 },
33163                 {
33164                     tag: 'div',
33165                     cls: 'masonry-brick-split-body',
33166                     cn: []
33167                 }
33168             ]
33169         };
33170         
33171         if(this.href.length){
33172             cfg.href = this.href;
33173         }
33174         
33175         if(this.title.length){
33176             cfg.cn[0].cn[0].cn.push({
33177                 tag: 'h4',
33178                 cls: 'masonry-brick-title',
33179                 html: this.title
33180             });
33181         }
33182         
33183         if(this.html.length){
33184             cfg.cn[1].cn.push({
33185                 tag: 'p',
33186                 cls: 'masonry-brick-text',
33187                 html: this.html
33188             });
33189         }
33190
33191         if(this.bgimage.length){
33192             cfg.cn[0].cn.push({
33193                 tag: 'img',
33194                 cls: 'masonry-brick-image-view',
33195                 src: this.bgimage
33196             });
33197         }
33198         
33199         if(this.videourl.length){
33200             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33201             // youtube support only?
33202             cfg.cn[0].cn.cn.push({
33203                 tag: 'iframe',
33204                 cls: 'masonry-brick-image-view',
33205                 src: vurl,
33206                 frameborder : 0,
33207                 allowfullscreen : true
33208             });
33209         }
33210         
33211         return cfg;
33212     },
33213     
33214     initEvents: function() 
33215     {
33216         switch (this.size) {
33217             case 'xs' :
33218                 this.x = 1;
33219                 this.y = 1;
33220                 break;
33221             case 'sm' :
33222                 this.x = 2;
33223                 this.y = 2;
33224                 break;
33225             case 'md' :
33226             case 'md-left' :
33227             case 'md-right' :
33228                 this.x = 3;
33229                 this.y = 3;
33230                 break;
33231             case 'tall' :
33232                 this.x = 2;
33233                 this.y = 3;
33234                 break;
33235             case 'wide' :
33236                 this.x = 3;
33237                 this.y = 2;
33238                 break;
33239             case 'wide-thin' :
33240                 this.x = 3;
33241                 this.y = 1;
33242                 break;
33243                         
33244             default :
33245                 break;
33246         }
33247         
33248         if(Roo.isTouch){
33249             this.el.on('touchstart', this.onTouchStart, this);
33250             this.el.on('touchmove', this.onTouchMove, this);
33251             this.el.on('touchend', this.onTouchEnd, this);
33252             this.el.on('contextmenu', this.onContextMenu, this);
33253         } else {
33254             this.el.on('mouseenter'  ,this.enter, this);
33255             this.el.on('mouseleave', this.leave, this);
33256             this.el.on('click', this.onClick, this);
33257         }
33258         
33259         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33260             this.parent().bricks.push(this);   
33261         }
33262         
33263     },
33264     
33265     onClick: function(e, el)
33266     {
33267         var time = this.endTimer - this.startTimer;
33268         // Roo.log(e.preventDefault());
33269         if(Roo.isTouch){
33270             if(time > 1000){
33271                 e.preventDefault();
33272                 return;
33273             }
33274         }
33275         
33276         if(!this.preventDefault){
33277             return;
33278         }
33279         
33280         e.preventDefault();
33281         
33282         if (this.activeClass != '') {
33283             this.selectBrick();
33284         }
33285         
33286         this.fireEvent('click', this, e);
33287     },
33288     
33289     enter: function(e, el)
33290     {
33291         e.preventDefault();
33292         
33293         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33294             return;
33295         }
33296         
33297         if(this.bgimage.length && this.html.length){
33298             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33299         }
33300     },
33301     
33302     leave: function(e, el)
33303     {
33304         e.preventDefault();
33305         
33306         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33307             return;
33308         }
33309         
33310         if(this.bgimage.length && this.html.length){
33311             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33312         }
33313     },
33314     
33315     onTouchStart: function(e, el)
33316     {
33317 //        e.preventDefault();
33318         
33319         this.touchmoved = false;
33320         
33321         if(!this.isFitContainer){
33322             return;
33323         }
33324         
33325         if(!this.bgimage.length || !this.html.length){
33326             return;
33327         }
33328         
33329         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33330         
33331         this.timer = new Date().getTime();
33332         
33333     },
33334     
33335     onTouchMove: function(e, el)
33336     {
33337         this.touchmoved = true;
33338     },
33339     
33340     onContextMenu : function(e,el)
33341     {
33342         e.preventDefault();
33343         e.stopPropagation();
33344         return false;
33345     },
33346     
33347     onTouchEnd: function(e, el)
33348     {
33349 //        e.preventDefault();
33350         
33351         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33352         
33353             this.leave(e,el);
33354             
33355             return;
33356         }
33357         
33358         if(!this.bgimage.length || !this.html.length){
33359             
33360             if(this.href.length){
33361                 window.location.href = this.href;
33362             }
33363             
33364             return;
33365         }
33366         
33367         if(!this.isFitContainer){
33368             return;
33369         }
33370         
33371         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33372         
33373         window.location.href = this.href;
33374     },
33375     
33376     //selection on single brick only
33377     selectBrick : function() {
33378         
33379         if (!this.parentId) {
33380             return;
33381         }
33382         
33383         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33384         var index = m.selectedBrick.indexOf(this.id);
33385         
33386         if ( index > -1) {
33387             m.selectedBrick.splice(index,1);
33388             this.el.removeClass(this.activeClass);
33389             return;
33390         }
33391         
33392         for(var i = 0; i < m.selectedBrick.length; i++) {
33393             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33394             b.el.removeClass(b.activeClass);
33395         }
33396         
33397         m.selectedBrick = [];
33398         
33399         m.selectedBrick.push(this.id);
33400         this.el.addClass(this.activeClass);
33401         return;
33402     },
33403     
33404     isSelected : function(){
33405         return this.el.hasClass(this.activeClass);
33406         
33407     }
33408 });
33409
33410 Roo.apply(Roo.bootstrap.MasonryBrick, {
33411     
33412     //groups: {},
33413     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33414      /**
33415     * register a Masonry Brick
33416     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33417     */
33418     
33419     register : function(brick)
33420     {
33421         //this.groups[brick.id] = brick;
33422         this.groups.add(brick.id, brick);
33423     },
33424     /**
33425     * fetch a  masonry brick based on the masonry brick ID
33426     * @param {string} the masonry brick to add
33427     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33428     */
33429     
33430     get: function(brick_id) 
33431     {
33432         // if (typeof(this.groups[brick_id]) == 'undefined') {
33433         //     return false;
33434         // }
33435         // return this.groups[brick_id] ;
33436         
33437         if(this.groups.key(brick_id)) {
33438             return this.groups.key(brick_id);
33439         }
33440         
33441         return false;
33442     }
33443     
33444     
33445     
33446 });
33447
33448  /*
33449  * - LGPL
33450  *
33451  * element
33452  * 
33453  */
33454
33455 /**
33456  * @class Roo.bootstrap.Brick
33457  * @extends Roo.bootstrap.Component
33458  * Bootstrap Brick class
33459  * 
33460  * @constructor
33461  * Create a new Brick
33462  * @param {Object} config The config object
33463  */
33464
33465 Roo.bootstrap.Brick = function(config){
33466     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33467     
33468     this.addEvents({
33469         // raw events
33470         /**
33471          * @event click
33472          * When a Brick is click
33473          * @param {Roo.bootstrap.Brick} this
33474          * @param {Roo.EventObject} e
33475          */
33476         "click" : true
33477     });
33478 };
33479
33480 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33481     
33482     /**
33483      * @cfg {String} title
33484      */   
33485     title : '',
33486     /**
33487      * @cfg {String} html
33488      */   
33489     html : '',
33490     /**
33491      * @cfg {String} bgimage
33492      */   
33493     bgimage : '',
33494     /**
33495      * @cfg {String} cls
33496      */   
33497     cls : '',
33498     /**
33499      * @cfg {String} href
33500      */   
33501     href : '',
33502     /**
33503      * @cfg {String} video
33504      */   
33505     video : '',
33506     /**
33507      * @cfg {Boolean} square
33508      */   
33509     square : true,
33510     
33511     getAutoCreate : function()
33512     {
33513         var cls = 'roo-brick';
33514         
33515         if(this.href.length){
33516             cls += ' roo-brick-link';
33517         }
33518         
33519         if(this.bgimage.length){
33520             cls += ' roo-brick-image';
33521         }
33522         
33523         if(!this.html.length && !this.bgimage.length){
33524             cls += ' roo-brick-center-title';
33525         }
33526         
33527         if(!this.html.length && this.bgimage.length){
33528             cls += ' roo-brick-bottom-title';
33529         }
33530         
33531         if(this.cls){
33532             cls += ' ' + this.cls;
33533         }
33534         
33535         var cfg = {
33536             tag: (this.href.length) ? 'a' : 'div',
33537             cls: cls,
33538             cn: [
33539                 {
33540                     tag: 'div',
33541                     cls: 'roo-brick-paragraph',
33542                     cn: []
33543                 }
33544             ]
33545         };
33546         
33547         if(this.href.length){
33548             cfg.href = this.href;
33549         }
33550         
33551         var cn = cfg.cn[0].cn;
33552         
33553         if(this.title.length){
33554             cn.push({
33555                 tag: 'h4',
33556                 cls: 'roo-brick-title',
33557                 html: this.title
33558             });
33559         }
33560         
33561         if(this.html.length){
33562             cn.push({
33563                 tag: 'p',
33564                 cls: 'roo-brick-text',
33565                 html: this.html
33566             });
33567         } else {
33568             cn.cls += ' hide';
33569         }
33570         
33571         if(this.bgimage.length){
33572             cfg.cn.push({
33573                 tag: 'img',
33574                 cls: 'roo-brick-image-view',
33575                 src: this.bgimage
33576             });
33577         }
33578         
33579         return cfg;
33580     },
33581     
33582     initEvents: function() 
33583     {
33584         if(this.title.length || this.html.length){
33585             this.el.on('mouseenter'  ,this.enter, this);
33586             this.el.on('mouseleave', this.leave, this);
33587         }
33588         
33589         Roo.EventManager.onWindowResize(this.resize, this); 
33590         
33591         if(this.bgimage.length){
33592             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33593             this.imageEl.on('load', this.onImageLoad, this);
33594             return;
33595         }
33596         
33597         this.resize();
33598     },
33599     
33600     onImageLoad : function()
33601     {
33602         this.resize();
33603     },
33604     
33605     resize : function()
33606     {
33607         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33608         
33609         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33610         
33611         if(this.bgimage.length){
33612             var image = this.el.select('.roo-brick-image-view', true).first();
33613             
33614             image.setWidth(paragraph.getWidth());
33615             
33616             if(this.square){
33617                 image.setHeight(paragraph.getWidth());
33618             }
33619             
33620             this.el.setHeight(image.getHeight());
33621             paragraph.setHeight(image.getHeight());
33622             
33623         }
33624         
33625     },
33626     
33627     enter: function(e, el)
33628     {
33629         e.preventDefault();
33630         
33631         if(this.bgimage.length){
33632             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33633             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33634         }
33635     },
33636     
33637     leave: function(e, el)
33638     {
33639         e.preventDefault();
33640         
33641         if(this.bgimage.length){
33642             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33643             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33644         }
33645     }
33646     
33647 });
33648
33649  
33650
33651  /*
33652  * - LGPL
33653  *
33654  * Number field 
33655  */
33656
33657 /**
33658  * @class Roo.bootstrap.NumberField
33659  * @extends Roo.bootstrap.Input
33660  * Bootstrap NumberField class
33661  * 
33662  * 
33663  * 
33664  * 
33665  * @constructor
33666  * Create a new NumberField
33667  * @param {Object} config The config object
33668  */
33669
33670 Roo.bootstrap.NumberField = function(config){
33671     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33672 };
33673
33674 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33675     
33676     /**
33677      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33678      */
33679     allowDecimals : true,
33680     /**
33681      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33682      */
33683     decimalSeparator : ".",
33684     /**
33685      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33686      */
33687     decimalPrecision : 2,
33688     /**
33689      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33690      */
33691     allowNegative : true,
33692     
33693     /**
33694      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33695      */
33696     allowZero: true,
33697     /**
33698      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33699      */
33700     minValue : Number.NEGATIVE_INFINITY,
33701     /**
33702      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33703      */
33704     maxValue : Number.MAX_VALUE,
33705     /**
33706      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33707      */
33708     minText : "The minimum value for this field is {0}",
33709     /**
33710      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33711      */
33712     maxText : "The maximum value for this field is {0}",
33713     /**
33714      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33715      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33716      */
33717     nanText : "{0} is not a valid number",
33718     /**
33719      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33720      */
33721     thousandsDelimiter : false,
33722     /**
33723      * @cfg {String} valueAlign alignment of value
33724      */
33725     valueAlign : "left",
33726
33727     getAutoCreate : function()
33728     {
33729         var hiddenInput = {
33730             tag: 'input',
33731             type: 'hidden',
33732             id: Roo.id(),
33733             cls: 'hidden-number-input'
33734         };
33735         
33736         if (this.name) {
33737             hiddenInput.name = this.name;
33738         }
33739         
33740         this.name = '';
33741         
33742         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33743         
33744         this.name = hiddenInput.name;
33745         
33746         if(cfg.cn.length > 0) {
33747             cfg.cn.push(hiddenInput);
33748         }
33749         
33750         return cfg;
33751     },
33752
33753     // private
33754     initEvents : function()
33755     {   
33756         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33757         
33758         var allowed = "0123456789";
33759         
33760         if(this.allowDecimals){
33761             allowed += this.decimalSeparator;
33762         }
33763         
33764         if(this.allowNegative){
33765             allowed += "-";
33766         }
33767         
33768         if(this.thousandsDelimiter) {
33769             allowed += ",";
33770         }
33771         
33772         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33773         
33774         var keyPress = function(e){
33775             
33776             var k = e.getKey();
33777             
33778             var c = e.getCharCode();
33779             
33780             if(
33781                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33782                     allowed.indexOf(String.fromCharCode(c)) === -1
33783             ){
33784                 e.stopEvent();
33785                 return;
33786             }
33787             
33788             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33789                 return;
33790             }
33791             
33792             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33793                 e.stopEvent();
33794             }
33795         };
33796         
33797         this.el.on("keypress", keyPress, this);
33798     },
33799     
33800     validateValue : function(value)
33801     {
33802         
33803         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33804             return false;
33805         }
33806         
33807         var num = this.parseValue(value);
33808         
33809         if(isNaN(num)){
33810             this.markInvalid(String.format(this.nanText, value));
33811             return false;
33812         }
33813         
33814         if(num < this.minValue){
33815             this.markInvalid(String.format(this.minText, this.minValue));
33816             return false;
33817         }
33818         
33819         if(num > this.maxValue){
33820             this.markInvalid(String.format(this.maxText, this.maxValue));
33821             return false;
33822         }
33823         
33824         return true;
33825     },
33826
33827     getValue : function()
33828     {
33829         var v = this.hiddenEl().getValue();
33830         
33831         return this.fixPrecision(this.parseValue(v));
33832     },
33833
33834     parseValue : function(value)
33835     {
33836         if(this.thousandsDelimiter) {
33837             value += "";
33838             r = new RegExp(",", "g");
33839             value = value.replace(r, "");
33840         }
33841         
33842         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33843         return isNaN(value) ? '' : value;
33844     },
33845
33846     fixPrecision : function(value)
33847     {
33848         if(this.thousandsDelimiter) {
33849             value += "";
33850             r = new RegExp(",", "g");
33851             value = value.replace(r, "");
33852         }
33853         
33854         var nan = isNaN(value);
33855         
33856         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33857             return nan ? '' : value;
33858         }
33859         return parseFloat(value).toFixed(this.decimalPrecision);
33860     },
33861
33862     setValue : function(v)
33863     {
33864         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33865         
33866         this.value = v;
33867         
33868         if(this.rendered){
33869             
33870             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33871             
33872             this.inputEl().dom.value = (v == '') ? '' :
33873                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33874             
33875             if(!this.allowZero && v === '0') {
33876                 this.hiddenEl().dom.value = '';
33877                 this.inputEl().dom.value = '';
33878             }
33879             
33880             this.validate();
33881         }
33882     },
33883
33884     decimalPrecisionFcn : function(v)
33885     {
33886         return Math.floor(v);
33887     },
33888
33889     beforeBlur : function()
33890     {
33891         var v = this.parseValue(this.getRawValue());
33892         
33893         if(v || v === 0 || v === ''){
33894             this.setValue(v);
33895         }
33896     },
33897     
33898     hiddenEl : function()
33899     {
33900         return this.el.select('input.hidden-number-input',true).first();
33901     }
33902     
33903 });
33904
33905  
33906
33907 /*
33908 * Licence: LGPL
33909 */
33910
33911 /**
33912  * @class Roo.bootstrap.DocumentSlider
33913  * @extends Roo.bootstrap.Component
33914  * Bootstrap DocumentSlider class
33915  * 
33916  * @constructor
33917  * Create a new DocumentViewer
33918  * @param {Object} config The config object
33919  */
33920
33921 Roo.bootstrap.DocumentSlider = function(config){
33922     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33923     
33924     this.files = [];
33925     
33926     this.addEvents({
33927         /**
33928          * @event initial
33929          * Fire after initEvent
33930          * @param {Roo.bootstrap.DocumentSlider} this
33931          */
33932         "initial" : true,
33933         /**
33934          * @event update
33935          * Fire after update
33936          * @param {Roo.bootstrap.DocumentSlider} this
33937          */
33938         "update" : true,
33939         /**
33940          * @event click
33941          * Fire after click
33942          * @param {Roo.bootstrap.DocumentSlider} this
33943          */
33944         "click" : true
33945     });
33946 };
33947
33948 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33949     
33950     files : false,
33951     
33952     indicator : 0,
33953     
33954     getAutoCreate : function()
33955     {
33956         var cfg = {
33957             tag : 'div',
33958             cls : 'roo-document-slider',
33959             cn : [
33960                 {
33961                     tag : 'div',
33962                     cls : 'roo-document-slider-header',
33963                     cn : [
33964                         {
33965                             tag : 'div',
33966                             cls : 'roo-document-slider-header-title'
33967                         }
33968                     ]
33969                 },
33970                 {
33971                     tag : 'div',
33972                     cls : 'roo-document-slider-body',
33973                     cn : [
33974                         {
33975                             tag : 'div',
33976                             cls : 'roo-document-slider-prev',
33977                             cn : [
33978                                 {
33979                                     tag : 'i',
33980                                     cls : 'fa fa-chevron-left'
33981                                 }
33982                             ]
33983                         },
33984                         {
33985                             tag : 'div',
33986                             cls : 'roo-document-slider-thumb',
33987                             cn : [
33988                                 {
33989                                     tag : 'img',
33990                                     cls : 'roo-document-slider-image'
33991                                 }
33992                             ]
33993                         },
33994                         {
33995                             tag : 'div',
33996                             cls : 'roo-document-slider-next',
33997                             cn : [
33998                                 {
33999                                     tag : 'i',
34000                                     cls : 'fa fa-chevron-right'
34001                                 }
34002                             ]
34003                         }
34004                     ]
34005                 }
34006             ]
34007         };
34008         
34009         return cfg;
34010     },
34011     
34012     initEvents : function()
34013     {
34014         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34015         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34016         
34017         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34018         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34019         
34020         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34021         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34022         
34023         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34024         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34025         
34026         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34027         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34028         
34029         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34030         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34031         
34032         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34033         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34034         
34035         this.thumbEl.on('click', this.onClick, this);
34036         
34037         this.prevIndicator.on('click', this.prev, this);
34038         
34039         this.nextIndicator.on('click', this.next, this);
34040         
34041     },
34042     
34043     initial : function()
34044     {
34045         if(this.files.length){
34046             this.indicator = 1;
34047             this.update()
34048         }
34049         
34050         this.fireEvent('initial', this);
34051     },
34052     
34053     update : function()
34054     {
34055         this.imageEl.attr('src', this.files[this.indicator - 1]);
34056         
34057         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34058         
34059         this.prevIndicator.show();
34060         
34061         if(this.indicator == 1){
34062             this.prevIndicator.hide();
34063         }
34064         
34065         this.nextIndicator.show();
34066         
34067         if(this.indicator == this.files.length){
34068             this.nextIndicator.hide();
34069         }
34070         
34071         this.thumbEl.scrollTo('top');
34072         
34073         this.fireEvent('update', this);
34074     },
34075     
34076     onClick : function(e)
34077     {
34078         e.preventDefault();
34079         
34080         this.fireEvent('click', this);
34081     },
34082     
34083     prev : function(e)
34084     {
34085         e.preventDefault();
34086         
34087         this.indicator = Math.max(1, this.indicator - 1);
34088         
34089         this.update();
34090     },
34091     
34092     next : function(e)
34093     {
34094         e.preventDefault();
34095         
34096         this.indicator = Math.min(this.files.length, this.indicator + 1);
34097         
34098         this.update();
34099     }
34100 });
34101 /*
34102  * - LGPL
34103  *
34104  * RadioSet
34105  *
34106  *
34107  */
34108
34109 /**
34110  * @class Roo.bootstrap.RadioSet
34111  * @extends Roo.bootstrap.Input
34112  * Bootstrap RadioSet class
34113  * @cfg {String} indicatorpos (left|right) default left
34114  * @cfg {Boolean} inline (true|false) inline the element (default true)
34115  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34116  * @constructor
34117  * Create a new RadioSet
34118  * @param {Object} config The config object
34119  */
34120
34121 Roo.bootstrap.RadioSet = function(config){
34122     
34123     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34124     
34125     this.radioes = [];
34126     
34127     Roo.bootstrap.RadioSet.register(this);
34128     
34129     this.addEvents({
34130         /**
34131         * @event check
34132         * Fires when the element is checked or unchecked.
34133         * @param {Roo.bootstrap.RadioSet} this This radio
34134         * @param {Roo.bootstrap.Radio} item The checked item
34135         */
34136        check : true,
34137        /**
34138         * @event click
34139         * Fires when the element is click.
34140         * @param {Roo.bootstrap.RadioSet} this This radio set
34141         * @param {Roo.bootstrap.Radio} item The checked item
34142         * @param {Roo.EventObject} e The event object
34143         */
34144        click : true
34145     });
34146     
34147 };
34148
34149 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34150
34151     radioes : false,
34152     
34153     inline : true,
34154     
34155     weight : '',
34156     
34157     indicatorpos : 'left',
34158     
34159     getAutoCreate : function()
34160     {
34161         var label = {
34162             tag : 'label',
34163             cls : 'roo-radio-set-label',
34164             cn : [
34165                 {
34166                     tag : 'span',
34167                     html : this.fieldLabel
34168                 }
34169             ]
34170         };
34171         if (Roo.bootstrap.version == 3) {
34172             
34173             
34174             if(this.indicatorpos == 'left'){
34175                 label.cn.unshift({
34176                     tag : 'i',
34177                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34178                     tooltip : 'This field is required'
34179                 });
34180             } else {
34181                 label.cn.push({
34182                     tag : 'i',
34183                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34184                     tooltip : 'This field is required'
34185                 });
34186             }
34187         }
34188         var items = {
34189             tag : 'div',
34190             cls : 'roo-radio-set-items'
34191         };
34192         
34193         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34194         
34195         if (align === 'left' && this.fieldLabel.length) {
34196             
34197             items = {
34198                 cls : "roo-radio-set-right", 
34199                 cn: [
34200                     items
34201                 ]
34202             };
34203             
34204             if(this.labelWidth > 12){
34205                 label.style = "width: " + this.labelWidth + 'px';
34206             }
34207             
34208             if(this.labelWidth < 13 && this.labelmd == 0){
34209                 this.labelmd = this.labelWidth;
34210             }
34211             
34212             if(this.labellg > 0){
34213                 label.cls += ' col-lg-' + this.labellg;
34214                 items.cls += ' col-lg-' + (12 - this.labellg);
34215             }
34216             
34217             if(this.labelmd > 0){
34218                 label.cls += ' col-md-' + this.labelmd;
34219                 items.cls += ' col-md-' + (12 - this.labelmd);
34220             }
34221             
34222             if(this.labelsm > 0){
34223                 label.cls += ' col-sm-' + this.labelsm;
34224                 items.cls += ' col-sm-' + (12 - this.labelsm);
34225             }
34226             
34227             if(this.labelxs > 0){
34228                 label.cls += ' col-xs-' + this.labelxs;
34229                 items.cls += ' col-xs-' + (12 - this.labelxs);
34230             }
34231         }
34232         
34233         var cfg = {
34234             tag : 'div',
34235             cls : 'roo-radio-set',
34236             cn : [
34237                 {
34238                     tag : 'input',
34239                     cls : 'roo-radio-set-input',
34240                     type : 'hidden',
34241                     name : this.name,
34242                     value : this.value ? this.value :  ''
34243                 },
34244                 label,
34245                 items
34246             ]
34247         };
34248         
34249         if(this.weight.length){
34250             cfg.cls += ' roo-radio-' + this.weight;
34251         }
34252         
34253         if(this.inline) {
34254             cfg.cls += ' roo-radio-set-inline';
34255         }
34256         
34257         var settings=this;
34258         ['xs','sm','md','lg'].map(function(size){
34259             if (settings[size]) {
34260                 cfg.cls += ' col-' + size + '-' + settings[size];
34261             }
34262         });
34263         
34264         return cfg;
34265         
34266     },
34267
34268     initEvents : function()
34269     {
34270         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34271         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34272         
34273         if(!this.fieldLabel.length){
34274             this.labelEl.hide();
34275         }
34276         
34277         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34278         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34279         
34280         this.indicator = this.indicatorEl();
34281         
34282         if(this.indicator){
34283             this.indicator.addClass('invisible');
34284         }
34285         
34286         this.originalValue = this.getValue();
34287         
34288     },
34289     
34290     inputEl: function ()
34291     {
34292         return this.el.select('.roo-radio-set-input', true).first();
34293     },
34294     
34295     getChildContainer : function()
34296     {
34297         return this.itemsEl;
34298     },
34299     
34300     register : function(item)
34301     {
34302         this.radioes.push(item);
34303         
34304     },
34305     
34306     validate : function()
34307     {   
34308         if(this.getVisibilityEl().hasClass('hidden')){
34309             return true;
34310         }
34311         
34312         var valid = false;
34313         
34314         Roo.each(this.radioes, function(i){
34315             if(!i.checked){
34316                 return;
34317             }
34318             
34319             valid = true;
34320             return false;
34321         });
34322         
34323         if(this.allowBlank) {
34324             return true;
34325         }
34326         
34327         if(this.disabled || valid){
34328             this.markValid();
34329             return true;
34330         }
34331         
34332         this.markInvalid();
34333         return false;
34334         
34335     },
34336     
34337     markValid : function()
34338     {
34339         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34340             this.indicatorEl().removeClass('visible');
34341             this.indicatorEl().addClass('invisible');
34342         }
34343         
34344         
34345         if (Roo.bootstrap.version == 3) {
34346             this.el.removeClass([this.invalidClass, this.validClass]);
34347             this.el.addClass(this.validClass);
34348         } else {
34349             this.el.removeClass(['is-invalid','is-valid']);
34350             this.el.addClass(['is-valid']);
34351         }
34352         this.fireEvent('valid', this);
34353     },
34354     
34355     markInvalid : function(msg)
34356     {
34357         if(this.allowBlank || this.disabled){
34358             return;
34359         }
34360         
34361         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34362             this.indicatorEl().removeClass('invisible');
34363             this.indicatorEl().addClass('visible');
34364         }
34365         if (Roo.bootstrap.version == 3) {
34366             this.el.removeClass([this.invalidClass, this.validClass]);
34367             this.el.addClass(this.invalidClass);
34368         } else {
34369             this.el.removeClass(['is-invalid','is-valid']);
34370             this.el.addClass(['is-invalid']);
34371         }
34372         
34373         this.fireEvent('invalid', this, msg);
34374         
34375     },
34376     
34377     setValue : function(v, suppressEvent)
34378     {   
34379         if(this.value === v){
34380             return;
34381         }
34382         
34383         this.value = v;
34384         
34385         if(this.rendered){
34386             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34387         }
34388         
34389         Roo.each(this.radioes, function(i){
34390             i.checked = false;
34391             i.el.removeClass('checked');
34392         });
34393         
34394         Roo.each(this.radioes, function(i){
34395             
34396             if(i.value === v || i.value.toString() === v.toString()){
34397                 i.checked = true;
34398                 i.el.addClass('checked');
34399                 
34400                 if(suppressEvent !== true){
34401                     this.fireEvent('check', this, i);
34402                 }
34403                 
34404                 return false;
34405             }
34406             
34407         }, this);
34408         
34409         this.validate();
34410     },
34411     
34412     clearInvalid : function(){
34413         
34414         if(!this.el || this.preventMark){
34415             return;
34416         }
34417         
34418         this.el.removeClass([this.invalidClass]);
34419         
34420         this.fireEvent('valid', this);
34421     }
34422     
34423 });
34424
34425 Roo.apply(Roo.bootstrap.RadioSet, {
34426     
34427     groups: {},
34428     
34429     register : function(set)
34430     {
34431         this.groups[set.name] = set;
34432     },
34433     
34434     get: function(name) 
34435     {
34436         if (typeof(this.groups[name]) == 'undefined') {
34437             return false;
34438         }
34439         
34440         return this.groups[name] ;
34441     }
34442     
34443 });
34444 /*
34445  * Based on:
34446  * Ext JS Library 1.1.1
34447  * Copyright(c) 2006-2007, Ext JS, LLC.
34448  *
34449  * Originally Released Under LGPL - original licence link has changed is not relivant.
34450  *
34451  * Fork - LGPL
34452  * <script type="text/javascript">
34453  */
34454
34455
34456 /**
34457  * @class Roo.bootstrap.SplitBar
34458  * @extends Roo.util.Observable
34459  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34460  * <br><br>
34461  * Usage:
34462  * <pre><code>
34463 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34464                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34465 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34466 split.minSize = 100;
34467 split.maxSize = 600;
34468 split.animate = true;
34469 split.on('moved', splitterMoved);
34470 </code></pre>
34471  * @constructor
34472  * Create a new SplitBar
34473  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34474  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34475  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34476  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34477                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34478                         position of the SplitBar).
34479  */
34480 Roo.bootstrap.SplitBar = function(cfg){
34481     
34482     /** @private */
34483     
34484     //{
34485     //  dragElement : elm
34486     //  resizingElement: el,
34487         // optional..
34488     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34489     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34490         // existingProxy ???
34491     //}
34492     
34493     this.el = Roo.get(cfg.dragElement, true);
34494     this.el.dom.unselectable = "on";
34495     /** @private */
34496     this.resizingEl = Roo.get(cfg.resizingElement, true);
34497
34498     /**
34499      * @private
34500      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34501      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34502      * @type Number
34503      */
34504     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34505     
34506     /**
34507      * The minimum size of the resizing element. (Defaults to 0)
34508      * @type Number
34509      */
34510     this.minSize = 0;
34511     
34512     /**
34513      * The maximum size of the resizing element. (Defaults to 2000)
34514      * @type Number
34515      */
34516     this.maxSize = 2000;
34517     
34518     /**
34519      * Whether to animate the transition to the new size
34520      * @type Boolean
34521      */
34522     this.animate = false;
34523     
34524     /**
34525      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34526      * @type Boolean
34527      */
34528     this.useShim = false;
34529     
34530     /** @private */
34531     this.shim = null;
34532     
34533     if(!cfg.existingProxy){
34534         /** @private */
34535         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34536     }else{
34537         this.proxy = Roo.get(cfg.existingProxy).dom;
34538     }
34539     /** @private */
34540     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34541     
34542     /** @private */
34543     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34544     
34545     /** @private */
34546     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34547     
34548     /** @private */
34549     this.dragSpecs = {};
34550     
34551     /**
34552      * @private The adapter to use to positon and resize elements
34553      */
34554     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34555     this.adapter.init(this);
34556     
34557     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34558         /** @private */
34559         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34560         this.el.addClass("roo-splitbar-h");
34561     }else{
34562         /** @private */
34563         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34564         this.el.addClass("roo-splitbar-v");
34565     }
34566     
34567     this.addEvents({
34568         /**
34569          * @event resize
34570          * Fires when the splitter is moved (alias for {@link #event-moved})
34571          * @param {Roo.bootstrap.SplitBar} this
34572          * @param {Number} newSize the new width or height
34573          */
34574         "resize" : true,
34575         /**
34576          * @event moved
34577          * Fires when the splitter is moved
34578          * @param {Roo.bootstrap.SplitBar} this
34579          * @param {Number} newSize the new width or height
34580          */
34581         "moved" : true,
34582         /**
34583          * @event beforeresize
34584          * Fires before the splitter is dragged
34585          * @param {Roo.bootstrap.SplitBar} this
34586          */
34587         "beforeresize" : true,
34588
34589         "beforeapply" : true
34590     });
34591
34592     Roo.util.Observable.call(this);
34593 };
34594
34595 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34596     onStartProxyDrag : function(x, y){
34597         this.fireEvent("beforeresize", this);
34598         if(!this.overlay){
34599             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34600             o.unselectable();
34601             o.enableDisplayMode("block");
34602             // all splitbars share the same overlay
34603             Roo.bootstrap.SplitBar.prototype.overlay = o;
34604         }
34605         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34606         this.overlay.show();
34607         Roo.get(this.proxy).setDisplayed("block");
34608         var size = this.adapter.getElementSize(this);
34609         this.activeMinSize = this.getMinimumSize();;
34610         this.activeMaxSize = this.getMaximumSize();;
34611         var c1 = size - this.activeMinSize;
34612         var c2 = Math.max(this.activeMaxSize - size, 0);
34613         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34614             this.dd.resetConstraints();
34615             this.dd.setXConstraint(
34616                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34617                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34618             );
34619             this.dd.setYConstraint(0, 0);
34620         }else{
34621             this.dd.resetConstraints();
34622             this.dd.setXConstraint(0, 0);
34623             this.dd.setYConstraint(
34624                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34625                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34626             );
34627          }
34628         this.dragSpecs.startSize = size;
34629         this.dragSpecs.startPoint = [x, y];
34630         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34631     },
34632     
34633     /** 
34634      * @private Called after the drag operation by the DDProxy
34635      */
34636     onEndProxyDrag : function(e){
34637         Roo.get(this.proxy).setDisplayed(false);
34638         var endPoint = Roo.lib.Event.getXY(e);
34639         if(this.overlay){
34640             this.overlay.hide();
34641         }
34642         var newSize;
34643         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34644             newSize = this.dragSpecs.startSize + 
34645                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34646                     endPoint[0] - this.dragSpecs.startPoint[0] :
34647                     this.dragSpecs.startPoint[0] - endPoint[0]
34648                 );
34649         }else{
34650             newSize = this.dragSpecs.startSize + 
34651                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34652                     endPoint[1] - this.dragSpecs.startPoint[1] :
34653                     this.dragSpecs.startPoint[1] - endPoint[1]
34654                 );
34655         }
34656         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34657         if(newSize != this.dragSpecs.startSize){
34658             if(this.fireEvent('beforeapply', this, newSize) !== false){
34659                 this.adapter.setElementSize(this, newSize);
34660                 this.fireEvent("moved", this, newSize);
34661                 this.fireEvent("resize", this, newSize);
34662             }
34663         }
34664     },
34665     
34666     /**
34667      * Get the adapter this SplitBar uses
34668      * @return The adapter object
34669      */
34670     getAdapter : function(){
34671         return this.adapter;
34672     },
34673     
34674     /**
34675      * Set the adapter this SplitBar uses
34676      * @param {Object} adapter A SplitBar adapter object
34677      */
34678     setAdapter : function(adapter){
34679         this.adapter = adapter;
34680         this.adapter.init(this);
34681     },
34682     
34683     /**
34684      * Gets the minimum size for the resizing element
34685      * @return {Number} The minimum size
34686      */
34687     getMinimumSize : function(){
34688         return this.minSize;
34689     },
34690     
34691     /**
34692      * Sets the minimum size for the resizing element
34693      * @param {Number} minSize The minimum size
34694      */
34695     setMinimumSize : function(minSize){
34696         this.minSize = minSize;
34697     },
34698     
34699     /**
34700      * Gets the maximum size for the resizing element
34701      * @return {Number} The maximum size
34702      */
34703     getMaximumSize : function(){
34704         return this.maxSize;
34705     },
34706     
34707     /**
34708      * Sets the maximum size for the resizing element
34709      * @param {Number} maxSize The maximum size
34710      */
34711     setMaximumSize : function(maxSize){
34712         this.maxSize = maxSize;
34713     },
34714     
34715     /**
34716      * Sets the initialize size for the resizing element
34717      * @param {Number} size The initial size
34718      */
34719     setCurrentSize : function(size){
34720         var oldAnimate = this.animate;
34721         this.animate = false;
34722         this.adapter.setElementSize(this, size);
34723         this.animate = oldAnimate;
34724     },
34725     
34726     /**
34727      * Destroy this splitbar. 
34728      * @param {Boolean} removeEl True to remove the element
34729      */
34730     destroy : function(removeEl){
34731         if(this.shim){
34732             this.shim.remove();
34733         }
34734         this.dd.unreg();
34735         this.proxy.parentNode.removeChild(this.proxy);
34736         if(removeEl){
34737             this.el.remove();
34738         }
34739     }
34740 });
34741
34742 /**
34743  * @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.
34744  */
34745 Roo.bootstrap.SplitBar.createProxy = function(dir){
34746     var proxy = new Roo.Element(document.createElement("div"));
34747     proxy.unselectable();
34748     var cls = 'roo-splitbar-proxy';
34749     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34750     document.body.appendChild(proxy.dom);
34751     return proxy.dom;
34752 };
34753
34754 /** 
34755  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34756  * Default Adapter. It assumes the splitter and resizing element are not positioned
34757  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34758  */
34759 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34760 };
34761
34762 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34763     // do nothing for now
34764     init : function(s){
34765     
34766     },
34767     /**
34768      * Called before drag operations to get the current size of the resizing element. 
34769      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34770      */
34771      getElementSize : function(s){
34772         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34773             return s.resizingEl.getWidth();
34774         }else{
34775             return s.resizingEl.getHeight();
34776         }
34777     },
34778     
34779     /**
34780      * Called after drag operations to set the size of the resizing element.
34781      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34782      * @param {Number} newSize The new size to set
34783      * @param {Function} onComplete A function to be invoked when resizing is complete
34784      */
34785     setElementSize : function(s, newSize, onComplete){
34786         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34787             if(!s.animate){
34788                 s.resizingEl.setWidth(newSize);
34789                 if(onComplete){
34790                     onComplete(s, newSize);
34791                 }
34792             }else{
34793                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34794             }
34795         }else{
34796             
34797             if(!s.animate){
34798                 s.resizingEl.setHeight(newSize);
34799                 if(onComplete){
34800                     onComplete(s, newSize);
34801                 }
34802             }else{
34803                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34804             }
34805         }
34806     }
34807 };
34808
34809 /** 
34810  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34811  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34812  * Adapter that  moves the splitter element to align with the resized sizing element. 
34813  * Used with an absolute positioned SplitBar.
34814  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34815  * document.body, make sure you assign an id to the body element.
34816  */
34817 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34818     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34819     this.container = Roo.get(container);
34820 };
34821
34822 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34823     init : function(s){
34824         this.basic.init(s);
34825     },
34826     
34827     getElementSize : function(s){
34828         return this.basic.getElementSize(s);
34829     },
34830     
34831     setElementSize : function(s, newSize, onComplete){
34832         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34833     },
34834     
34835     moveSplitter : function(s){
34836         var yes = Roo.bootstrap.SplitBar;
34837         switch(s.placement){
34838             case yes.LEFT:
34839                 s.el.setX(s.resizingEl.getRight());
34840                 break;
34841             case yes.RIGHT:
34842                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34843                 break;
34844             case yes.TOP:
34845                 s.el.setY(s.resizingEl.getBottom());
34846                 break;
34847             case yes.BOTTOM:
34848                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34849                 break;
34850         }
34851     }
34852 };
34853
34854 /**
34855  * Orientation constant - Create a vertical SplitBar
34856  * @static
34857  * @type Number
34858  */
34859 Roo.bootstrap.SplitBar.VERTICAL = 1;
34860
34861 /**
34862  * Orientation constant - Create a horizontal SplitBar
34863  * @static
34864  * @type Number
34865  */
34866 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34867
34868 /**
34869  * Placement constant - The resizing element is to the left of the splitter element
34870  * @static
34871  * @type Number
34872  */
34873 Roo.bootstrap.SplitBar.LEFT = 1;
34874
34875 /**
34876  * Placement constant - The resizing element is to the right of the splitter element
34877  * @static
34878  * @type Number
34879  */
34880 Roo.bootstrap.SplitBar.RIGHT = 2;
34881
34882 /**
34883  * Placement constant - The resizing element is positioned above the splitter element
34884  * @static
34885  * @type Number
34886  */
34887 Roo.bootstrap.SplitBar.TOP = 3;
34888
34889 /**
34890  * Placement constant - The resizing element is positioned under splitter element
34891  * @static
34892  * @type Number
34893  */
34894 Roo.bootstrap.SplitBar.BOTTOM = 4;
34895 Roo.namespace("Roo.bootstrap.layout");/*
34896  * Based on:
34897  * Ext JS Library 1.1.1
34898  * Copyright(c) 2006-2007, Ext JS, LLC.
34899  *
34900  * Originally Released Under LGPL - original licence link has changed is not relivant.
34901  *
34902  * Fork - LGPL
34903  * <script type="text/javascript">
34904  */
34905
34906 /**
34907  * @class Roo.bootstrap.layout.Manager
34908  * @extends Roo.bootstrap.Component
34909  * Base class for layout managers.
34910  */
34911 Roo.bootstrap.layout.Manager = function(config)
34912 {
34913     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34914
34915
34916
34917
34918
34919     /** false to disable window resize monitoring @type Boolean */
34920     this.monitorWindowResize = true;
34921     this.regions = {};
34922     this.addEvents({
34923         /**
34924          * @event layout
34925          * Fires when a layout is performed.
34926          * @param {Roo.LayoutManager} this
34927          */
34928         "layout" : true,
34929         /**
34930          * @event regionresized
34931          * Fires when the user resizes a region.
34932          * @param {Roo.LayoutRegion} region The resized region
34933          * @param {Number} newSize The new size (width for east/west, height for north/south)
34934          */
34935         "regionresized" : true,
34936         /**
34937          * @event regioncollapsed
34938          * Fires when a region is collapsed.
34939          * @param {Roo.LayoutRegion} region The collapsed region
34940          */
34941         "regioncollapsed" : true,
34942         /**
34943          * @event regionexpanded
34944          * Fires when a region is expanded.
34945          * @param {Roo.LayoutRegion} region The expanded region
34946          */
34947         "regionexpanded" : true
34948     });
34949     this.updating = false;
34950
34951     if (config.el) {
34952         this.el = Roo.get(config.el);
34953         this.initEvents();
34954     }
34955
34956 };
34957
34958 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34959
34960
34961     regions : null,
34962
34963     monitorWindowResize : true,
34964
34965
34966     updating : false,
34967
34968
34969     onRender : function(ct, position)
34970     {
34971         if(!this.el){
34972             this.el = Roo.get(ct);
34973             this.initEvents();
34974         }
34975         //this.fireEvent('render',this);
34976     },
34977
34978
34979     initEvents: function()
34980     {
34981
34982
34983         // ie scrollbar fix
34984         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34985             document.body.scroll = "no";
34986         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34987             this.el.position('relative');
34988         }
34989         this.id = this.el.id;
34990         this.el.addClass("roo-layout-container");
34991         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34992         if(this.el.dom != document.body ) {
34993             this.el.on('resize', this.layout,this);
34994             this.el.on('show', this.layout,this);
34995         }
34996
34997     },
34998
34999     /**
35000      * Returns true if this layout is currently being updated
35001      * @return {Boolean}
35002      */
35003     isUpdating : function(){
35004         return this.updating;
35005     },
35006
35007     /**
35008      * Suspend the LayoutManager from doing auto-layouts while
35009      * making multiple add or remove calls
35010      */
35011     beginUpdate : function(){
35012         this.updating = true;
35013     },
35014
35015     /**
35016      * Restore auto-layouts and optionally disable the manager from performing a layout
35017      * @param {Boolean} noLayout true to disable a layout update
35018      */
35019     endUpdate : function(noLayout){
35020         this.updating = false;
35021         if(!noLayout){
35022             this.layout();
35023         }
35024     },
35025
35026     layout: function(){
35027         // abstract...
35028     },
35029
35030     onRegionResized : function(region, newSize){
35031         this.fireEvent("regionresized", region, newSize);
35032         this.layout();
35033     },
35034
35035     onRegionCollapsed : function(region){
35036         this.fireEvent("regioncollapsed", region);
35037     },
35038
35039     onRegionExpanded : function(region){
35040         this.fireEvent("regionexpanded", region);
35041     },
35042
35043     /**
35044      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35045      * performs box-model adjustments.
35046      * @return {Object} The size as an object {width: (the width), height: (the height)}
35047      */
35048     getViewSize : function()
35049     {
35050         var size;
35051         if(this.el.dom != document.body){
35052             size = this.el.getSize();
35053         }else{
35054             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35055         }
35056         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35057         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35058         return size;
35059     },
35060
35061     /**
35062      * Returns the Element this layout is bound to.
35063      * @return {Roo.Element}
35064      */
35065     getEl : function(){
35066         return this.el;
35067     },
35068
35069     /**
35070      * Returns the specified region.
35071      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35072      * @return {Roo.LayoutRegion}
35073      */
35074     getRegion : function(target){
35075         return this.regions[target.toLowerCase()];
35076     },
35077
35078     onWindowResize : function(){
35079         if(this.monitorWindowResize){
35080             this.layout();
35081         }
35082     }
35083 });
35084 /*
35085  * Based on:
35086  * Ext JS Library 1.1.1
35087  * Copyright(c) 2006-2007, Ext JS, LLC.
35088  *
35089  * Originally Released Under LGPL - original licence link has changed is not relivant.
35090  *
35091  * Fork - LGPL
35092  * <script type="text/javascript">
35093  */
35094 /**
35095  * @class Roo.bootstrap.layout.Border
35096  * @extends Roo.bootstrap.layout.Manager
35097  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35098  * please see: examples/bootstrap/nested.html<br><br>
35099  
35100 <b>The container the layout is rendered into can be either the body element or any other element.
35101 If it is not the body element, the container needs to either be an absolute positioned element,
35102 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35103 the container size if it is not the body element.</b>
35104
35105 * @constructor
35106 * Create a new Border
35107 * @param {Object} config Configuration options
35108  */
35109 Roo.bootstrap.layout.Border = function(config){
35110     config = config || {};
35111     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35112     
35113     
35114     
35115     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35116         if(config[region]){
35117             config[region].region = region;
35118             this.addRegion(config[region]);
35119         }
35120     },this);
35121     
35122 };
35123
35124 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35125
35126 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35127     /**
35128      * Creates and adds a new region if it doesn't already exist.
35129      * @param {String} target The target region key (north, south, east, west or center).
35130      * @param {Object} config The regions config object
35131      * @return {BorderLayoutRegion} The new region
35132      */
35133     addRegion : function(config)
35134     {
35135         if(!this.regions[config.region]){
35136             var r = this.factory(config);
35137             this.bindRegion(r);
35138         }
35139         return this.regions[config.region];
35140     },
35141
35142     // private (kinda)
35143     bindRegion : function(r){
35144         this.regions[r.config.region] = r;
35145         
35146         r.on("visibilitychange",    this.layout, this);
35147         r.on("paneladded",          this.layout, this);
35148         r.on("panelremoved",        this.layout, this);
35149         r.on("invalidated",         this.layout, this);
35150         r.on("resized",             this.onRegionResized, this);
35151         r.on("collapsed",           this.onRegionCollapsed, this);
35152         r.on("expanded",            this.onRegionExpanded, this);
35153     },
35154
35155     /**
35156      * Performs a layout update.
35157      */
35158     layout : function()
35159     {
35160         if(this.updating) {
35161             return;
35162         }
35163         
35164         // render all the rebions if they have not been done alreayd?
35165         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35166             if(this.regions[region] && !this.regions[region].bodyEl){
35167                 this.regions[region].onRender(this.el)
35168             }
35169         },this);
35170         
35171         var size = this.getViewSize();
35172         var w = size.width;
35173         var h = size.height;
35174         var centerW = w;
35175         var centerH = h;
35176         var centerY = 0;
35177         var centerX = 0;
35178         //var x = 0, y = 0;
35179
35180         var rs = this.regions;
35181         var north = rs["north"];
35182         var south = rs["south"]; 
35183         var west = rs["west"];
35184         var east = rs["east"];
35185         var center = rs["center"];
35186         //if(this.hideOnLayout){ // not supported anymore
35187             //c.el.setStyle("display", "none");
35188         //}
35189         if(north && north.isVisible()){
35190             var b = north.getBox();
35191             var m = north.getMargins();
35192             b.width = w - (m.left+m.right);
35193             b.x = m.left;
35194             b.y = m.top;
35195             centerY = b.height + b.y + m.bottom;
35196             centerH -= centerY;
35197             north.updateBox(this.safeBox(b));
35198         }
35199         if(south && south.isVisible()){
35200             var b = south.getBox();
35201             var m = south.getMargins();
35202             b.width = w - (m.left+m.right);
35203             b.x = m.left;
35204             var totalHeight = (b.height + m.top + m.bottom);
35205             b.y = h - totalHeight + m.top;
35206             centerH -= totalHeight;
35207             south.updateBox(this.safeBox(b));
35208         }
35209         if(west && west.isVisible()){
35210             var b = west.getBox();
35211             var m = west.getMargins();
35212             b.height = centerH - (m.top+m.bottom);
35213             b.x = m.left;
35214             b.y = centerY + m.top;
35215             var totalWidth = (b.width + m.left + m.right);
35216             centerX += totalWidth;
35217             centerW -= totalWidth;
35218             west.updateBox(this.safeBox(b));
35219         }
35220         if(east && east.isVisible()){
35221             var b = east.getBox();
35222             var m = east.getMargins();
35223             b.height = centerH - (m.top+m.bottom);
35224             var totalWidth = (b.width + m.left + m.right);
35225             b.x = w - totalWidth + m.left;
35226             b.y = centerY + m.top;
35227             centerW -= totalWidth;
35228             east.updateBox(this.safeBox(b));
35229         }
35230         if(center){
35231             var m = center.getMargins();
35232             var centerBox = {
35233                 x: centerX + m.left,
35234                 y: centerY + m.top,
35235                 width: centerW - (m.left+m.right),
35236                 height: centerH - (m.top+m.bottom)
35237             };
35238             //if(this.hideOnLayout){
35239                 //center.el.setStyle("display", "block");
35240             //}
35241             center.updateBox(this.safeBox(centerBox));
35242         }
35243         this.el.repaint();
35244         this.fireEvent("layout", this);
35245     },
35246
35247     // private
35248     safeBox : function(box){
35249         box.width = Math.max(0, box.width);
35250         box.height = Math.max(0, box.height);
35251         return box;
35252     },
35253
35254     /**
35255      * Adds a ContentPanel (or subclass) to this layout.
35256      * @param {String} target The target region key (north, south, east, west or center).
35257      * @param {Roo.ContentPanel} panel The panel to add
35258      * @return {Roo.ContentPanel} The added panel
35259      */
35260     add : function(target, panel){
35261          
35262         target = target.toLowerCase();
35263         return this.regions[target].add(panel);
35264     },
35265
35266     /**
35267      * Remove a ContentPanel (or subclass) to this layout.
35268      * @param {String} target The target region key (north, south, east, west or center).
35269      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35270      * @return {Roo.ContentPanel} The removed panel
35271      */
35272     remove : function(target, panel){
35273         target = target.toLowerCase();
35274         return this.regions[target].remove(panel);
35275     },
35276
35277     /**
35278      * Searches all regions for a panel with the specified id
35279      * @param {String} panelId
35280      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35281      */
35282     findPanel : function(panelId){
35283         var rs = this.regions;
35284         for(var target in rs){
35285             if(typeof rs[target] != "function"){
35286                 var p = rs[target].getPanel(panelId);
35287                 if(p){
35288                     return p;
35289                 }
35290             }
35291         }
35292         return null;
35293     },
35294
35295     /**
35296      * Searches all regions for a panel with the specified id and activates (shows) it.
35297      * @param {String/ContentPanel} panelId The panels id or the panel itself
35298      * @return {Roo.ContentPanel} The shown panel or null
35299      */
35300     showPanel : function(panelId) {
35301       var rs = this.regions;
35302       for(var target in rs){
35303          var r = rs[target];
35304          if(typeof r != "function"){
35305             if(r.hasPanel(panelId)){
35306                return r.showPanel(panelId);
35307             }
35308          }
35309       }
35310       return null;
35311    },
35312
35313    /**
35314      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35315      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35316      */
35317    /*
35318     restoreState : function(provider){
35319         if(!provider){
35320             provider = Roo.state.Manager;
35321         }
35322         var sm = new Roo.LayoutStateManager();
35323         sm.init(this, provider);
35324     },
35325 */
35326  
35327  
35328     /**
35329      * Adds a xtype elements to the layout.
35330      * <pre><code>
35331
35332 layout.addxtype({
35333        xtype : 'ContentPanel',
35334        region: 'west',
35335        items: [ .... ]
35336    }
35337 );
35338
35339 layout.addxtype({
35340         xtype : 'NestedLayoutPanel',
35341         region: 'west',
35342         layout: {
35343            center: { },
35344            west: { }   
35345         },
35346         items : [ ... list of content panels or nested layout panels.. ]
35347    }
35348 );
35349 </code></pre>
35350      * @param {Object} cfg Xtype definition of item to add.
35351      */
35352     addxtype : function(cfg)
35353     {
35354         // basically accepts a pannel...
35355         // can accept a layout region..!?!?
35356         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35357         
35358         
35359         // theory?  children can only be panels??
35360         
35361         //if (!cfg.xtype.match(/Panel$/)) {
35362         //    return false;
35363         //}
35364         var ret = false;
35365         
35366         if (typeof(cfg.region) == 'undefined') {
35367             Roo.log("Failed to add Panel, region was not set");
35368             Roo.log(cfg);
35369             return false;
35370         }
35371         var region = cfg.region;
35372         delete cfg.region;
35373         
35374           
35375         var xitems = [];
35376         if (cfg.items) {
35377             xitems = cfg.items;
35378             delete cfg.items;
35379         }
35380         var nb = false;
35381         
35382         switch(cfg.xtype) 
35383         {
35384             case 'Content':  // ContentPanel (el, cfg)
35385             case 'Scroll':  // ContentPanel (el, cfg)
35386             case 'View': 
35387                 cfg.autoCreate = cfg.autoCreate || true;
35388                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35389                 //} else {
35390                 //    var el = this.el.createChild();
35391                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35392                 //}
35393                 
35394                 this.add(region, ret);
35395                 break;
35396             
35397             /*
35398             case 'TreePanel': // our new panel!
35399                 cfg.el = this.el.createChild();
35400                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35401                 this.add(region, ret);
35402                 break;
35403             */
35404             
35405             case 'Nest': 
35406                 // create a new Layout (which is  a Border Layout...
35407                 
35408                 var clayout = cfg.layout;
35409                 clayout.el  = this.el.createChild();
35410                 clayout.items   = clayout.items  || [];
35411                 
35412                 delete cfg.layout;
35413                 
35414                 // replace this exitems with the clayout ones..
35415                 xitems = clayout.items;
35416                  
35417                 // force background off if it's in center...
35418                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35419                     cfg.background = false;
35420                 }
35421                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35422                 
35423                 
35424                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35425                 //console.log('adding nested layout panel '  + cfg.toSource());
35426                 this.add(region, ret);
35427                 nb = {}; /// find first...
35428                 break;
35429             
35430             case 'Grid':
35431                 
35432                 // needs grid and region
35433                 
35434                 //var el = this.getRegion(region).el.createChild();
35435                 /*
35436                  *var el = this.el.createChild();
35437                 // create the grid first...
35438                 cfg.grid.container = el;
35439                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35440                 */
35441                 
35442                 if (region == 'center' && this.active ) {
35443                     cfg.background = false;
35444                 }
35445                 
35446                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35447                 
35448                 this.add(region, ret);
35449                 /*
35450                 if (cfg.background) {
35451                     // render grid on panel activation (if panel background)
35452                     ret.on('activate', function(gp) {
35453                         if (!gp.grid.rendered) {
35454                     //        gp.grid.render(el);
35455                         }
35456                     });
35457                 } else {
35458                   //  cfg.grid.render(el);
35459                 }
35460                 */
35461                 break;
35462            
35463            
35464             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35465                 // it was the old xcomponent building that caused this before.
35466                 // espeically if border is the top element in the tree.
35467                 ret = this;
35468                 break; 
35469                 
35470                     
35471                 
35472                 
35473                 
35474             default:
35475                 /*
35476                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35477                     
35478                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35479                     this.add(region, ret);
35480                 } else {
35481                 */
35482                     Roo.log(cfg);
35483                     throw "Can not add '" + cfg.xtype + "' to Border";
35484                     return null;
35485              
35486                                 
35487              
35488         }
35489         this.beginUpdate();
35490         // add children..
35491         var region = '';
35492         var abn = {};
35493         Roo.each(xitems, function(i)  {
35494             region = nb && i.region ? i.region : false;
35495             
35496             var add = ret.addxtype(i);
35497            
35498             if (region) {
35499                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35500                 if (!i.background) {
35501                     abn[region] = nb[region] ;
35502                 }
35503             }
35504             
35505         });
35506         this.endUpdate();
35507
35508         // make the last non-background panel active..
35509         //if (nb) { Roo.log(abn); }
35510         if (nb) {
35511             
35512             for(var r in abn) {
35513                 region = this.getRegion(r);
35514                 if (region) {
35515                     // tried using nb[r], but it does not work..
35516                      
35517                     region.showPanel(abn[r]);
35518                    
35519                 }
35520             }
35521         }
35522         return ret;
35523         
35524     },
35525     
35526     
35527 // private
35528     factory : function(cfg)
35529     {
35530         
35531         var validRegions = Roo.bootstrap.layout.Border.regions;
35532
35533         var target = cfg.region;
35534         cfg.mgr = this;
35535         
35536         var r = Roo.bootstrap.layout;
35537         Roo.log(target);
35538         switch(target){
35539             case "north":
35540                 return new r.North(cfg);
35541             case "south":
35542                 return new r.South(cfg);
35543             case "east":
35544                 return new r.East(cfg);
35545             case "west":
35546                 return new r.West(cfg);
35547             case "center":
35548                 return new r.Center(cfg);
35549         }
35550         throw 'Layout region "'+target+'" not supported.';
35551     }
35552     
35553     
35554 });
35555  /*
35556  * Based on:
35557  * Ext JS Library 1.1.1
35558  * Copyright(c) 2006-2007, Ext JS, LLC.
35559  *
35560  * Originally Released Under LGPL - original licence link has changed is not relivant.
35561  *
35562  * Fork - LGPL
35563  * <script type="text/javascript">
35564  */
35565  
35566 /**
35567  * @class Roo.bootstrap.layout.Basic
35568  * @extends Roo.util.Observable
35569  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35570  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35571  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35572  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35573  * @cfg {string}   region  the region that it inhabits..
35574  * @cfg {bool}   skipConfig skip config?
35575  * 
35576
35577  */
35578 Roo.bootstrap.layout.Basic = function(config){
35579     
35580     this.mgr = config.mgr;
35581     
35582     this.position = config.region;
35583     
35584     var skipConfig = config.skipConfig;
35585     
35586     this.events = {
35587         /**
35588          * @scope Roo.BasicLayoutRegion
35589          */
35590         
35591         /**
35592          * @event beforeremove
35593          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35594          * @param {Roo.LayoutRegion} this
35595          * @param {Roo.ContentPanel} panel The panel
35596          * @param {Object} e The cancel event object
35597          */
35598         "beforeremove" : true,
35599         /**
35600          * @event invalidated
35601          * Fires when the layout for this region is changed.
35602          * @param {Roo.LayoutRegion} this
35603          */
35604         "invalidated" : true,
35605         /**
35606          * @event visibilitychange
35607          * Fires when this region is shown or hidden 
35608          * @param {Roo.LayoutRegion} this
35609          * @param {Boolean} visibility true or false
35610          */
35611         "visibilitychange" : true,
35612         /**
35613          * @event paneladded
35614          * Fires when a panel is added. 
35615          * @param {Roo.LayoutRegion} this
35616          * @param {Roo.ContentPanel} panel The panel
35617          */
35618         "paneladded" : true,
35619         /**
35620          * @event panelremoved
35621          * Fires when a panel is removed. 
35622          * @param {Roo.LayoutRegion} this
35623          * @param {Roo.ContentPanel} panel The panel
35624          */
35625         "panelremoved" : true,
35626         /**
35627          * @event beforecollapse
35628          * Fires when this region before collapse.
35629          * @param {Roo.LayoutRegion} this
35630          */
35631         "beforecollapse" : true,
35632         /**
35633          * @event collapsed
35634          * Fires when this region is collapsed.
35635          * @param {Roo.LayoutRegion} this
35636          */
35637         "collapsed" : true,
35638         /**
35639          * @event expanded
35640          * Fires when this region is expanded.
35641          * @param {Roo.LayoutRegion} this
35642          */
35643         "expanded" : true,
35644         /**
35645          * @event slideshow
35646          * Fires when this region is slid into view.
35647          * @param {Roo.LayoutRegion} this
35648          */
35649         "slideshow" : true,
35650         /**
35651          * @event slidehide
35652          * Fires when this region slides out of view. 
35653          * @param {Roo.LayoutRegion} this
35654          */
35655         "slidehide" : true,
35656         /**
35657          * @event panelactivated
35658          * Fires when a panel is activated. 
35659          * @param {Roo.LayoutRegion} this
35660          * @param {Roo.ContentPanel} panel The activated panel
35661          */
35662         "panelactivated" : true,
35663         /**
35664          * @event resized
35665          * Fires when the user resizes this region. 
35666          * @param {Roo.LayoutRegion} this
35667          * @param {Number} newSize The new size (width for east/west, height for north/south)
35668          */
35669         "resized" : true
35670     };
35671     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35672     this.panels = new Roo.util.MixedCollection();
35673     this.panels.getKey = this.getPanelId.createDelegate(this);
35674     this.box = null;
35675     this.activePanel = null;
35676     // ensure listeners are added...
35677     
35678     if (config.listeners || config.events) {
35679         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35680             listeners : config.listeners || {},
35681             events : config.events || {}
35682         });
35683     }
35684     
35685     if(skipConfig !== true){
35686         this.applyConfig(config);
35687     }
35688 };
35689
35690 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35691 {
35692     getPanelId : function(p){
35693         return p.getId();
35694     },
35695     
35696     applyConfig : function(config){
35697         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35698         this.config = config;
35699         
35700     },
35701     
35702     /**
35703      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35704      * the width, for horizontal (north, south) the height.
35705      * @param {Number} newSize The new width or height
35706      */
35707     resizeTo : function(newSize){
35708         var el = this.el ? this.el :
35709                  (this.activePanel ? this.activePanel.getEl() : null);
35710         if(el){
35711             switch(this.position){
35712                 case "east":
35713                 case "west":
35714                     el.setWidth(newSize);
35715                     this.fireEvent("resized", this, newSize);
35716                 break;
35717                 case "north":
35718                 case "south":
35719                     el.setHeight(newSize);
35720                     this.fireEvent("resized", this, newSize);
35721                 break;                
35722             }
35723         }
35724     },
35725     
35726     getBox : function(){
35727         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35728     },
35729     
35730     getMargins : function(){
35731         return this.margins;
35732     },
35733     
35734     updateBox : function(box){
35735         this.box = box;
35736         var el = this.activePanel.getEl();
35737         el.dom.style.left = box.x + "px";
35738         el.dom.style.top = box.y + "px";
35739         this.activePanel.setSize(box.width, box.height);
35740     },
35741     
35742     /**
35743      * Returns the container element for this region.
35744      * @return {Roo.Element}
35745      */
35746     getEl : function(){
35747         return this.activePanel;
35748     },
35749     
35750     /**
35751      * Returns true if this region is currently visible.
35752      * @return {Boolean}
35753      */
35754     isVisible : function(){
35755         return this.activePanel ? true : false;
35756     },
35757     
35758     setActivePanel : function(panel){
35759         panel = this.getPanel(panel);
35760         if(this.activePanel && this.activePanel != panel){
35761             this.activePanel.setActiveState(false);
35762             this.activePanel.getEl().setLeftTop(-10000,-10000);
35763         }
35764         this.activePanel = panel;
35765         panel.setActiveState(true);
35766         if(this.box){
35767             panel.setSize(this.box.width, this.box.height);
35768         }
35769         this.fireEvent("panelactivated", this, panel);
35770         this.fireEvent("invalidated");
35771     },
35772     
35773     /**
35774      * Show the specified panel.
35775      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35776      * @return {Roo.ContentPanel} The shown panel or null
35777      */
35778     showPanel : function(panel){
35779         panel = this.getPanel(panel);
35780         if(panel){
35781             this.setActivePanel(panel);
35782         }
35783         return panel;
35784     },
35785     
35786     /**
35787      * Get the active panel for this region.
35788      * @return {Roo.ContentPanel} The active panel or null
35789      */
35790     getActivePanel : function(){
35791         return this.activePanel;
35792     },
35793     
35794     /**
35795      * Add the passed ContentPanel(s)
35796      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35797      * @return {Roo.ContentPanel} The panel added (if only one was added)
35798      */
35799     add : function(panel){
35800         if(arguments.length > 1){
35801             for(var i = 0, len = arguments.length; i < len; i++) {
35802                 this.add(arguments[i]);
35803             }
35804             return null;
35805         }
35806         if(this.hasPanel(panel)){
35807             this.showPanel(panel);
35808             return panel;
35809         }
35810         var el = panel.getEl();
35811         if(el.dom.parentNode != this.mgr.el.dom){
35812             this.mgr.el.dom.appendChild(el.dom);
35813         }
35814         if(panel.setRegion){
35815             panel.setRegion(this);
35816         }
35817         this.panels.add(panel);
35818         el.setStyle("position", "absolute");
35819         if(!panel.background){
35820             this.setActivePanel(panel);
35821             if(this.config.initialSize && this.panels.getCount()==1){
35822                 this.resizeTo(this.config.initialSize);
35823             }
35824         }
35825         this.fireEvent("paneladded", this, panel);
35826         return panel;
35827     },
35828     
35829     /**
35830      * Returns true if the panel is in this region.
35831      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35832      * @return {Boolean}
35833      */
35834     hasPanel : function(panel){
35835         if(typeof panel == "object"){ // must be panel obj
35836             panel = panel.getId();
35837         }
35838         return this.getPanel(panel) ? true : false;
35839     },
35840     
35841     /**
35842      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35843      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35844      * @param {Boolean} preservePanel Overrides the config preservePanel option
35845      * @return {Roo.ContentPanel} The panel that was removed
35846      */
35847     remove : function(panel, preservePanel){
35848         panel = this.getPanel(panel);
35849         if(!panel){
35850             return null;
35851         }
35852         var e = {};
35853         this.fireEvent("beforeremove", this, panel, e);
35854         if(e.cancel === true){
35855             return null;
35856         }
35857         var panelId = panel.getId();
35858         this.panels.removeKey(panelId);
35859         return panel;
35860     },
35861     
35862     /**
35863      * Returns the panel specified or null if it's not in this region.
35864      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35865      * @return {Roo.ContentPanel}
35866      */
35867     getPanel : function(id){
35868         if(typeof id == "object"){ // must be panel obj
35869             return id;
35870         }
35871         return this.panels.get(id);
35872     },
35873     
35874     /**
35875      * Returns this regions position (north/south/east/west/center).
35876      * @return {String} 
35877      */
35878     getPosition: function(){
35879         return this.position;    
35880     }
35881 });/*
35882  * Based on:
35883  * Ext JS Library 1.1.1
35884  * Copyright(c) 2006-2007, Ext JS, LLC.
35885  *
35886  * Originally Released Under LGPL - original licence link has changed is not relivant.
35887  *
35888  * Fork - LGPL
35889  * <script type="text/javascript">
35890  */
35891  
35892 /**
35893  * @class Roo.bootstrap.layout.Region
35894  * @extends Roo.bootstrap.layout.Basic
35895  * This class represents a region in a layout manager.
35896  
35897  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35898  * @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})
35899  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35900  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35901  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35902  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35903  * @cfg {String}    title           The title for the region (overrides panel titles)
35904  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35905  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35906  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35907  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35908  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35909  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35910  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35911  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35912  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35913  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35914
35915  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35916  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35917  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35918  * @cfg {Number}    width           For East/West panels
35919  * @cfg {Number}    height          For North/South panels
35920  * @cfg {Boolean}   split           To show the splitter
35921  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35922  * 
35923  * @cfg {string}   cls             Extra CSS classes to add to region
35924  * 
35925  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35926  * @cfg {string}   region  the region that it inhabits..
35927  *
35928
35929  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35930  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35931
35932  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35933  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35934  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35935  */
35936 Roo.bootstrap.layout.Region = function(config)
35937 {
35938     this.applyConfig(config);
35939
35940     var mgr = config.mgr;
35941     var pos = config.region;
35942     config.skipConfig = true;
35943     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35944     
35945     if (mgr.el) {
35946         this.onRender(mgr.el);   
35947     }
35948      
35949     this.visible = true;
35950     this.collapsed = false;
35951     this.unrendered_panels = [];
35952 };
35953
35954 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35955
35956     position: '', // set by wrapper (eg. north/south etc..)
35957     unrendered_panels : null,  // unrendered panels.
35958     createBody : function(){
35959         /** This region's body element 
35960         * @type Roo.Element */
35961         this.bodyEl = this.el.createChild({
35962                 tag: "div",
35963                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35964         });
35965     },
35966
35967     onRender: function(ctr, pos)
35968     {
35969         var dh = Roo.DomHelper;
35970         /** This region's container element 
35971         * @type Roo.Element */
35972         this.el = dh.append(ctr.dom, {
35973                 tag: "div",
35974                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35975             }, true);
35976         /** This region's title element 
35977         * @type Roo.Element */
35978     
35979         this.titleEl = dh.append(this.el.dom,
35980             {
35981                     tag: "div",
35982                     unselectable: "on",
35983                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35984                     children:[
35985                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35986                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35987                     ]}, true);
35988         
35989         this.titleEl.enableDisplayMode();
35990         /** This region's title text element 
35991         * @type HTMLElement */
35992         this.titleTextEl = this.titleEl.dom.firstChild;
35993         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35994         /*
35995         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35996         this.closeBtn.enableDisplayMode();
35997         this.closeBtn.on("click", this.closeClicked, this);
35998         this.closeBtn.hide();
35999     */
36000         this.createBody(this.config);
36001         if(this.config.hideWhenEmpty){
36002             this.hide();
36003             this.on("paneladded", this.validateVisibility, this);
36004             this.on("panelremoved", this.validateVisibility, this);
36005         }
36006         if(this.autoScroll){
36007             this.bodyEl.setStyle("overflow", "auto");
36008         }else{
36009             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36010         }
36011         //if(c.titlebar !== false){
36012             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36013                 this.titleEl.hide();
36014             }else{
36015                 this.titleEl.show();
36016                 if(this.config.title){
36017                     this.titleTextEl.innerHTML = this.config.title;
36018                 }
36019             }
36020         //}
36021         if(this.config.collapsed){
36022             this.collapse(true);
36023         }
36024         if(this.config.hidden){
36025             this.hide();
36026         }
36027         
36028         if (this.unrendered_panels && this.unrendered_panels.length) {
36029             for (var i =0;i< this.unrendered_panels.length; i++) {
36030                 this.add(this.unrendered_panels[i]);
36031             }
36032             this.unrendered_panels = null;
36033             
36034         }
36035         
36036     },
36037     
36038     applyConfig : function(c)
36039     {
36040         /*
36041          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36042             var dh = Roo.DomHelper;
36043             if(c.titlebar !== false){
36044                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36045                 this.collapseBtn.on("click", this.collapse, this);
36046                 this.collapseBtn.enableDisplayMode();
36047                 /*
36048                 if(c.showPin === true || this.showPin){
36049                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36050                     this.stickBtn.enableDisplayMode();
36051                     this.stickBtn.on("click", this.expand, this);
36052                     this.stickBtn.hide();
36053                 }
36054                 
36055             }
36056             */
36057             /** This region's collapsed element
36058             * @type Roo.Element */
36059             /*
36060              *
36061             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36062                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36063             ]}, true);
36064             
36065             if(c.floatable !== false){
36066                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36067                this.collapsedEl.on("click", this.collapseClick, this);
36068             }
36069
36070             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36071                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36072                    id: "message", unselectable: "on", style:{"float":"left"}});
36073                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36074              }
36075             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36076             this.expandBtn.on("click", this.expand, this);
36077             
36078         }
36079         
36080         if(this.collapseBtn){
36081             this.collapseBtn.setVisible(c.collapsible == true);
36082         }
36083         
36084         this.cmargins = c.cmargins || this.cmargins ||
36085                          (this.position == "west" || this.position == "east" ?
36086                              {top: 0, left: 2, right:2, bottom: 0} :
36087                              {top: 2, left: 0, right:0, bottom: 2});
36088         */
36089         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36090         
36091         
36092         this.bottomTabs = c.tabPosition != "top";
36093         
36094         this.autoScroll = c.autoScroll || false;
36095         
36096         
36097        
36098         
36099         this.duration = c.duration || .30;
36100         this.slideDuration = c.slideDuration || .45;
36101         this.config = c;
36102        
36103     },
36104     /**
36105      * Returns true if this region is currently visible.
36106      * @return {Boolean}
36107      */
36108     isVisible : function(){
36109         return this.visible;
36110     },
36111
36112     /**
36113      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36114      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36115      */
36116     //setCollapsedTitle : function(title){
36117     //    title = title || "&#160;";
36118      //   if(this.collapsedTitleTextEl){
36119       //      this.collapsedTitleTextEl.innerHTML = title;
36120        // }
36121     //},
36122
36123     getBox : function(){
36124         var b;
36125       //  if(!this.collapsed){
36126             b = this.el.getBox(false, true);
36127        // }else{
36128           //  b = this.collapsedEl.getBox(false, true);
36129         //}
36130         return b;
36131     },
36132
36133     getMargins : function(){
36134         return this.margins;
36135         //return this.collapsed ? this.cmargins : this.margins;
36136     },
36137 /*
36138     highlight : function(){
36139         this.el.addClass("x-layout-panel-dragover");
36140     },
36141
36142     unhighlight : function(){
36143         this.el.removeClass("x-layout-panel-dragover");
36144     },
36145 */
36146     updateBox : function(box)
36147     {
36148         if (!this.bodyEl) {
36149             return; // not rendered yet..
36150         }
36151         
36152         this.box = box;
36153         if(!this.collapsed){
36154             this.el.dom.style.left = box.x + "px";
36155             this.el.dom.style.top = box.y + "px";
36156             this.updateBody(box.width, box.height);
36157         }else{
36158             this.collapsedEl.dom.style.left = box.x + "px";
36159             this.collapsedEl.dom.style.top = box.y + "px";
36160             this.collapsedEl.setSize(box.width, box.height);
36161         }
36162         if(this.tabs){
36163             this.tabs.autoSizeTabs();
36164         }
36165     },
36166
36167     updateBody : function(w, h)
36168     {
36169         if(w !== null){
36170             this.el.setWidth(w);
36171             w -= this.el.getBorderWidth("rl");
36172             if(this.config.adjustments){
36173                 w += this.config.adjustments[0];
36174             }
36175         }
36176         if(h !== null && h > 0){
36177             this.el.setHeight(h);
36178             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36179             h -= this.el.getBorderWidth("tb");
36180             if(this.config.adjustments){
36181                 h += this.config.adjustments[1];
36182             }
36183             this.bodyEl.setHeight(h);
36184             if(this.tabs){
36185                 h = this.tabs.syncHeight(h);
36186             }
36187         }
36188         if(this.panelSize){
36189             w = w !== null ? w : this.panelSize.width;
36190             h = h !== null ? h : this.panelSize.height;
36191         }
36192         if(this.activePanel){
36193             var el = this.activePanel.getEl();
36194             w = w !== null ? w : el.getWidth();
36195             h = h !== null ? h : el.getHeight();
36196             this.panelSize = {width: w, height: h};
36197             this.activePanel.setSize(w, h);
36198         }
36199         if(Roo.isIE && this.tabs){
36200             this.tabs.el.repaint();
36201         }
36202     },
36203
36204     /**
36205      * Returns the container element for this region.
36206      * @return {Roo.Element}
36207      */
36208     getEl : function(){
36209         return this.el;
36210     },
36211
36212     /**
36213      * Hides this region.
36214      */
36215     hide : function(){
36216         //if(!this.collapsed){
36217             this.el.dom.style.left = "-2000px";
36218             this.el.hide();
36219         //}else{
36220          //   this.collapsedEl.dom.style.left = "-2000px";
36221          //   this.collapsedEl.hide();
36222        // }
36223         this.visible = false;
36224         this.fireEvent("visibilitychange", this, false);
36225     },
36226
36227     /**
36228      * Shows this region if it was previously hidden.
36229      */
36230     show : function(){
36231         //if(!this.collapsed){
36232             this.el.show();
36233         //}else{
36234         //    this.collapsedEl.show();
36235        // }
36236         this.visible = true;
36237         this.fireEvent("visibilitychange", this, true);
36238     },
36239 /*
36240     closeClicked : function(){
36241         if(this.activePanel){
36242             this.remove(this.activePanel);
36243         }
36244     },
36245
36246     collapseClick : function(e){
36247         if(this.isSlid){
36248            e.stopPropagation();
36249            this.slideIn();
36250         }else{
36251            e.stopPropagation();
36252            this.slideOut();
36253         }
36254     },
36255 */
36256     /**
36257      * Collapses this region.
36258      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36259      */
36260     /*
36261     collapse : function(skipAnim, skipCheck = false){
36262         if(this.collapsed) {
36263             return;
36264         }
36265         
36266         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36267             
36268             this.collapsed = true;
36269             if(this.split){
36270                 this.split.el.hide();
36271             }
36272             if(this.config.animate && skipAnim !== true){
36273                 this.fireEvent("invalidated", this);
36274                 this.animateCollapse();
36275             }else{
36276                 this.el.setLocation(-20000,-20000);
36277                 this.el.hide();
36278                 this.collapsedEl.show();
36279                 this.fireEvent("collapsed", this);
36280                 this.fireEvent("invalidated", this);
36281             }
36282         }
36283         
36284     },
36285 */
36286     animateCollapse : function(){
36287         // overridden
36288     },
36289
36290     /**
36291      * Expands this region if it was previously collapsed.
36292      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36293      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36294      */
36295     /*
36296     expand : function(e, skipAnim){
36297         if(e) {
36298             e.stopPropagation();
36299         }
36300         if(!this.collapsed || this.el.hasActiveFx()) {
36301             return;
36302         }
36303         if(this.isSlid){
36304             this.afterSlideIn();
36305             skipAnim = true;
36306         }
36307         this.collapsed = false;
36308         if(this.config.animate && skipAnim !== true){
36309             this.animateExpand();
36310         }else{
36311             this.el.show();
36312             if(this.split){
36313                 this.split.el.show();
36314             }
36315             this.collapsedEl.setLocation(-2000,-2000);
36316             this.collapsedEl.hide();
36317             this.fireEvent("invalidated", this);
36318             this.fireEvent("expanded", this);
36319         }
36320     },
36321 */
36322     animateExpand : function(){
36323         // overridden
36324     },
36325
36326     initTabs : function()
36327     {
36328         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36329         
36330         var ts = new Roo.bootstrap.panel.Tabs({
36331                 el: this.bodyEl.dom,
36332                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36333                 disableTooltips: this.config.disableTabTips,
36334                 toolbar : this.config.toolbar
36335             });
36336         
36337         if(this.config.hideTabs){
36338             ts.stripWrap.setDisplayed(false);
36339         }
36340         this.tabs = ts;
36341         ts.resizeTabs = this.config.resizeTabs === true;
36342         ts.minTabWidth = this.config.minTabWidth || 40;
36343         ts.maxTabWidth = this.config.maxTabWidth || 250;
36344         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36345         ts.monitorResize = false;
36346         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36347         ts.bodyEl.addClass('roo-layout-tabs-body');
36348         this.panels.each(this.initPanelAsTab, this);
36349     },
36350
36351     initPanelAsTab : function(panel){
36352         var ti = this.tabs.addTab(
36353             panel.getEl().id,
36354             panel.getTitle(),
36355             null,
36356             this.config.closeOnTab && panel.isClosable(),
36357             panel.tpl
36358         );
36359         if(panel.tabTip !== undefined){
36360             ti.setTooltip(panel.tabTip);
36361         }
36362         ti.on("activate", function(){
36363               this.setActivePanel(panel);
36364         }, this);
36365         
36366         if(this.config.closeOnTab){
36367             ti.on("beforeclose", function(t, e){
36368                 e.cancel = true;
36369                 this.remove(panel);
36370             }, this);
36371         }
36372         
36373         panel.tabItem = ti;
36374         
36375         return ti;
36376     },
36377
36378     updatePanelTitle : function(panel, title)
36379     {
36380         if(this.activePanel == panel){
36381             this.updateTitle(title);
36382         }
36383         if(this.tabs){
36384             var ti = this.tabs.getTab(panel.getEl().id);
36385             ti.setText(title);
36386             if(panel.tabTip !== undefined){
36387                 ti.setTooltip(panel.tabTip);
36388             }
36389         }
36390     },
36391
36392     updateTitle : function(title){
36393         if(this.titleTextEl && !this.config.title){
36394             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36395         }
36396     },
36397
36398     setActivePanel : function(panel)
36399     {
36400         panel = this.getPanel(panel);
36401         if(this.activePanel && this.activePanel != panel){
36402             if(this.activePanel.setActiveState(false) === false){
36403                 return;
36404             }
36405         }
36406         this.activePanel = panel;
36407         panel.setActiveState(true);
36408         if(this.panelSize){
36409             panel.setSize(this.panelSize.width, this.panelSize.height);
36410         }
36411         if(this.closeBtn){
36412             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36413         }
36414         this.updateTitle(panel.getTitle());
36415         if(this.tabs){
36416             this.fireEvent("invalidated", this);
36417         }
36418         this.fireEvent("panelactivated", this, panel);
36419     },
36420
36421     /**
36422      * Shows the specified panel.
36423      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36424      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36425      */
36426     showPanel : function(panel)
36427     {
36428         panel = this.getPanel(panel);
36429         if(panel){
36430             if(this.tabs){
36431                 var tab = this.tabs.getTab(panel.getEl().id);
36432                 if(tab.isHidden()){
36433                     this.tabs.unhideTab(tab.id);
36434                 }
36435                 tab.activate();
36436             }else{
36437                 this.setActivePanel(panel);
36438             }
36439         }
36440         return panel;
36441     },
36442
36443     /**
36444      * Get the active panel for this region.
36445      * @return {Roo.ContentPanel} The active panel or null
36446      */
36447     getActivePanel : function(){
36448         return this.activePanel;
36449     },
36450
36451     validateVisibility : function(){
36452         if(this.panels.getCount() < 1){
36453             this.updateTitle("&#160;");
36454             this.closeBtn.hide();
36455             this.hide();
36456         }else{
36457             if(!this.isVisible()){
36458                 this.show();
36459             }
36460         }
36461     },
36462
36463     /**
36464      * Adds the passed ContentPanel(s) to this region.
36465      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36466      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36467      */
36468     add : function(panel)
36469     {
36470         if(arguments.length > 1){
36471             for(var i = 0, len = arguments.length; i < len; i++) {
36472                 this.add(arguments[i]);
36473             }
36474             return null;
36475         }
36476         
36477         // if we have not been rendered yet, then we can not really do much of this..
36478         if (!this.bodyEl) {
36479             this.unrendered_panels.push(panel);
36480             return panel;
36481         }
36482         
36483         
36484         
36485         
36486         if(this.hasPanel(panel)){
36487             this.showPanel(panel);
36488             return panel;
36489         }
36490         panel.setRegion(this);
36491         this.panels.add(panel);
36492        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36493             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36494             // and hide them... ???
36495             this.bodyEl.dom.appendChild(panel.getEl().dom);
36496             if(panel.background !== true){
36497                 this.setActivePanel(panel);
36498             }
36499             this.fireEvent("paneladded", this, panel);
36500             return panel;
36501         }
36502         */
36503         if(!this.tabs){
36504             this.initTabs();
36505         }else{
36506             this.initPanelAsTab(panel);
36507         }
36508         
36509         
36510         if(panel.background !== true){
36511             this.tabs.activate(panel.getEl().id);
36512         }
36513         this.fireEvent("paneladded", this, panel);
36514         return panel;
36515     },
36516
36517     /**
36518      * Hides the tab for the specified panel.
36519      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36520      */
36521     hidePanel : function(panel){
36522         if(this.tabs && (panel = this.getPanel(panel))){
36523             this.tabs.hideTab(panel.getEl().id);
36524         }
36525     },
36526
36527     /**
36528      * Unhides the tab for a previously hidden panel.
36529      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36530      */
36531     unhidePanel : function(panel){
36532         if(this.tabs && (panel = this.getPanel(panel))){
36533             this.tabs.unhideTab(panel.getEl().id);
36534         }
36535     },
36536
36537     clearPanels : function(){
36538         while(this.panels.getCount() > 0){
36539              this.remove(this.panels.first());
36540         }
36541     },
36542
36543     /**
36544      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36545      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36546      * @param {Boolean} preservePanel Overrides the config preservePanel option
36547      * @return {Roo.ContentPanel} The panel that was removed
36548      */
36549     remove : function(panel, preservePanel)
36550     {
36551         panel = this.getPanel(panel);
36552         if(!panel){
36553             return null;
36554         }
36555         var e = {};
36556         this.fireEvent("beforeremove", this, panel, e);
36557         if(e.cancel === true){
36558             return null;
36559         }
36560         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36561         var panelId = panel.getId();
36562         this.panels.removeKey(panelId);
36563         if(preservePanel){
36564             document.body.appendChild(panel.getEl().dom);
36565         }
36566         if(this.tabs){
36567             this.tabs.removeTab(panel.getEl().id);
36568         }else if (!preservePanel){
36569             this.bodyEl.dom.removeChild(panel.getEl().dom);
36570         }
36571         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36572             var p = this.panels.first();
36573             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36574             tempEl.appendChild(p.getEl().dom);
36575             this.bodyEl.update("");
36576             this.bodyEl.dom.appendChild(p.getEl().dom);
36577             tempEl = null;
36578             this.updateTitle(p.getTitle());
36579             this.tabs = null;
36580             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36581             this.setActivePanel(p);
36582         }
36583         panel.setRegion(null);
36584         if(this.activePanel == panel){
36585             this.activePanel = null;
36586         }
36587         if(this.config.autoDestroy !== false && preservePanel !== true){
36588             try{panel.destroy();}catch(e){}
36589         }
36590         this.fireEvent("panelremoved", this, panel);
36591         return panel;
36592     },
36593
36594     /**
36595      * Returns the TabPanel component used by this region
36596      * @return {Roo.TabPanel}
36597      */
36598     getTabs : function(){
36599         return this.tabs;
36600     },
36601
36602     createTool : function(parentEl, className){
36603         var btn = Roo.DomHelper.append(parentEl, {
36604             tag: "div",
36605             cls: "x-layout-tools-button",
36606             children: [ {
36607                 tag: "div",
36608                 cls: "roo-layout-tools-button-inner " + className,
36609                 html: "&#160;"
36610             }]
36611         }, true);
36612         btn.addClassOnOver("roo-layout-tools-button-over");
36613         return btn;
36614     }
36615 });/*
36616  * Based on:
36617  * Ext JS Library 1.1.1
36618  * Copyright(c) 2006-2007, Ext JS, LLC.
36619  *
36620  * Originally Released Under LGPL - original licence link has changed is not relivant.
36621  *
36622  * Fork - LGPL
36623  * <script type="text/javascript">
36624  */
36625  
36626
36627
36628 /**
36629  * @class Roo.SplitLayoutRegion
36630  * @extends Roo.LayoutRegion
36631  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36632  */
36633 Roo.bootstrap.layout.Split = function(config){
36634     this.cursor = config.cursor;
36635     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36636 };
36637
36638 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36639 {
36640     splitTip : "Drag to resize.",
36641     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36642     useSplitTips : false,
36643
36644     applyConfig : function(config){
36645         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36646     },
36647     
36648     onRender : function(ctr,pos) {
36649         
36650         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36651         if(!this.config.split){
36652             return;
36653         }
36654         if(!this.split){
36655             
36656             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36657                             tag: "div",
36658                             id: this.el.id + "-split",
36659                             cls: "roo-layout-split roo-layout-split-"+this.position,
36660                             html: "&#160;"
36661             });
36662             /** The SplitBar for this region 
36663             * @type Roo.SplitBar */
36664             // does not exist yet...
36665             Roo.log([this.position, this.orientation]);
36666             
36667             this.split = new Roo.bootstrap.SplitBar({
36668                 dragElement : splitEl,
36669                 resizingElement: this.el,
36670                 orientation : this.orientation
36671             });
36672             
36673             this.split.on("moved", this.onSplitMove, this);
36674             this.split.useShim = this.config.useShim === true;
36675             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36676             if(this.useSplitTips){
36677                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36678             }
36679             //if(config.collapsible){
36680             //    this.split.el.on("dblclick", this.collapse,  this);
36681             //}
36682         }
36683         if(typeof this.config.minSize != "undefined"){
36684             this.split.minSize = this.config.minSize;
36685         }
36686         if(typeof this.config.maxSize != "undefined"){
36687             this.split.maxSize = this.config.maxSize;
36688         }
36689         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36690             this.hideSplitter();
36691         }
36692         
36693     },
36694
36695     getHMaxSize : function(){
36696          var cmax = this.config.maxSize || 10000;
36697          var center = this.mgr.getRegion("center");
36698          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36699     },
36700
36701     getVMaxSize : function(){
36702          var cmax = this.config.maxSize || 10000;
36703          var center = this.mgr.getRegion("center");
36704          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36705     },
36706
36707     onSplitMove : function(split, newSize){
36708         this.fireEvent("resized", this, newSize);
36709     },
36710     
36711     /** 
36712      * Returns the {@link Roo.SplitBar} for this region.
36713      * @return {Roo.SplitBar}
36714      */
36715     getSplitBar : function(){
36716         return this.split;
36717     },
36718     
36719     hide : function(){
36720         this.hideSplitter();
36721         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36722     },
36723
36724     hideSplitter : function(){
36725         if(this.split){
36726             this.split.el.setLocation(-2000,-2000);
36727             this.split.el.hide();
36728         }
36729     },
36730
36731     show : function(){
36732         if(this.split){
36733             this.split.el.show();
36734         }
36735         Roo.bootstrap.layout.Split.superclass.show.call(this);
36736     },
36737     
36738     beforeSlide: function(){
36739         if(Roo.isGecko){// firefox overflow auto bug workaround
36740             this.bodyEl.clip();
36741             if(this.tabs) {
36742                 this.tabs.bodyEl.clip();
36743             }
36744             if(this.activePanel){
36745                 this.activePanel.getEl().clip();
36746                 
36747                 if(this.activePanel.beforeSlide){
36748                     this.activePanel.beforeSlide();
36749                 }
36750             }
36751         }
36752     },
36753     
36754     afterSlide : function(){
36755         if(Roo.isGecko){// firefox overflow auto bug workaround
36756             this.bodyEl.unclip();
36757             if(this.tabs) {
36758                 this.tabs.bodyEl.unclip();
36759             }
36760             if(this.activePanel){
36761                 this.activePanel.getEl().unclip();
36762                 if(this.activePanel.afterSlide){
36763                     this.activePanel.afterSlide();
36764                 }
36765             }
36766         }
36767     },
36768
36769     initAutoHide : function(){
36770         if(this.autoHide !== false){
36771             if(!this.autoHideHd){
36772                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36773                 this.autoHideHd = {
36774                     "mouseout": function(e){
36775                         if(!e.within(this.el, true)){
36776                             st.delay(500);
36777                         }
36778                     },
36779                     "mouseover" : function(e){
36780                         st.cancel();
36781                     },
36782                     scope : this
36783                 };
36784             }
36785             this.el.on(this.autoHideHd);
36786         }
36787     },
36788
36789     clearAutoHide : function(){
36790         if(this.autoHide !== false){
36791             this.el.un("mouseout", this.autoHideHd.mouseout);
36792             this.el.un("mouseover", this.autoHideHd.mouseover);
36793         }
36794     },
36795
36796     clearMonitor : function(){
36797         Roo.get(document).un("click", this.slideInIf, this);
36798     },
36799
36800     // these names are backwards but not changed for compat
36801     slideOut : function(){
36802         if(this.isSlid || this.el.hasActiveFx()){
36803             return;
36804         }
36805         this.isSlid = true;
36806         if(this.collapseBtn){
36807             this.collapseBtn.hide();
36808         }
36809         this.closeBtnState = this.closeBtn.getStyle('display');
36810         this.closeBtn.hide();
36811         if(this.stickBtn){
36812             this.stickBtn.show();
36813         }
36814         this.el.show();
36815         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36816         this.beforeSlide();
36817         this.el.setStyle("z-index", 10001);
36818         this.el.slideIn(this.getSlideAnchor(), {
36819             callback: function(){
36820                 this.afterSlide();
36821                 this.initAutoHide();
36822                 Roo.get(document).on("click", this.slideInIf, this);
36823                 this.fireEvent("slideshow", this);
36824             },
36825             scope: this,
36826             block: true
36827         });
36828     },
36829
36830     afterSlideIn : function(){
36831         this.clearAutoHide();
36832         this.isSlid = false;
36833         this.clearMonitor();
36834         this.el.setStyle("z-index", "");
36835         if(this.collapseBtn){
36836             this.collapseBtn.show();
36837         }
36838         this.closeBtn.setStyle('display', this.closeBtnState);
36839         if(this.stickBtn){
36840             this.stickBtn.hide();
36841         }
36842         this.fireEvent("slidehide", this);
36843     },
36844
36845     slideIn : function(cb){
36846         if(!this.isSlid || this.el.hasActiveFx()){
36847             Roo.callback(cb);
36848             return;
36849         }
36850         this.isSlid = false;
36851         this.beforeSlide();
36852         this.el.slideOut(this.getSlideAnchor(), {
36853             callback: function(){
36854                 this.el.setLeftTop(-10000, -10000);
36855                 this.afterSlide();
36856                 this.afterSlideIn();
36857                 Roo.callback(cb);
36858             },
36859             scope: this,
36860             block: true
36861         });
36862     },
36863     
36864     slideInIf : function(e){
36865         if(!e.within(this.el)){
36866             this.slideIn();
36867         }
36868     },
36869
36870     animateCollapse : function(){
36871         this.beforeSlide();
36872         this.el.setStyle("z-index", 20000);
36873         var anchor = this.getSlideAnchor();
36874         this.el.slideOut(anchor, {
36875             callback : function(){
36876                 this.el.setStyle("z-index", "");
36877                 this.collapsedEl.slideIn(anchor, {duration:.3});
36878                 this.afterSlide();
36879                 this.el.setLocation(-10000,-10000);
36880                 this.el.hide();
36881                 this.fireEvent("collapsed", this);
36882             },
36883             scope: this,
36884             block: true
36885         });
36886     },
36887
36888     animateExpand : function(){
36889         this.beforeSlide();
36890         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36891         this.el.setStyle("z-index", 20000);
36892         this.collapsedEl.hide({
36893             duration:.1
36894         });
36895         this.el.slideIn(this.getSlideAnchor(), {
36896             callback : function(){
36897                 this.el.setStyle("z-index", "");
36898                 this.afterSlide();
36899                 if(this.split){
36900                     this.split.el.show();
36901                 }
36902                 this.fireEvent("invalidated", this);
36903                 this.fireEvent("expanded", this);
36904             },
36905             scope: this,
36906             block: true
36907         });
36908     },
36909
36910     anchors : {
36911         "west" : "left",
36912         "east" : "right",
36913         "north" : "top",
36914         "south" : "bottom"
36915     },
36916
36917     sanchors : {
36918         "west" : "l",
36919         "east" : "r",
36920         "north" : "t",
36921         "south" : "b"
36922     },
36923
36924     canchors : {
36925         "west" : "tl-tr",
36926         "east" : "tr-tl",
36927         "north" : "tl-bl",
36928         "south" : "bl-tl"
36929     },
36930
36931     getAnchor : function(){
36932         return this.anchors[this.position];
36933     },
36934
36935     getCollapseAnchor : function(){
36936         return this.canchors[this.position];
36937     },
36938
36939     getSlideAnchor : function(){
36940         return this.sanchors[this.position];
36941     },
36942
36943     getAlignAdj : function(){
36944         var cm = this.cmargins;
36945         switch(this.position){
36946             case "west":
36947                 return [0, 0];
36948             break;
36949             case "east":
36950                 return [0, 0];
36951             break;
36952             case "north":
36953                 return [0, 0];
36954             break;
36955             case "south":
36956                 return [0, 0];
36957             break;
36958         }
36959     },
36960
36961     getExpandAdj : function(){
36962         var c = this.collapsedEl, cm = this.cmargins;
36963         switch(this.position){
36964             case "west":
36965                 return [-(cm.right+c.getWidth()+cm.left), 0];
36966             break;
36967             case "east":
36968                 return [cm.right+c.getWidth()+cm.left, 0];
36969             break;
36970             case "north":
36971                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36972             break;
36973             case "south":
36974                 return [0, cm.top+cm.bottom+c.getHeight()];
36975             break;
36976         }
36977     }
36978 });/*
36979  * Based on:
36980  * Ext JS Library 1.1.1
36981  * Copyright(c) 2006-2007, Ext JS, LLC.
36982  *
36983  * Originally Released Under LGPL - original licence link has changed is not relivant.
36984  *
36985  * Fork - LGPL
36986  * <script type="text/javascript">
36987  */
36988 /*
36989  * These classes are private internal classes
36990  */
36991 Roo.bootstrap.layout.Center = function(config){
36992     config.region = "center";
36993     Roo.bootstrap.layout.Region.call(this, config);
36994     this.visible = true;
36995     this.minWidth = config.minWidth || 20;
36996     this.minHeight = config.minHeight || 20;
36997 };
36998
36999 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37000     hide : function(){
37001         // center panel can't be hidden
37002     },
37003     
37004     show : function(){
37005         // center panel can't be hidden
37006     },
37007     
37008     getMinWidth: function(){
37009         return this.minWidth;
37010     },
37011     
37012     getMinHeight: function(){
37013         return this.minHeight;
37014     }
37015 });
37016
37017
37018
37019
37020  
37021
37022
37023
37024
37025
37026 Roo.bootstrap.layout.North = function(config)
37027 {
37028     config.region = 'north';
37029     config.cursor = 'n-resize';
37030     
37031     Roo.bootstrap.layout.Split.call(this, config);
37032     
37033     
37034     if(this.split){
37035         this.split.placement = Roo.bootstrap.SplitBar.TOP;
37036         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37037         this.split.el.addClass("roo-layout-split-v");
37038     }
37039     var size = config.initialSize || config.height;
37040     if(typeof size != "undefined"){
37041         this.el.setHeight(size);
37042     }
37043 };
37044 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37045 {
37046     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37047     
37048     
37049     
37050     getBox : function(){
37051         if(this.collapsed){
37052             return this.collapsedEl.getBox();
37053         }
37054         var box = this.el.getBox();
37055         if(this.split){
37056             box.height += this.split.el.getHeight();
37057         }
37058         return box;
37059     },
37060     
37061     updateBox : function(box){
37062         if(this.split && !this.collapsed){
37063             box.height -= this.split.el.getHeight();
37064             this.split.el.setLeft(box.x);
37065             this.split.el.setTop(box.y+box.height);
37066             this.split.el.setWidth(box.width);
37067         }
37068         if(this.collapsed){
37069             this.updateBody(box.width, null);
37070         }
37071         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37072     }
37073 });
37074
37075
37076
37077
37078
37079 Roo.bootstrap.layout.South = function(config){
37080     config.region = 'south';
37081     config.cursor = 's-resize';
37082     Roo.bootstrap.layout.Split.call(this, config);
37083     if(this.split){
37084         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37085         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37086         this.split.el.addClass("roo-layout-split-v");
37087     }
37088     var size = config.initialSize || config.height;
37089     if(typeof size != "undefined"){
37090         this.el.setHeight(size);
37091     }
37092 };
37093
37094 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37095     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37096     getBox : function(){
37097         if(this.collapsed){
37098             return this.collapsedEl.getBox();
37099         }
37100         var box = this.el.getBox();
37101         if(this.split){
37102             var sh = this.split.el.getHeight();
37103             box.height += sh;
37104             box.y -= sh;
37105         }
37106         return box;
37107     },
37108     
37109     updateBox : function(box){
37110         if(this.split && !this.collapsed){
37111             var sh = this.split.el.getHeight();
37112             box.height -= sh;
37113             box.y += sh;
37114             this.split.el.setLeft(box.x);
37115             this.split.el.setTop(box.y-sh);
37116             this.split.el.setWidth(box.width);
37117         }
37118         if(this.collapsed){
37119             this.updateBody(box.width, null);
37120         }
37121         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37122     }
37123 });
37124
37125 Roo.bootstrap.layout.East = function(config){
37126     config.region = "east";
37127     config.cursor = "e-resize";
37128     Roo.bootstrap.layout.Split.call(this, config);
37129     if(this.split){
37130         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37131         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37132         this.split.el.addClass("roo-layout-split-h");
37133     }
37134     var size = config.initialSize || config.width;
37135     if(typeof size != "undefined"){
37136         this.el.setWidth(size);
37137     }
37138 };
37139 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37140     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37141     getBox : function(){
37142         if(this.collapsed){
37143             return this.collapsedEl.getBox();
37144         }
37145         var box = this.el.getBox();
37146         if(this.split){
37147             var sw = this.split.el.getWidth();
37148             box.width += sw;
37149             box.x -= sw;
37150         }
37151         return box;
37152     },
37153
37154     updateBox : function(box){
37155         if(this.split && !this.collapsed){
37156             var sw = this.split.el.getWidth();
37157             box.width -= sw;
37158             this.split.el.setLeft(box.x);
37159             this.split.el.setTop(box.y);
37160             this.split.el.setHeight(box.height);
37161             box.x += sw;
37162         }
37163         if(this.collapsed){
37164             this.updateBody(null, box.height);
37165         }
37166         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37167     }
37168 });
37169
37170 Roo.bootstrap.layout.West = function(config){
37171     config.region = "west";
37172     config.cursor = "w-resize";
37173     
37174     Roo.bootstrap.layout.Split.call(this, config);
37175     if(this.split){
37176         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37177         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37178         this.split.el.addClass("roo-layout-split-h");
37179     }
37180     
37181 };
37182 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37183     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37184     
37185     onRender: function(ctr, pos)
37186     {
37187         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37188         var size = this.config.initialSize || this.config.width;
37189         if(typeof size != "undefined"){
37190             this.el.setWidth(size);
37191         }
37192     },
37193     
37194     getBox : function(){
37195         if(this.collapsed){
37196             return this.collapsedEl.getBox();
37197         }
37198         var box = this.el.getBox();
37199         if(this.split){
37200             box.width += this.split.el.getWidth();
37201         }
37202         return box;
37203     },
37204     
37205     updateBox : function(box){
37206         if(this.split && !this.collapsed){
37207             var sw = this.split.el.getWidth();
37208             box.width -= sw;
37209             this.split.el.setLeft(box.x+box.width);
37210             this.split.el.setTop(box.y);
37211             this.split.el.setHeight(box.height);
37212         }
37213         if(this.collapsed){
37214             this.updateBody(null, box.height);
37215         }
37216         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37217     }
37218 });
37219 Roo.namespace("Roo.bootstrap.panel");/*
37220  * Based on:
37221  * Ext JS Library 1.1.1
37222  * Copyright(c) 2006-2007, Ext JS, LLC.
37223  *
37224  * Originally Released Under LGPL - original licence link has changed is not relivant.
37225  *
37226  * Fork - LGPL
37227  * <script type="text/javascript">
37228  */
37229 /**
37230  * @class Roo.ContentPanel
37231  * @extends Roo.util.Observable
37232  * A basic ContentPanel element.
37233  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37234  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37235  * @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
37236  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37237  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37238  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37239  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37240  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37241  * @cfg {String} title          The title for this panel
37242  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37243  * @cfg {String} url            Calls {@link #setUrl} with this value
37244  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37245  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37246  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37247  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37248  * @cfg {Boolean} badges render the badges
37249
37250  * @constructor
37251  * Create a new ContentPanel.
37252  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37253  * @param {String/Object} config A string to set only the title or a config object
37254  * @param {String} content (optional) Set the HTML content for this panel
37255  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37256  */
37257 Roo.bootstrap.panel.Content = function( config){
37258     
37259     this.tpl = config.tpl || false;
37260     
37261     var el = config.el;
37262     var content = config.content;
37263
37264     if(config.autoCreate){ // xtype is available if this is called from factory
37265         el = Roo.id();
37266     }
37267     this.el = Roo.get(el);
37268     if(!this.el && config && config.autoCreate){
37269         if(typeof config.autoCreate == "object"){
37270             if(!config.autoCreate.id){
37271                 config.autoCreate.id = config.id||el;
37272             }
37273             this.el = Roo.DomHelper.append(document.body,
37274                         config.autoCreate, true);
37275         }else{
37276             var elcfg =  {   tag: "div",
37277                             cls: "roo-layout-inactive-content",
37278                             id: config.id||el
37279                             };
37280             if (config.html) {
37281                 elcfg.html = config.html;
37282                 
37283             }
37284                         
37285             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37286         }
37287     } 
37288     this.closable = false;
37289     this.loaded = false;
37290     this.active = false;
37291    
37292       
37293     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37294         
37295         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37296         
37297         this.wrapEl = this.el; //this.el.wrap();
37298         var ti = [];
37299         if (config.toolbar.items) {
37300             ti = config.toolbar.items ;
37301             delete config.toolbar.items ;
37302         }
37303         
37304         var nitems = [];
37305         this.toolbar.render(this.wrapEl, 'before');
37306         for(var i =0;i < ti.length;i++) {
37307           //  Roo.log(['add child', items[i]]);
37308             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37309         }
37310         this.toolbar.items = nitems;
37311         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37312         delete config.toolbar;
37313         
37314     }
37315     /*
37316     // xtype created footer. - not sure if will work as we normally have to render first..
37317     if (this.footer && !this.footer.el && this.footer.xtype) {
37318         if (!this.wrapEl) {
37319             this.wrapEl = this.el.wrap();
37320         }
37321     
37322         this.footer.container = this.wrapEl.createChild();
37323          
37324         this.footer = Roo.factory(this.footer, Roo);
37325         
37326     }
37327     */
37328     
37329      if(typeof config == "string"){
37330         this.title = config;
37331     }else{
37332         Roo.apply(this, config);
37333     }
37334     
37335     if(this.resizeEl){
37336         this.resizeEl = Roo.get(this.resizeEl, true);
37337     }else{
37338         this.resizeEl = this.el;
37339     }
37340     // handle view.xtype
37341     
37342  
37343     
37344     
37345     this.addEvents({
37346         /**
37347          * @event activate
37348          * Fires when this panel is activated. 
37349          * @param {Roo.ContentPanel} this
37350          */
37351         "activate" : true,
37352         /**
37353          * @event deactivate
37354          * Fires when this panel is activated. 
37355          * @param {Roo.ContentPanel} this
37356          */
37357         "deactivate" : true,
37358
37359         /**
37360          * @event resize
37361          * Fires when this panel is resized if fitToFrame is true.
37362          * @param {Roo.ContentPanel} this
37363          * @param {Number} width The width after any component adjustments
37364          * @param {Number} height The height after any component adjustments
37365          */
37366         "resize" : true,
37367         
37368          /**
37369          * @event render
37370          * Fires when this tab is created
37371          * @param {Roo.ContentPanel} this
37372          */
37373         "render" : true
37374         
37375         
37376         
37377     });
37378     
37379
37380     
37381     
37382     if(this.autoScroll){
37383         this.resizeEl.setStyle("overflow", "auto");
37384     } else {
37385         // fix randome scrolling
37386         //this.el.on('scroll', function() {
37387         //    Roo.log('fix random scolling');
37388         //    this.scrollTo('top',0); 
37389         //});
37390     }
37391     content = content || this.content;
37392     if(content){
37393         this.setContent(content);
37394     }
37395     if(config && config.url){
37396         this.setUrl(this.url, this.params, this.loadOnce);
37397     }
37398     
37399     
37400     
37401     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37402     
37403     if (this.view && typeof(this.view.xtype) != 'undefined') {
37404         this.view.el = this.el.appendChild(document.createElement("div"));
37405         this.view = Roo.factory(this.view); 
37406         this.view.render  &&  this.view.render(false, '');  
37407     }
37408     
37409     
37410     this.fireEvent('render', this);
37411 };
37412
37413 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37414     
37415     tabTip : '',
37416     
37417     setRegion : function(region){
37418         this.region = region;
37419         this.setActiveClass(region && !this.background);
37420     },
37421     
37422     
37423     setActiveClass: function(state)
37424     {
37425         if(state){
37426            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37427            this.el.setStyle('position','relative');
37428         }else{
37429            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37430            this.el.setStyle('position', 'absolute');
37431         } 
37432     },
37433     
37434     /**
37435      * Returns the toolbar for this Panel if one was configured. 
37436      * @return {Roo.Toolbar} 
37437      */
37438     getToolbar : function(){
37439         return this.toolbar;
37440     },
37441     
37442     setActiveState : function(active)
37443     {
37444         this.active = active;
37445         this.setActiveClass(active);
37446         if(!active){
37447             if(this.fireEvent("deactivate", this) === false){
37448                 return false;
37449             }
37450             return true;
37451         }
37452         this.fireEvent("activate", this);
37453         return true;
37454     },
37455     /**
37456      * Updates this panel's element
37457      * @param {String} content The new content
37458      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37459     */
37460     setContent : function(content, loadScripts){
37461         this.el.update(content, loadScripts);
37462     },
37463
37464     ignoreResize : function(w, h){
37465         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37466             return true;
37467         }else{
37468             this.lastSize = {width: w, height: h};
37469             return false;
37470         }
37471     },
37472     /**
37473      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37474      * @return {Roo.UpdateManager} The UpdateManager
37475      */
37476     getUpdateManager : function(){
37477         return this.el.getUpdateManager();
37478     },
37479      /**
37480      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37481      * @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:
37482 <pre><code>
37483 panel.load({
37484     url: "your-url.php",
37485     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37486     callback: yourFunction,
37487     scope: yourObject, //(optional scope)
37488     discardUrl: false,
37489     nocache: false,
37490     text: "Loading...",
37491     timeout: 30,
37492     scripts: false
37493 });
37494 </code></pre>
37495      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37496      * 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.
37497      * @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}
37498      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37499      * @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.
37500      * @return {Roo.ContentPanel} this
37501      */
37502     load : function(){
37503         var um = this.el.getUpdateManager();
37504         um.update.apply(um, arguments);
37505         return this;
37506     },
37507
37508
37509     /**
37510      * 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.
37511      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37512      * @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)
37513      * @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)
37514      * @return {Roo.UpdateManager} The UpdateManager
37515      */
37516     setUrl : function(url, params, loadOnce){
37517         if(this.refreshDelegate){
37518             this.removeListener("activate", this.refreshDelegate);
37519         }
37520         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37521         this.on("activate", this.refreshDelegate);
37522         return this.el.getUpdateManager();
37523     },
37524     
37525     _handleRefresh : function(url, params, loadOnce){
37526         if(!loadOnce || !this.loaded){
37527             var updater = this.el.getUpdateManager();
37528             updater.update(url, params, this._setLoaded.createDelegate(this));
37529         }
37530     },
37531     
37532     _setLoaded : function(){
37533         this.loaded = true;
37534     }, 
37535     
37536     /**
37537      * Returns this panel's id
37538      * @return {String} 
37539      */
37540     getId : function(){
37541         return this.el.id;
37542     },
37543     
37544     /** 
37545      * Returns this panel's element - used by regiosn to add.
37546      * @return {Roo.Element} 
37547      */
37548     getEl : function(){
37549         return this.wrapEl || this.el;
37550     },
37551     
37552    
37553     
37554     adjustForComponents : function(width, height)
37555     {
37556         //Roo.log('adjustForComponents ');
37557         if(this.resizeEl != this.el){
37558             width -= this.el.getFrameWidth('lr');
37559             height -= this.el.getFrameWidth('tb');
37560         }
37561         if(this.toolbar){
37562             var te = this.toolbar.getEl();
37563             te.setWidth(width);
37564             height -= te.getHeight();
37565         }
37566         if(this.footer){
37567             var te = this.footer.getEl();
37568             te.setWidth(width);
37569             height -= te.getHeight();
37570         }
37571         
37572         
37573         if(this.adjustments){
37574             width += this.adjustments[0];
37575             height += this.adjustments[1];
37576         }
37577         return {"width": width, "height": height};
37578     },
37579     
37580     setSize : function(width, height){
37581         if(this.fitToFrame && !this.ignoreResize(width, height)){
37582             if(this.fitContainer && this.resizeEl != this.el){
37583                 this.el.setSize(width, height);
37584             }
37585             var size = this.adjustForComponents(width, height);
37586             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37587             this.fireEvent('resize', this, size.width, size.height);
37588         }
37589     },
37590     
37591     /**
37592      * Returns this panel's title
37593      * @return {String} 
37594      */
37595     getTitle : function(){
37596         
37597         if (typeof(this.title) != 'object') {
37598             return this.title;
37599         }
37600         
37601         var t = '';
37602         for (var k in this.title) {
37603             if (!this.title.hasOwnProperty(k)) {
37604                 continue;
37605             }
37606             
37607             if (k.indexOf('-') >= 0) {
37608                 var s = k.split('-');
37609                 for (var i = 0; i<s.length; i++) {
37610                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37611                 }
37612             } else {
37613                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37614             }
37615         }
37616         return t;
37617     },
37618     
37619     /**
37620      * Set this panel's title
37621      * @param {String} title
37622      */
37623     setTitle : function(title){
37624         this.title = title;
37625         if(this.region){
37626             this.region.updatePanelTitle(this, title);
37627         }
37628     },
37629     
37630     /**
37631      * Returns true is this panel was configured to be closable
37632      * @return {Boolean} 
37633      */
37634     isClosable : function(){
37635         return this.closable;
37636     },
37637     
37638     beforeSlide : function(){
37639         this.el.clip();
37640         this.resizeEl.clip();
37641     },
37642     
37643     afterSlide : function(){
37644         this.el.unclip();
37645         this.resizeEl.unclip();
37646     },
37647     
37648     /**
37649      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37650      *   Will fail silently if the {@link #setUrl} method has not been called.
37651      *   This does not activate the panel, just updates its content.
37652      */
37653     refresh : function(){
37654         if(this.refreshDelegate){
37655            this.loaded = false;
37656            this.refreshDelegate();
37657         }
37658     },
37659     
37660     /**
37661      * Destroys this panel
37662      */
37663     destroy : function(){
37664         this.el.removeAllListeners();
37665         var tempEl = document.createElement("span");
37666         tempEl.appendChild(this.el.dom);
37667         tempEl.innerHTML = "";
37668         this.el.remove();
37669         this.el = null;
37670     },
37671     
37672     /**
37673      * form - if the content panel contains a form - this is a reference to it.
37674      * @type {Roo.form.Form}
37675      */
37676     form : false,
37677     /**
37678      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37679      *    This contains a reference to it.
37680      * @type {Roo.View}
37681      */
37682     view : false,
37683     
37684       /**
37685      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37686      * <pre><code>
37687
37688 layout.addxtype({
37689        xtype : 'Form',
37690        items: [ .... ]
37691    }
37692 );
37693
37694 </code></pre>
37695      * @param {Object} cfg Xtype definition of item to add.
37696      */
37697     
37698     
37699     getChildContainer: function () {
37700         return this.getEl();
37701     }
37702     
37703     
37704     /*
37705         var  ret = new Roo.factory(cfg);
37706         return ret;
37707         
37708         
37709         // add form..
37710         if (cfg.xtype.match(/^Form$/)) {
37711             
37712             var el;
37713             //if (this.footer) {
37714             //    el = this.footer.container.insertSibling(false, 'before');
37715             //} else {
37716                 el = this.el.createChild();
37717             //}
37718
37719             this.form = new  Roo.form.Form(cfg);
37720             
37721             
37722             if ( this.form.allItems.length) {
37723                 this.form.render(el.dom);
37724             }
37725             return this.form;
37726         }
37727         // should only have one of theses..
37728         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37729             // views.. should not be just added - used named prop 'view''
37730             
37731             cfg.el = this.el.appendChild(document.createElement("div"));
37732             // factory?
37733             
37734             var ret = new Roo.factory(cfg);
37735              
37736              ret.render && ret.render(false, ''); // render blank..
37737             this.view = ret;
37738             return ret;
37739         }
37740         return false;
37741     }
37742     \*/
37743 });
37744  
37745 /**
37746  * @class Roo.bootstrap.panel.Grid
37747  * @extends Roo.bootstrap.panel.Content
37748  * @constructor
37749  * Create a new GridPanel.
37750  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37751  * @param {Object} config A the config object
37752   
37753  */
37754
37755
37756
37757 Roo.bootstrap.panel.Grid = function(config)
37758 {
37759     
37760       
37761     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37762         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37763
37764     config.el = this.wrapper;
37765     //this.el = this.wrapper;
37766     
37767       if (config.container) {
37768         // ctor'ed from a Border/panel.grid
37769         
37770         
37771         this.wrapper.setStyle("overflow", "hidden");
37772         this.wrapper.addClass('roo-grid-container');
37773
37774     }
37775     
37776     
37777     if(config.toolbar){
37778         var tool_el = this.wrapper.createChild();    
37779         this.toolbar = Roo.factory(config.toolbar);
37780         var ti = [];
37781         if (config.toolbar.items) {
37782             ti = config.toolbar.items ;
37783             delete config.toolbar.items ;
37784         }
37785         
37786         var nitems = [];
37787         this.toolbar.render(tool_el);
37788         for(var i =0;i < ti.length;i++) {
37789           //  Roo.log(['add child', items[i]]);
37790             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37791         }
37792         this.toolbar.items = nitems;
37793         
37794         delete config.toolbar;
37795     }
37796     
37797     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37798     config.grid.scrollBody = true;;
37799     config.grid.monitorWindowResize = false; // turn off autosizing
37800     config.grid.autoHeight = false;
37801     config.grid.autoWidth = false;
37802     
37803     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37804     
37805     if (config.background) {
37806         // render grid on panel activation (if panel background)
37807         this.on('activate', function(gp) {
37808             if (!gp.grid.rendered) {
37809                 gp.grid.render(this.wrapper);
37810                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37811             }
37812         });
37813             
37814     } else {
37815         this.grid.render(this.wrapper);
37816         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37817
37818     }
37819     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37820     // ??? needed ??? config.el = this.wrapper;
37821     
37822     
37823     
37824   
37825     // xtype created footer. - not sure if will work as we normally have to render first..
37826     if (this.footer && !this.footer.el && this.footer.xtype) {
37827         
37828         var ctr = this.grid.getView().getFooterPanel(true);
37829         this.footer.dataSource = this.grid.dataSource;
37830         this.footer = Roo.factory(this.footer, Roo);
37831         this.footer.render(ctr);
37832         
37833     }
37834     
37835     
37836     
37837     
37838      
37839 };
37840
37841 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37842     getId : function(){
37843         return this.grid.id;
37844     },
37845     
37846     /**
37847      * Returns the grid for this panel
37848      * @return {Roo.bootstrap.Table} 
37849      */
37850     getGrid : function(){
37851         return this.grid;    
37852     },
37853     
37854     setSize : function(width, height){
37855         if(!this.ignoreResize(width, height)){
37856             var grid = this.grid;
37857             var size = this.adjustForComponents(width, height);
37858             var gridel = grid.getGridEl();
37859             gridel.setSize(size.width, size.height);
37860             /*
37861             var thd = grid.getGridEl().select('thead',true).first();
37862             var tbd = grid.getGridEl().select('tbody', true).first();
37863             if (tbd) {
37864                 tbd.setSize(width, height - thd.getHeight());
37865             }
37866             */
37867             grid.autoSize();
37868         }
37869     },
37870      
37871     
37872     
37873     beforeSlide : function(){
37874         this.grid.getView().scroller.clip();
37875     },
37876     
37877     afterSlide : function(){
37878         this.grid.getView().scroller.unclip();
37879     },
37880     
37881     destroy : function(){
37882         this.grid.destroy();
37883         delete this.grid;
37884         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37885     }
37886 });
37887
37888 /**
37889  * @class Roo.bootstrap.panel.Nest
37890  * @extends Roo.bootstrap.panel.Content
37891  * @constructor
37892  * Create a new Panel, that can contain a layout.Border.
37893  * 
37894  * 
37895  * @param {Roo.BorderLayout} layout The layout for this panel
37896  * @param {String/Object} config A string to set only the title or a config object
37897  */
37898 Roo.bootstrap.panel.Nest = function(config)
37899 {
37900     // construct with only one argument..
37901     /* FIXME - implement nicer consturctors
37902     if (layout.layout) {
37903         config = layout;
37904         layout = config.layout;
37905         delete config.layout;
37906     }
37907     if (layout.xtype && !layout.getEl) {
37908         // then layout needs constructing..
37909         layout = Roo.factory(layout, Roo);
37910     }
37911     */
37912     
37913     config.el =  config.layout.getEl();
37914     
37915     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37916     
37917     config.layout.monitorWindowResize = false; // turn off autosizing
37918     this.layout = config.layout;
37919     this.layout.getEl().addClass("roo-layout-nested-layout");
37920     
37921     
37922     
37923     
37924 };
37925
37926 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37927
37928     setSize : function(width, height){
37929         if(!this.ignoreResize(width, height)){
37930             var size = this.adjustForComponents(width, height);
37931             var el = this.layout.getEl();
37932             if (size.height < 1) {
37933                 el.setWidth(size.width);   
37934             } else {
37935                 el.setSize(size.width, size.height);
37936             }
37937             var touch = el.dom.offsetWidth;
37938             this.layout.layout();
37939             // ie requires a double layout on the first pass
37940             if(Roo.isIE && !this.initialized){
37941                 this.initialized = true;
37942                 this.layout.layout();
37943             }
37944         }
37945     },
37946     
37947     // activate all subpanels if not currently active..
37948     
37949     setActiveState : function(active){
37950         this.active = active;
37951         this.setActiveClass(active);
37952         
37953         if(!active){
37954             this.fireEvent("deactivate", this);
37955             return;
37956         }
37957         
37958         this.fireEvent("activate", this);
37959         // not sure if this should happen before or after..
37960         if (!this.layout) {
37961             return; // should not happen..
37962         }
37963         var reg = false;
37964         for (var r in this.layout.regions) {
37965             reg = this.layout.getRegion(r);
37966             if (reg.getActivePanel()) {
37967                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37968                 reg.setActivePanel(reg.getActivePanel());
37969                 continue;
37970             }
37971             if (!reg.panels.length) {
37972                 continue;
37973             }
37974             reg.showPanel(reg.getPanel(0));
37975         }
37976         
37977         
37978         
37979         
37980     },
37981     
37982     /**
37983      * Returns the nested BorderLayout for this panel
37984      * @return {Roo.BorderLayout} 
37985      */
37986     getLayout : function(){
37987         return this.layout;
37988     },
37989     
37990      /**
37991      * Adds a xtype elements to the layout of the nested panel
37992      * <pre><code>
37993
37994 panel.addxtype({
37995        xtype : 'ContentPanel',
37996        region: 'west',
37997        items: [ .... ]
37998    }
37999 );
38000
38001 panel.addxtype({
38002         xtype : 'NestedLayoutPanel',
38003         region: 'west',
38004         layout: {
38005            center: { },
38006            west: { }   
38007         },
38008         items : [ ... list of content panels or nested layout panels.. ]
38009    }
38010 );
38011 </code></pre>
38012      * @param {Object} cfg Xtype definition of item to add.
38013      */
38014     addxtype : function(cfg) {
38015         return this.layout.addxtype(cfg);
38016     
38017     }
38018 });        /*
38019  * Based on:
38020  * Ext JS Library 1.1.1
38021  * Copyright(c) 2006-2007, Ext JS, LLC.
38022  *
38023  * Originally Released Under LGPL - original licence link has changed is not relivant.
38024  *
38025  * Fork - LGPL
38026  * <script type="text/javascript">
38027  */
38028 /**
38029  * @class Roo.TabPanel
38030  * @extends Roo.util.Observable
38031  * A lightweight tab container.
38032  * <br><br>
38033  * Usage:
38034  * <pre><code>
38035 // basic tabs 1, built from existing content
38036 var tabs = new Roo.TabPanel("tabs1");
38037 tabs.addTab("script", "View Script");
38038 tabs.addTab("markup", "View Markup");
38039 tabs.activate("script");
38040
38041 // more advanced tabs, built from javascript
38042 var jtabs = new Roo.TabPanel("jtabs");
38043 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38044
38045 // set up the UpdateManager
38046 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38047 var updater = tab2.getUpdateManager();
38048 updater.setDefaultUrl("ajax1.htm");
38049 tab2.on('activate', updater.refresh, updater, true);
38050
38051 // Use setUrl for Ajax loading
38052 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38053 tab3.setUrl("ajax2.htm", null, true);
38054
38055 // Disabled tab
38056 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38057 tab4.disable();
38058
38059 jtabs.activate("jtabs-1");
38060  * </code></pre>
38061  * @constructor
38062  * Create a new TabPanel.
38063  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38064  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38065  */
38066 Roo.bootstrap.panel.Tabs = function(config){
38067     /**
38068     * The container element for this TabPanel.
38069     * @type Roo.Element
38070     */
38071     this.el = Roo.get(config.el);
38072     delete config.el;
38073     if(config){
38074         if(typeof config == "boolean"){
38075             this.tabPosition = config ? "bottom" : "top";
38076         }else{
38077             Roo.apply(this, config);
38078         }
38079     }
38080     
38081     if(this.tabPosition == "bottom"){
38082         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38083         this.el.addClass("roo-tabs-bottom");
38084     }
38085     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38086     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38087     this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38088     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38089     if(Roo.isIE){
38090         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38091     }
38092     if(this.tabPosition != "bottom"){
38093         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38094          * @type Roo.Element
38095          */
38096         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38097         this.el.addClass("roo-tabs-top");
38098     }
38099     this.items = [];
38100
38101     this.bodyEl.setStyle("position", "relative");
38102
38103     this.active = null;
38104     this.activateDelegate = this.activate.createDelegate(this);
38105
38106     this.addEvents({
38107         /**
38108          * @event tabchange
38109          * Fires when the active tab changes
38110          * @param {Roo.TabPanel} this
38111          * @param {Roo.TabPanelItem} activePanel The new active tab
38112          */
38113         "tabchange": true,
38114         /**
38115          * @event beforetabchange
38116          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38117          * @param {Roo.TabPanel} this
38118          * @param {Object} e Set cancel to true on this object to cancel the tab change
38119          * @param {Roo.TabPanelItem} tab The tab being changed to
38120          */
38121         "beforetabchange" : true
38122     });
38123
38124     Roo.EventManager.onWindowResize(this.onResize, this);
38125     this.cpad = this.el.getPadding("lr");
38126     this.hiddenCount = 0;
38127
38128
38129     // toolbar on the tabbar support...
38130     if (this.toolbar) {
38131         alert("no toolbar support yet");
38132         this.toolbar  = false;
38133         /*
38134         var tcfg = this.toolbar;
38135         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38136         this.toolbar = new Roo.Toolbar(tcfg);
38137         if (Roo.isSafari) {
38138             var tbl = tcfg.container.child('table', true);
38139             tbl.setAttribute('width', '100%');
38140         }
38141         */
38142         
38143     }
38144    
38145
38146
38147     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38148 };
38149
38150 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38151     /*
38152      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38153      */
38154     tabPosition : "top",
38155     /*
38156      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38157      */
38158     currentTabWidth : 0,
38159     /*
38160      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38161      */
38162     minTabWidth : 40,
38163     /*
38164      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38165      */
38166     maxTabWidth : 250,
38167     /*
38168      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38169      */
38170     preferredTabWidth : 175,
38171     /*
38172      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38173      */
38174     resizeTabs : false,
38175     /*
38176      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38177      */
38178     monitorResize : true,
38179     /*
38180      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38181      */
38182     toolbar : false,
38183
38184     /**
38185      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38186      * @param {String} id The id of the div to use <b>or create</b>
38187      * @param {String} text The text for the tab
38188      * @param {String} content (optional) Content to put in the TabPanelItem body
38189      * @param {Boolean} closable (optional) True to create a close icon on the tab
38190      * @return {Roo.TabPanelItem} The created TabPanelItem
38191      */
38192     addTab : function(id, text, content, closable, tpl)
38193     {
38194         var item = new Roo.bootstrap.panel.TabItem({
38195             panel: this,
38196             id : id,
38197             text : text,
38198             closable : closable,
38199             tpl : tpl
38200         });
38201         this.addTabItem(item);
38202         if(content){
38203             item.setContent(content);
38204         }
38205         return item;
38206     },
38207
38208     /**
38209      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38210      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38211      * @return {Roo.TabPanelItem}
38212      */
38213     getTab : function(id){
38214         return this.items[id];
38215     },
38216
38217     /**
38218      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38219      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38220      */
38221     hideTab : function(id){
38222         var t = this.items[id];
38223         if(!t.isHidden()){
38224            t.setHidden(true);
38225            this.hiddenCount++;
38226            this.autoSizeTabs();
38227         }
38228     },
38229
38230     /**
38231      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38232      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38233      */
38234     unhideTab : function(id){
38235         var t = this.items[id];
38236         if(t.isHidden()){
38237            t.setHidden(false);
38238            this.hiddenCount--;
38239            this.autoSizeTabs();
38240         }
38241     },
38242
38243     /**
38244      * Adds an existing {@link Roo.TabPanelItem}.
38245      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38246      */
38247     addTabItem : function(item)
38248     {
38249         this.items[item.id] = item;
38250         this.items.push(item);
38251         this.autoSizeTabs();
38252       //  if(this.resizeTabs){
38253     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38254   //         this.autoSizeTabs();
38255 //        }else{
38256 //            item.autoSize();
38257        // }
38258     },
38259
38260     /**
38261      * Removes a {@link Roo.TabPanelItem}.
38262      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38263      */
38264     removeTab : function(id){
38265         var items = this.items;
38266         var tab = items[id];
38267         if(!tab) { return; }
38268         var index = items.indexOf(tab);
38269         if(this.active == tab && items.length > 1){
38270             var newTab = this.getNextAvailable(index);
38271             if(newTab) {
38272                 newTab.activate();
38273             }
38274         }
38275         this.stripEl.dom.removeChild(tab.pnode.dom);
38276         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38277             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38278         }
38279         items.splice(index, 1);
38280         delete this.items[tab.id];
38281         tab.fireEvent("close", tab);
38282         tab.purgeListeners();
38283         this.autoSizeTabs();
38284     },
38285
38286     getNextAvailable : function(start){
38287         var items = this.items;
38288         var index = start;
38289         // look for a next tab that will slide over to
38290         // replace the one being removed
38291         while(index < items.length){
38292             var item = items[++index];
38293             if(item && !item.isHidden()){
38294                 return item;
38295             }
38296         }
38297         // if one isn't found select the previous tab (on the left)
38298         index = start;
38299         while(index >= 0){
38300             var item = items[--index];
38301             if(item && !item.isHidden()){
38302                 return item;
38303             }
38304         }
38305         return null;
38306     },
38307
38308     /**
38309      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38310      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38311      */
38312     disableTab : function(id){
38313         var tab = this.items[id];
38314         if(tab && this.active != tab){
38315             tab.disable();
38316         }
38317     },
38318
38319     /**
38320      * Enables a {@link Roo.TabPanelItem} that is disabled.
38321      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38322      */
38323     enableTab : function(id){
38324         var tab = this.items[id];
38325         tab.enable();
38326     },
38327
38328     /**
38329      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38330      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38331      * @return {Roo.TabPanelItem} The TabPanelItem.
38332      */
38333     activate : function(id)
38334     {
38335         var tab = this.items[id];
38336         if(!tab){
38337             return null;
38338         }
38339         if(tab == this.active || tab.disabled){
38340             return tab;
38341         }
38342         var e = {};
38343         this.fireEvent("beforetabchange", this, e, tab);
38344         if(e.cancel !== true && !tab.disabled){
38345             if(this.active){
38346                 this.active.hide();
38347             }
38348             this.active = this.items[id];
38349             this.active.show();
38350             this.fireEvent("tabchange", this, this.active);
38351         }
38352         return tab;
38353     },
38354
38355     /**
38356      * Gets the active {@link Roo.TabPanelItem}.
38357      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38358      */
38359     getActiveTab : function(){
38360         return this.active;
38361     },
38362
38363     /**
38364      * Updates the tab body element to fit the height of the container element
38365      * for overflow scrolling
38366      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38367      */
38368     syncHeight : function(targetHeight){
38369         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38370         var bm = this.bodyEl.getMargins();
38371         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38372         this.bodyEl.setHeight(newHeight);
38373         return newHeight;
38374     },
38375
38376     onResize : function(){
38377         if(this.monitorResize){
38378             this.autoSizeTabs();
38379         }
38380     },
38381
38382     /**
38383      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38384      */
38385     beginUpdate : function(){
38386         this.updating = true;
38387     },
38388
38389     /**
38390      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38391      */
38392     endUpdate : function(){
38393         this.updating = false;
38394         this.autoSizeTabs();
38395     },
38396
38397     /**
38398      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38399      */
38400     autoSizeTabs : function()
38401     {
38402         var count = this.items.length;
38403         var vcount = count - this.hiddenCount;
38404         
38405         if (vcount < 2) {
38406             this.stripEl.hide();
38407         } else {
38408             this.stripEl.show();
38409         }
38410         
38411         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38412             return;
38413         }
38414         
38415         
38416         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38417         var availWidth = Math.floor(w / vcount);
38418         var b = this.stripBody;
38419         if(b.getWidth() > w){
38420             var tabs = this.items;
38421             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38422             if(availWidth < this.minTabWidth){
38423                 /*if(!this.sleft){    // incomplete scrolling code
38424                     this.createScrollButtons();
38425                 }
38426                 this.showScroll();
38427                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38428             }
38429         }else{
38430             if(this.currentTabWidth < this.preferredTabWidth){
38431                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38432             }
38433         }
38434     },
38435
38436     /**
38437      * Returns the number of tabs in this TabPanel.
38438      * @return {Number}
38439      */
38440      getCount : function(){
38441          return this.items.length;
38442      },
38443
38444     /**
38445      * Resizes all the tabs to the passed width
38446      * @param {Number} The new width
38447      */
38448     setTabWidth : function(width){
38449         this.currentTabWidth = width;
38450         for(var i = 0, len = this.items.length; i < len; i++) {
38451                 if(!this.items[i].isHidden()) {
38452                 this.items[i].setWidth(width);
38453             }
38454         }
38455     },
38456
38457     /**
38458      * Destroys this TabPanel
38459      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38460      */
38461     destroy : function(removeEl){
38462         Roo.EventManager.removeResizeListener(this.onResize, this);
38463         for(var i = 0, len = this.items.length; i < len; i++){
38464             this.items[i].purgeListeners();
38465         }
38466         if(removeEl === true){
38467             this.el.update("");
38468             this.el.remove();
38469         }
38470     },
38471     
38472     createStrip : function(container)
38473     {
38474         var strip = document.createElement("nav");
38475         strip.className = Roo.bootstrap.version == 4 ?
38476             "navbar-light bg-light" : 
38477             "navbar navbar-default"; //"x-tabs-wrap";
38478         container.appendChild(strip);
38479         return strip;
38480     },
38481     
38482     createStripList : function(strip)
38483     {
38484         // div wrapper for retard IE
38485         // returns the "tr" element.
38486         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38487         //'<div class="x-tabs-strip-wrap">'+
38488           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38489           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38490         return strip.firstChild; //.firstChild.firstChild.firstChild;
38491     },
38492     createBody : function(container)
38493     {
38494         var body = document.createElement("div");
38495         Roo.id(body, "tab-body");
38496         //Roo.fly(body).addClass("x-tabs-body");
38497         Roo.fly(body).addClass("tab-content");
38498         container.appendChild(body);
38499         return body;
38500     },
38501     createItemBody :function(bodyEl, id){
38502         var body = Roo.getDom(id);
38503         if(!body){
38504             body = document.createElement("div");
38505             body.id = id;
38506         }
38507         //Roo.fly(body).addClass("x-tabs-item-body");
38508         Roo.fly(body).addClass("tab-pane");
38509          bodyEl.insertBefore(body, bodyEl.firstChild);
38510         return body;
38511     },
38512     /** @private */
38513     createStripElements :  function(stripEl, text, closable, tpl)
38514     {
38515         var td = document.createElement("li"); // was td..
38516         td.className = 'nav-item';
38517         
38518         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38519         
38520         
38521         stripEl.appendChild(td);
38522         /*if(closable){
38523             td.className = "x-tabs-closable";
38524             if(!this.closeTpl){
38525                 this.closeTpl = new Roo.Template(
38526                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38527                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38528                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38529                 );
38530             }
38531             var el = this.closeTpl.overwrite(td, {"text": text});
38532             var close = el.getElementsByTagName("div")[0];
38533             var inner = el.getElementsByTagName("em")[0];
38534             return {"el": el, "close": close, "inner": inner};
38535         } else {
38536         */
38537         // not sure what this is..
38538 //            if(!this.tabTpl){
38539                 //this.tabTpl = new Roo.Template(
38540                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38541                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38542                 //);
38543 //                this.tabTpl = new Roo.Template(
38544 //                   '<a href="#">' +
38545 //                   '<span unselectable="on"' +
38546 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38547 //                            ' >{text}</span></a>'
38548 //                );
38549 //                
38550 //            }
38551
38552
38553             var template = tpl || this.tabTpl || false;
38554             
38555             if(!template){
38556                 template =  new Roo.Template(
38557                         Roo.bootstrap.version == 4 ? 
38558                             (
38559                                 '<a class="nav-link" href="#" unselectable="on"' +
38560                                      (this.disableTooltips ? '' : ' title="{text}"') +
38561                                      ' >{text}</a>'
38562                             ) : (
38563                                 '<a class="nav-link" href="#">' +
38564                                 '<span unselectable="on"' +
38565                                          (this.disableTooltips ? '' : ' title="{text}"') +
38566                                     ' >{text}</span></a>'
38567                             )
38568                 );
38569             }
38570             
38571             switch (typeof(template)) {
38572                 case 'object' :
38573                     break;
38574                 case 'string' :
38575                     template = new Roo.Template(template);
38576                     break;
38577                 default :
38578                     break;
38579             }
38580             
38581             var el = template.overwrite(td, {"text": text});
38582             
38583             var inner = el.getElementsByTagName("span")[0];
38584             
38585             return {"el": el, "inner": inner};
38586             
38587     }
38588         
38589     
38590 });
38591
38592 /**
38593  * @class Roo.TabPanelItem
38594  * @extends Roo.util.Observable
38595  * Represents an individual item (tab plus body) in a TabPanel.
38596  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38597  * @param {String} id The id of this TabPanelItem
38598  * @param {String} text The text for the tab of this TabPanelItem
38599  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38600  */
38601 Roo.bootstrap.panel.TabItem = function(config){
38602     /**
38603      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38604      * @type Roo.TabPanel
38605      */
38606     this.tabPanel = config.panel;
38607     /**
38608      * The id for this TabPanelItem
38609      * @type String
38610      */
38611     this.id = config.id;
38612     /** @private */
38613     this.disabled = false;
38614     /** @private */
38615     this.text = config.text;
38616     /** @private */
38617     this.loaded = false;
38618     this.closable = config.closable;
38619
38620     /**
38621      * The body element for this TabPanelItem.
38622      * @type Roo.Element
38623      */
38624     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38625     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38626     this.bodyEl.setStyle("display", "block");
38627     this.bodyEl.setStyle("zoom", "1");
38628     //this.hideAction();
38629
38630     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38631     /** @private */
38632     this.el = Roo.get(els.el);
38633     this.inner = Roo.get(els.inner, true);
38634      this.textEl = Roo.bootstrap.version == 4 ?
38635         this.el : Roo.get(this.el.dom.firstChild, true);
38636
38637     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38638     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38639
38640     
38641 //    this.el.on("mousedown", this.onTabMouseDown, this);
38642     this.el.on("click", this.onTabClick, this);
38643     /** @private */
38644     if(config.closable){
38645         var c = Roo.get(els.close, true);
38646         c.dom.title = this.closeText;
38647         c.addClassOnOver("close-over");
38648         c.on("click", this.closeClick, this);
38649      }
38650
38651     this.addEvents({
38652          /**
38653          * @event activate
38654          * Fires when this tab becomes the active tab.
38655          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38656          * @param {Roo.TabPanelItem} this
38657          */
38658         "activate": true,
38659         /**
38660          * @event beforeclose
38661          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38662          * @param {Roo.TabPanelItem} this
38663          * @param {Object} e Set cancel to true on this object to cancel the close.
38664          */
38665         "beforeclose": true,
38666         /**
38667          * @event close
38668          * Fires when this tab is closed.
38669          * @param {Roo.TabPanelItem} this
38670          */
38671          "close": true,
38672         /**
38673          * @event deactivate
38674          * Fires when this tab is no longer the active tab.
38675          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38676          * @param {Roo.TabPanelItem} this
38677          */
38678          "deactivate" : true
38679     });
38680     this.hidden = false;
38681
38682     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38683 };
38684
38685 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38686            {
38687     purgeListeners : function(){
38688        Roo.util.Observable.prototype.purgeListeners.call(this);
38689        this.el.removeAllListeners();
38690     },
38691     /**
38692      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38693      */
38694     show : function(){
38695         this.status_node.addClass("active");
38696         this.showAction();
38697         if(Roo.isOpera){
38698             this.tabPanel.stripWrap.repaint();
38699         }
38700         this.fireEvent("activate", this.tabPanel, this);
38701     },
38702
38703     /**
38704      * Returns true if this tab is the active tab.
38705      * @return {Boolean}
38706      */
38707     isActive : function(){
38708         return this.tabPanel.getActiveTab() == this;
38709     },
38710
38711     /**
38712      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38713      */
38714     hide : function(){
38715         this.status_node.removeClass("active");
38716         this.hideAction();
38717         this.fireEvent("deactivate", this.tabPanel, this);
38718     },
38719
38720     hideAction : function(){
38721         this.bodyEl.hide();
38722         this.bodyEl.setStyle("position", "absolute");
38723         this.bodyEl.setLeft("-20000px");
38724         this.bodyEl.setTop("-20000px");
38725     },
38726
38727     showAction : function(){
38728         this.bodyEl.setStyle("position", "relative");
38729         this.bodyEl.setTop("");
38730         this.bodyEl.setLeft("");
38731         this.bodyEl.show();
38732     },
38733
38734     /**
38735      * Set the tooltip for the tab.
38736      * @param {String} tooltip The tab's tooltip
38737      */
38738     setTooltip : function(text){
38739         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38740             this.textEl.dom.qtip = text;
38741             this.textEl.dom.removeAttribute('title');
38742         }else{
38743             this.textEl.dom.title = text;
38744         }
38745     },
38746
38747     onTabClick : function(e){
38748         e.preventDefault();
38749         this.tabPanel.activate(this.id);
38750     },
38751
38752     onTabMouseDown : function(e){
38753         e.preventDefault();
38754         this.tabPanel.activate(this.id);
38755     },
38756 /*
38757     getWidth : function(){
38758         return this.inner.getWidth();
38759     },
38760
38761     setWidth : function(width){
38762         var iwidth = width - this.linode.getPadding("lr");
38763         this.inner.setWidth(iwidth);
38764         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38765         this.linode.setWidth(width);
38766     },
38767 */
38768     /**
38769      * Show or hide the tab
38770      * @param {Boolean} hidden True to hide or false to show.
38771      */
38772     setHidden : function(hidden){
38773         this.hidden = hidden;
38774         this.linode.setStyle("display", hidden ? "none" : "");
38775     },
38776
38777     /**
38778      * Returns true if this tab is "hidden"
38779      * @return {Boolean}
38780      */
38781     isHidden : function(){
38782         return this.hidden;
38783     },
38784
38785     /**
38786      * Returns the text for this tab
38787      * @return {String}
38788      */
38789     getText : function(){
38790         return this.text;
38791     },
38792     /*
38793     autoSize : function(){
38794         //this.el.beginMeasure();
38795         this.textEl.setWidth(1);
38796         /*
38797          *  #2804 [new] Tabs in Roojs
38798          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38799          */
38800         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38801         //this.el.endMeasure();
38802     //},
38803
38804     /**
38805      * Sets the text for the tab (Note: this also sets the tooltip text)
38806      * @param {String} text The tab's text and tooltip
38807      */
38808     setText : function(text){
38809         this.text = text;
38810         this.textEl.update(text);
38811         this.setTooltip(text);
38812         //if(!this.tabPanel.resizeTabs){
38813         //    this.autoSize();
38814         //}
38815     },
38816     /**
38817      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38818      */
38819     activate : function(){
38820         this.tabPanel.activate(this.id);
38821     },
38822
38823     /**
38824      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38825      */
38826     disable : function(){
38827         if(this.tabPanel.active != this){
38828             this.disabled = true;
38829             this.status_node.addClass("disabled");
38830         }
38831     },
38832
38833     /**
38834      * Enables this TabPanelItem if it was previously disabled.
38835      */
38836     enable : function(){
38837         this.disabled = false;
38838         this.status_node.removeClass("disabled");
38839     },
38840
38841     /**
38842      * Sets the content for this TabPanelItem.
38843      * @param {String} content The content
38844      * @param {Boolean} loadScripts true to look for and load scripts
38845      */
38846     setContent : function(content, loadScripts){
38847         this.bodyEl.update(content, loadScripts);
38848     },
38849
38850     /**
38851      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38852      * @return {Roo.UpdateManager} The UpdateManager
38853      */
38854     getUpdateManager : function(){
38855         return this.bodyEl.getUpdateManager();
38856     },
38857
38858     /**
38859      * Set a URL to be used to load the content for this TabPanelItem.
38860      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38861      * @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)
38862      * @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)
38863      * @return {Roo.UpdateManager} The UpdateManager
38864      */
38865     setUrl : function(url, params, loadOnce){
38866         if(this.refreshDelegate){
38867             this.un('activate', this.refreshDelegate);
38868         }
38869         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38870         this.on("activate", this.refreshDelegate);
38871         return this.bodyEl.getUpdateManager();
38872     },
38873
38874     /** @private */
38875     _handleRefresh : function(url, params, loadOnce){
38876         if(!loadOnce || !this.loaded){
38877             var updater = this.bodyEl.getUpdateManager();
38878             updater.update(url, params, this._setLoaded.createDelegate(this));
38879         }
38880     },
38881
38882     /**
38883      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38884      *   Will fail silently if the setUrl method has not been called.
38885      *   This does not activate the panel, just updates its content.
38886      */
38887     refresh : function(){
38888         if(this.refreshDelegate){
38889            this.loaded = false;
38890            this.refreshDelegate();
38891         }
38892     },
38893
38894     /** @private */
38895     _setLoaded : function(){
38896         this.loaded = true;
38897     },
38898
38899     /** @private */
38900     closeClick : function(e){
38901         var o = {};
38902         e.stopEvent();
38903         this.fireEvent("beforeclose", this, o);
38904         if(o.cancel !== true){
38905             this.tabPanel.removeTab(this.id);
38906         }
38907     },
38908     /**
38909      * The text displayed in the tooltip for the close icon.
38910      * @type String
38911      */
38912     closeText : "Close this tab"
38913 });
38914 /**
38915 *    This script refer to:
38916 *    Title: International Telephone Input
38917 *    Author: Jack O'Connor
38918 *    Code version:  v12.1.12
38919 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38920 **/
38921
38922 Roo.bootstrap.PhoneInputData = function() {
38923     var d = [
38924       [
38925         "Afghanistan (‫افغانستان‬‎)",
38926         "af",
38927         "93"
38928       ],
38929       [
38930         "Albania (Shqipëri)",
38931         "al",
38932         "355"
38933       ],
38934       [
38935         "Algeria (‫الجزائر‬‎)",
38936         "dz",
38937         "213"
38938       ],
38939       [
38940         "American Samoa",
38941         "as",
38942         "1684"
38943       ],
38944       [
38945         "Andorra",
38946         "ad",
38947         "376"
38948       ],
38949       [
38950         "Angola",
38951         "ao",
38952         "244"
38953       ],
38954       [
38955         "Anguilla",
38956         "ai",
38957         "1264"
38958       ],
38959       [
38960         "Antigua and Barbuda",
38961         "ag",
38962         "1268"
38963       ],
38964       [
38965         "Argentina",
38966         "ar",
38967         "54"
38968       ],
38969       [
38970         "Armenia (Հայաստան)",
38971         "am",
38972         "374"
38973       ],
38974       [
38975         "Aruba",
38976         "aw",
38977         "297"
38978       ],
38979       [
38980         "Australia",
38981         "au",
38982         "61",
38983         0
38984       ],
38985       [
38986         "Austria (Österreich)",
38987         "at",
38988         "43"
38989       ],
38990       [
38991         "Azerbaijan (Azərbaycan)",
38992         "az",
38993         "994"
38994       ],
38995       [
38996         "Bahamas",
38997         "bs",
38998         "1242"
38999       ],
39000       [
39001         "Bahrain (‫البحرين‬‎)",
39002         "bh",
39003         "973"
39004       ],
39005       [
39006         "Bangladesh (বাংলাদেশ)",
39007         "bd",
39008         "880"
39009       ],
39010       [
39011         "Barbados",
39012         "bb",
39013         "1246"
39014       ],
39015       [
39016         "Belarus (Беларусь)",
39017         "by",
39018         "375"
39019       ],
39020       [
39021         "Belgium (België)",
39022         "be",
39023         "32"
39024       ],
39025       [
39026         "Belize",
39027         "bz",
39028         "501"
39029       ],
39030       [
39031         "Benin (Bénin)",
39032         "bj",
39033         "229"
39034       ],
39035       [
39036         "Bermuda",
39037         "bm",
39038         "1441"
39039       ],
39040       [
39041         "Bhutan (འབྲུག)",
39042         "bt",
39043         "975"
39044       ],
39045       [
39046         "Bolivia",
39047         "bo",
39048         "591"
39049       ],
39050       [
39051         "Bosnia and Herzegovina (Босна и Херцеговина)",
39052         "ba",
39053         "387"
39054       ],
39055       [
39056         "Botswana",
39057         "bw",
39058         "267"
39059       ],
39060       [
39061         "Brazil (Brasil)",
39062         "br",
39063         "55"
39064       ],
39065       [
39066         "British Indian Ocean Territory",
39067         "io",
39068         "246"
39069       ],
39070       [
39071         "British Virgin Islands",
39072         "vg",
39073         "1284"
39074       ],
39075       [
39076         "Brunei",
39077         "bn",
39078         "673"
39079       ],
39080       [
39081         "Bulgaria (България)",
39082         "bg",
39083         "359"
39084       ],
39085       [
39086         "Burkina Faso",
39087         "bf",
39088         "226"
39089       ],
39090       [
39091         "Burundi (Uburundi)",
39092         "bi",
39093         "257"
39094       ],
39095       [
39096         "Cambodia (កម្ពុជា)",
39097         "kh",
39098         "855"
39099       ],
39100       [
39101         "Cameroon (Cameroun)",
39102         "cm",
39103         "237"
39104       ],
39105       [
39106         "Canada",
39107         "ca",
39108         "1",
39109         1,
39110         ["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"]
39111       ],
39112       [
39113         "Cape Verde (Kabu Verdi)",
39114         "cv",
39115         "238"
39116       ],
39117       [
39118         "Caribbean Netherlands",
39119         "bq",
39120         "599",
39121         1
39122       ],
39123       [
39124         "Cayman Islands",
39125         "ky",
39126         "1345"
39127       ],
39128       [
39129         "Central African Republic (République centrafricaine)",
39130         "cf",
39131         "236"
39132       ],
39133       [
39134         "Chad (Tchad)",
39135         "td",
39136         "235"
39137       ],
39138       [
39139         "Chile",
39140         "cl",
39141         "56"
39142       ],
39143       [
39144         "China (中国)",
39145         "cn",
39146         "86"
39147       ],
39148       [
39149         "Christmas Island",
39150         "cx",
39151         "61",
39152         2
39153       ],
39154       [
39155         "Cocos (Keeling) Islands",
39156         "cc",
39157         "61",
39158         1
39159       ],
39160       [
39161         "Colombia",
39162         "co",
39163         "57"
39164       ],
39165       [
39166         "Comoros (‫جزر القمر‬‎)",
39167         "km",
39168         "269"
39169       ],
39170       [
39171         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39172         "cd",
39173         "243"
39174       ],
39175       [
39176         "Congo (Republic) (Congo-Brazzaville)",
39177         "cg",
39178         "242"
39179       ],
39180       [
39181         "Cook Islands",
39182         "ck",
39183         "682"
39184       ],
39185       [
39186         "Costa Rica",
39187         "cr",
39188         "506"
39189       ],
39190       [
39191         "Côte d’Ivoire",
39192         "ci",
39193         "225"
39194       ],
39195       [
39196         "Croatia (Hrvatska)",
39197         "hr",
39198         "385"
39199       ],
39200       [
39201         "Cuba",
39202         "cu",
39203         "53"
39204       ],
39205       [
39206         "Curaçao",
39207         "cw",
39208         "599",
39209         0
39210       ],
39211       [
39212         "Cyprus (Κύπρος)",
39213         "cy",
39214         "357"
39215       ],
39216       [
39217         "Czech Republic (Česká republika)",
39218         "cz",
39219         "420"
39220       ],
39221       [
39222         "Denmark (Danmark)",
39223         "dk",
39224         "45"
39225       ],
39226       [
39227         "Djibouti",
39228         "dj",
39229         "253"
39230       ],
39231       [
39232         "Dominica",
39233         "dm",
39234         "1767"
39235       ],
39236       [
39237         "Dominican Republic (República Dominicana)",
39238         "do",
39239         "1",
39240         2,
39241         ["809", "829", "849"]
39242       ],
39243       [
39244         "Ecuador",
39245         "ec",
39246         "593"
39247       ],
39248       [
39249         "Egypt (‫مصر‬‎)",
39250         "eg",
39251         "20"
39252       ],
39253       [
39254         "El Salvador",
39255         "sv",
39256         "503"
39257       ],
39258       [
39259         "Equatorial Guinea (Guinea Ecuatorial)",
39260         "gq",
39261         "240"
39262       ],
39263       [
39264         "Eritrea",
39265         "er",
39266         "291"
39267       ],
39268       [
39269         "Estonia (Eesti)",
39270         "ee",
39271         "372"
39272       ],
39273       [
39274         "Ethiopia",
39275         "et",
39276         "251"
39277       ],
39278       [
39279         "Falkland Islands (Islas Malvinas)",
39280         "fk",
39281         "500"
39282       ],
39283       [
39284         "Faroe Islands (Føroyar)",
39285         "fo",
39286         "298"
39287       ],
39288       [
39289         "Fiji",
39290         "fj",
39291         "679"
39292       ],
39293       [
39294         "Finland (Suomi)",
39295         "fi",
39296         "358",
39297         0
39298       ],
39299       [
39300         "France",
39301         "fr",
39302         "33"
39303       ],
39304       [
39305         "French Guiana (Guyane française)",
39306         "gf",
39307         "594"
39308       ],
39309       [
39310         "French Polynesia (Polynésie française)",
39311         "pf",
39312         "689"
39313       ],
39314       [
39315         "Gabon",
39316         "ga",
39317         "241"
39318       ],
39319       [
39320         "Gambia",
39321         "gm",
39322         "220"
39323       ],
39324       [
39325         "Georgia (საქართველო)",
39326         "ge",
39327         "995"
39328       ],
39329       [
39330         "Germany (Deutschland)",
39331         "de",
39332         "49"
39333       ],
39334       [
39335         "Ghana (Gaana)",
39336         "gh",
39337         "233"
39338       ],
39339       [
39340         "Gibraltar",
39341         "gi",
39342         "350"
39343       ],
39344       [
39345         "Greece (Ελλάδα)",
39346         "gr",
39347         "30"
39348       ],
39349       [
39350         "Greenland (Kalaallit Nunaat)",
39351         "gl",
39352         "299"
39353       ],
39354       [
39355         "Grenada",
39356         "gd",
39357         "1473"
39358       ],
39359       [
39360         "Guadeloupe",
39361         "gp",
39362         "590",
39363         0
39364       ],
39365       [
39366         "Guam",
39367         "gu",
39368         "1671"
39369       ],
39370       [
39371         "Guatemala",
39372         "gt",
39373         "502"
39374       ],
39375       [
39376         "Guernsey",
39377         "gg",
39378         "44",
39379         1
39380       ],
39381       [
39382         "Guinea (Guinée)",
39383         "gn",
39384         "224"
39385       ],
39386       [
39387         "Guinea-Bissau (Guiné Bissau)",
39388         "gw",
39389         "245"
39390       ],
39391       [
39392         "Guyana",
39393         "gy",
39394         "592"
39395       ],
39396       [
39397         "Haiti",
39398         "ht",
39399         "509"
39400       ],
39401       [
39402         "Honduras",
39403         "hn",
39404         "504"
39405       ],
39406       [
39407         "Hong Kong (香港)",
39408         "hk",
39409         "852"
39410       ],
39411       [
39412         "Hungary (Magyarország)",
39413         "hu",
39414         "36"
39415       ],
39416       [
39417         "Iceland (Ísland)",
39418         "is",
39419         "354"
39420       ],
39421       [
39422         "India (भारत)",
39423         "in",
39424         "91"
39425       ],
39426       [
39427         "Indonesia",
39428         "id",
39429         "62"
39430       ],
39431       [
39432         "Iran (‫ایران‬‎)",
39433         "ir",
39434         "98"
39435       ],
39436       [
39437         "Iraq (‫العراق‬‎)",
39438         "iq",
39439         "964"
39440       ],
39441       [
39442         "Ireland",
39443         "ie",
39444         "353"
39445       ],
39446       [
39447         "Isle of Man",
39448         "im",
39449         "44",
39450         2
39451       ],
39452       [
39453         "Israel (‫ישראל‬‎)",
39454         "il",
39455         "972"
39456       ],
39457       [
39458         "Italy (Italia)",
39459         "it",
39460         "39",
39461         0
39462       ],
39463       [
39464         "Jamaica",
39465         "jm",
39466         "1876"
39467       ],
39468       [
39469         "Japan (日本)",
39470         "jp",
39471         "81"
39472       ],
39473       [
39474         "Jersey",
39475         "je",
39476         "44",
39477         3
39478       ],
39479       [
39480         "Jordan (‫الأردن‬‎)",
39481         "jo",
39482         "962"
39483       ],
39484       [
39485         "Kazakhstan (Казахстан)",
39486         "kz",
39487         "7",
39488         1
39489       ],
39490       [
39491         "Kenya",
39492         "ke",
39493         "254"
39494       ],
39495       [
39496         "Kiribati",
39497         "ki",
39498         "686"
39499       ],
39500       [
39501         "Kosovo",
39502         "xk",
39503         "383"
39504       ],
39505       [
39506         "Kuwait (‫الكويت‬‎)",
39507         "kw",
39508         "965"
39509       ],
39510       [
39511         "Kyrgyzstan (Кыргызстан)",
39512         "kg",
39513         "996"
39514       ],
39515       [
39516         "Laos (ລາວ)",
39517         "la",
39518         "856"
39519       ],
39520       [
39521         "Latvia (Latvija)",
39522         "lv",
39523         "371"
39524       ],
39525       [
39526         "Lebanon (‫لبنان‬‎)",
39527         "lb",
39528         "961"
39529       ],
39530       [
39531         "Lesotho",
39532         "ls",
39533         "266"
39534       ],
39535       [
39536         "Liberia",
39537         "lr",
39538         "231"
39539       ],
39540       [
39541         "Libya (‫ليبيا‬‎)",
39542         "ly",
39543         "218"
39544       ],
39545       [
39546         "Liechtenstein",
39547         "li",
39548         "423"
39549       ],
39550       [
39551         "Lithuania (Lietuva)",
39552         "lt",
39553         "370"
39554       ],
39555       [
39556         "Luxembourg",
39557         "lu",
39558         "352"
39559       ],
39560       [
39561         "Macau (澳門)",
39562         "mo",
39563         "853"
39564       ],
39565       [
39566         "Macedonia (FYROM) (Македонија)",
39567         "mk",
39568         "389"
39569       ],
39570       [
39571         "Madagascar (Madagasikara)",
39572         "mg",
39573         "261"
39574       ],
39575       [
39576         "Malawi",
39577         "mw",
39578         "265"
39579       ],
39580       [
39581         "Malaysia",
39582         "my",
39583         "60"
39584       ],
39585       [
39586         "Maldives",
39587         "mv",
39588         "960"
39589       ],
39590       [
39591         "Mali",
39592         "ml",
39593         "223"
39594       ],
39595       [
39596         "Malta",
39597         "mt",
39598         "356"
39599       ],
39600       [
39601         "Marshall Islands",
39602         "mh",
39603         "692"
39604       ],
39605       [
39606         "Martinique",
39607         "mq",
39608         "596"
39609       ],
39610       [
39611         "Mauritania (‫موريتانيا‬‎)",
39612         "mr",
39613         "222"
39614       ],
39615       [
39616         "Mauritius (Moris)",
39617         "mu",
39618         "230"
39619       ],
39620       [
39621         "Mayotte",
39622         "yt",
39623         "262",
39624         1
39625       ],
39626       [
39627         "Mexico (México)",
39628         "mx",
39629         "52"
39630       ],
39631       [
39632         "Micronesia",
39633         "fm",
39634         "691"
39635       ],
39636       [
39637         "Moldova (Republica Moldova)",
39638         "md",
39639         "373"
39640       ],
39641       [
39642         "Monaco",
39643         "mc",
39644         "377"
39645       ],
39646       [
39647         "Mongolia (Монгол)",
39648         "mn",
39649         "976"
39650       ],
39651       [
39652         "Montenegro (Crna Gora)",
39653         "me",
39654         "382"
39655       ],
39656       [
39657         "Montserrat",
39658         "ms",
39659         "1664"
39660       ],
39661       [
39662         "Morocco (‫المغرب‬‎)",
39663         "ma",
39664         "212",
39665         0
39666       ],
39667       [
39668         "Mozambique (Moçambique)",
39669         "mz",
39670         "258"
39671       ],
39672       [
39673         "Myanmar (Burma) (မြန်မာ)",
39674         "mm",
39675         "95"
39676       ],
39677       [
39678         "Namibia (Namibië)",
39679         "na",
39680         "264"
39681       ],
39682       [
39683         "Nauru",
39684         "nr",
39685         "674"
39686       ],
39687       [
39688         "Nepal (नेपाल)",
39689         "np",
39690         "977"
39691       ],
39692       [
39693         "Netherlands (Nederland)",
39694         "nl",
39695         "31"
39696       ],
39697       [
39698         "New Caledonia (Nouvelle-Calédonie)",
39699         "nc",
39700         "687"
39701       ],
39702       [
39703         "New Zealand",
39704         "nz",
39705         "64"
39706       ],
39707       [
39708         "Nicaragua",
39709         "ni",
39710         "505"
39711       ],
39712       [
39713         "Niger (Nijar)",
39714         "ne",
39715         "227"
39716       ],
39717       [
39718         "Nigeria",
39719         "ng",
39720         "234"
39721       ],
39722       [
39723         "Niue",
39724         "nu",
39725         "683"
39726       ],
39727       [
39728         "Norfolk Island",
39729         "nf",
39730         "672"
39731       ],
39732       [
39733         "North Korea (조선 민주주의 인민 공화국)",
39734         "kp",
39735         "850"
39736       ],
39737       [
39738         "Northern Mariana Islands",
39739         "mp",
39740         "1670"
39741       ],
39742       [
39743         "Norway (Norge)",
39744         "no",
39745         "47",
39746         0
39747       ],
39748       [
39749         "Oman (‫عُمان‬‎)",
39750         "om",
39751         "968"
39752       ],
39753       [
39754         "Pakistan (‫پاکستان‬‎)",
39755         "pk",
39756         "92"
39757       ],
39758       [
39759         "Palau",
39760         "pw",
39761         "680"
39762       ],
39763       [
39764         "Palestine (‫فلسطين‬‎)",
39765         "ps",
39766         "970"
39767       ],
39768       [
39769         "Panama (Panamá)",
39770         "pa",
39771         "507"
39772       ],
39773       [
39774         "Papua New Guinea",
39775         "pg",
39776         "675"
39777       ],
39778       [
39779         "Paraguay",
39780         "py",
39781         "595"
39782       ],
39783       [
39784         "Peru (Perú)",
39785         "pe",
39786         "51"
39787       ],
39788       [
39789         "Philippines",
39790         "ph",
39791         "63"
39792       ],
39793       [
39794         "Poland (Polska)",
39795         "pl",
39796         "48"
39797       ],
39798       [
39799         "Portugal",
39800         "pt",
39801         "351"
39802       ],
39803       [
39804         "Puerto Rico",
39805         "pr",
39806         "1",
39807         3,
39808         ["787", "939"]
39809       ],
39810       [
39811         "Qatar (‫قطر‬‎)",
39812         "qa",
39813         "974"
39814       ],
39815       [
39816         "Réunion (La Réunion)",
39817         "re",
39818         "262",
39819         0
39820       ],
39821       [
39822         "Romania (România)",
39823         "ro",
39824         "40"
39825       ],
39826       [
39827         "Russia (Россия)",
39828         "ru",
39829         "7",
39830         0
39831       ],
39832       [
39833         "Rwanda",
39834         "rw",
39835         "250"
39836       ],
39837       [
39838         "Saint Barthélemy",
39839         "bl",
39840         "590",
39841         1
39842       ],
39843       [
39844         "Saint Helena",
39845         "sh",
39846         "290"
39847       ],
39848       [
39849         "Saint Kitts and Nevis",
39850         "kn",
39851         "1869"
39852       ],
39853       [
39854         "Saint Lucia",
39855         "lc",
39856         "1758"
39857       ],
39858       [
39859         "Saint Martin (Saint-Martin (partie française))",
39860         "mf",
39861         "590",
39862         2
39863       ],
39864       [
39865         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39866         "pm",
39867         "508"
39868       ],
39869       [
39870         "Saint Vincent and the Grenadines",
39871         "vc",
39872         "1784"
39873       ],
39874       [
39875         "Samoa",
39876         "ws",
39877         "685"
39878       ],
39879       [
39880         "San Marino",
39881         "sm",
39882         "378"
39883       ],
39884       [
39885         "São Tomé and Príncipe (São Tomé e Príncipe)",
39886         "st",
39887         "239"
39888       ],
39889       [
39890         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39891         "sa",
39892         "966"
39893       ],
39894       [
39895         "Senegal (Sénégal)",
39896         "sn",
39897         "221"
39898       ],
39899       [
39900         "Serbia (Србија)",
39901         "rs",
39902         "381"
39903       ],
39904       [
39905         "Seychelles",
39906         "sc",
39907         "248"
39908       ],
39909       [
39910         "Sierra Leone",
39911         "sl",
39912         "232"
39913       ],
39914       [
39915         "Singapore",
39916         "sg",
39917         "65"
39918       ],
39919       [
39920         "Sint Maarten",
39921         "sx",
39922         "1721"
39923       ],
39924       [
39925         "Slovakia (Slovensko)",
39926         "sk",
39927         "421"
39928       ],
39929       [
39930         "Slovenia (Slovenija)",
39931         "si",
39932         "386"
39933       ],
39934       [
39935         "Solomon Islands",
39936         "sb",
39937         "677"
39938       ],
39939       [
39940         "Somalia (Soomaaliya)",
39941         "so",
39942         "252"
39943       ],
39944       [
39945         "South Africa",
39946         "za",
39947         "27"
39948       ],
39949       [
39950         "South Korea (대한민국)",
39951         "kr",
39952         "82"
39953       ],
39954       [
39955         "South Sudan (‫جنوب السودان‬‎)",
39956         "ss",
39957         "211"
39958       ],
39959       [
39960         "Spain (España)",
39961         "es",
39962         "34"
39963       ],
39964       [
39965         "Sri Lanka (ශ්‍රී ලංකාව)",
39966         "lk",
39967         "94"
39968       ],
39969       [
39970         "Sudan (‫السودان‬‎)",
39971         "sd",
39972         "249"
39973       ],
39974       [
39975         "Suriname",
39976         "sr",
39977         "597"
39978       ],
39979       [
39980         "Svalbard and Jan Mayen",
39981         "sj",
39982         "47",
39983         1
39984       ],
39985       [
39986         "Swaziland",
39987         "sz",
39988         "268"
39989       ],
39990       [
39991         "Sweden (Sverige)",
39992         "se",
39993         "46"
39994       ],
39995       [
39996         "Switzerland (Schweiz)",
39997         "ch",
39998         "41"
39999       ],
40000       [
40001         "Syria (‫سوريا‬‎)",
40002         "sy",
40003         "963"
40004       ],
40005       [
40006         "Taiwan (台灣)",
40007         "tw",
40008         "886"
40009       ],
40010       [
40011         "Tajikistan",
40012         "tj",
40013         "992"
40014       ],
40015       [
40016         "Tanzania",
40017         "tz",
40018         "255"
40019       ],
40020       [
40021         "Thailand (ไทย)",
40022         "th",
40023         "66"
40024       ],
40025       [
40026         "Timor-Leste",
40027         "tl",
40028         "670"
40029       ],
40030       [
40031         "Togo",
40032         "tg",
40033         "228"
40034       ],
40035       [
40036         "Tokelau",
40037         "tk",
40038         "690"
40039       ],
40040       [
40041         "Tonga",
40042         "to",
40043         "676"
40044       ],
40045       [
40046         "Trinidad and Tobago",
40047         "tt",
40048         "1868"
40049       ],
40050       [
40051         "Tunisia (‫تونس‬‎)",
40052         "tn",
40053         "216"
40054       ],
40055       [
40056         "Turkey (Türkiye)",
40057         "tr",
40058         "90"
40059       ],
40060       [
40061         "Turkmenistan",
40062         "tm",
40063         "993"
40064       ],
40065       [
40066         "Turks and Caicos Islands",
40067         "tc",
40068         "1649"
40069       ],
40070       [
40071         "Tuvalu",
40072         "tv",
40073         "688"
40074       ],
40075       [
40076         "U.S. Virgin Islands",
40077         "vi",
40078         "1340"
40079       ],
40080       [
40081         "Uganda",
40082         "ug",
40083         "256"
40084       ],
40085       [
40086         "Ukraine (Україна)",
40087         "ua",
40088         "380"
40089       ],
40090       [
40091         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40092         "ae",
40093         "971"
40094       ],
40095       [
40096         "United Kingdom",
40097         "gb",
40098         "44",
40099         0
40100       ],
40101       [
40102         "United States",
40103         "us",
40104         "1",
40105         0
40106       ],
40107       [
40108         "Uruguay",
40109         "uy",
40110         "598"
40111       ],
40112       [
40113         "Uzbekistan (Oʻzbekiston)",
40114         "uz",
40115         "998"
40116       ],
40117       [
40118         "Vanuatu",
40119         "vu",
40120         "678"
40121       ],
40122       [
40123         "Vatican City (Città del Vaticano)",
40124         "va",
40125         "39",
40126         1
40127       ],
40128       [
40129         "Venezuela",
40130         "ve",
40131         "58"
40132       ],
40133       [
40134         "Vietnam (Việt Nam)",
40135         "vn",
40136         "84"
40137       ],
40138       [
40139         "Wallis and Futuna (Wallis-et-Futuna)",
40140         "wf",
40141         "681"
40142       ],
40143       [
40144         "Western Sahara (‫الصحراء الغربية‬‎)",
40145         "eh",
40146         "212",
40147         1
40148       ],
40149       [
40150         "Yemen (‫اليمن‬‎)",
40151         "ye",
40152         "967"
40153       ],
40154       [
40155         "Zambia",
40156         "zm",
40157         "260"
40158       ],
40159       [
40160         "Zimbabwe",
40161         "zw",
40162         "263"
40163       ],
40164       [
40165         "Åland Islands",
40166         "ax",
40167         "358",
40168         1
40169       ]
40170   ];
40171   
40172   return d;
40173 }/**
40174 *    This script refer to:
40175 *    Title: International Telephone Input
40176 *    Author: Jack O'Connor
40177 *    Code version:  v12.1.12
40178 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40179 **/
40180
40181 /**
40182  * @class Roo.bootstrap.PhoneInput
40183  * @extends Roo.bootstrap.TriggerField
40184  * An input with International dial-code selection
40185  
40186  * @cfg {String} defaultDialCode default '+852'
40187  * @cfg {Array} preferedCountries default []
40188   
40189  * @constructor
40190  * Create a new PhoneInput.
40191  * @param {Object} config Configuration options
40192  */
40193
40194 Roo.bootstrap.PhoneInput = function(config) {
40195     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40196 };
40197
40198 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40199         
40200         listWidth: undefined,
40201         
40202         selectedClass: 'active',
40203         
40204         invalidClass : "has-warning",
40205         
40206         validClass: 'has-success',
40207         
40208         allowed: '0123456789',
40209         
40210         max_length: 15,
40211         
40212         /**
40213          * @cfg {String} defaultDialCode The default dial code when initializing the input
40214          */
40215         defaultDialCode: '+852',
40216         
40217         /**
40218          * @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
40219          */
40220         preferedCountries: false,
40221         
40222         getAutoCreate : function()
40223         {
40224             var data = Roo.bootstrap.PhoneInputData();
40225             var align = this.labelAlign || this.parentLabelAlign();
40226             var id = Roo.id();
40227             
40228             this.allCountries = [];
40229             this.dialCodeMapping = [];
40230             
40231             for (var i = 0; i < data.length; i++) {
40232               var c = data[i];
40233               this.allCountries[i] = {
40234                 name: c[0],
40235                 iso2: c[1],
40236                 dialCode: c[2],
40237                 priority: c[3] || 0,
40238                 areaCodes: c[4] || null
40239               };
40240               this.dialCodeMapping[c[2]] = {
40241                   name: c[0],
40242                   iso2: c[1],
40243                   priority: c[3] || 0,
40244                   areaCodes: c[4] || null
40245               };
40246             }
40247             
40248             var cfg = {
40249                 cls: 'form-group',
40250                 cn: []
40251             };
40252             
40253             var input =  {
40254                 tag: 'input',
40255                 id : id,
40256                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40257                 maxlength: this.max_length,
40258                 cls : 'form-control tel-input',
40259                 autocomplete: 'new-password'
40260             };
40261             
40262             var hiddenInput = {
40263                 tag: 'input',
40264                 type: 'hidden',
40265                 cls: 'hidden-tel-input'
40266             };
40267             
40268             if (this.name) {
40269                 hiddenInput.name = this.name;
40270             }
40271             
40272             if (this.disabled) {
40273                 input.disabled = true;
40274             }
40275             
40276             var flag_container = {
40277                 tag: 'div',
40278                 cls: 'flag-box',
40279                 cn: [
40280                     {
40281                         tag: 'div',
40282                         cls: 'flag'
40283                     },
40284                     {
40285                         tag: 'div',
40286                         cls: 'caret'
40287                     }
40288                 ]
40289             };
40290             
40291             var box = {
40292                 tag: 'div',
40293                 cls: this.hasFeedback ? 'has-feedback' : '',
40294                 cn: [
40295                     hiddenInput,
40296                     input,
40297                     {
40298                         tag: 'input',
40299                         cls: 'dial-code-holder',
40300                         disabled: true
40301                     }
40302                 ]
40303             };
40304             
40305             var container = {
40306                 cls: 'roo-select2-container input-group',
40307                 cn: [
40308                     flag_container,
40309                     box
40310                 ]
40311             };
40312             
40313             if (this.fieldLabel.length) {
40314                 var indicator = {
40315                     tag: 'i',
40316                     tooltip: 'This field is required'
40317                 };
40318                 
40319                 var label = {
40320                     tag: 'label',
40321                     'for':  id,
40322                     cls: 'control-label',
40323                     cn: []
40324                 };
40325                 
40326                 var label_text = {
40327                     tag: 'span',
40328                     html: this.fieldLabel
40329                 };
40330                 
40331                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40332                 label.cn = [
40333                     indicator,
40334                     label_text
40335                 ];
40336                 
40337                 if(this.indicatorpos == 'right') {
40338                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40339                     label.cn = [
40340                         label_text,
40341                         indicator
40342                     ];
40343                 }
40344                 
40345                 if(align == 'left') {
40346                     container = {
40347                         tag: 'div',
40348                         cn: [
40349                             container
40350                         ]
40351                     };
40352                     
40353                     if(this.labelWidth > 12){
40354                         label.style = "width: " + this.labelWidth + 'px';
40355                     }
40356                     if(this.labelWidth < 13 && this.labelmd == 0){
40357                         this.labelmd = this.labelWidth;
40358                     }
40359                     if(this.labellg > 0){
40360                         label.cls += ' col-lg-' + this.labellg;
40361                         input.cls += ' col-lg-' + (12 - this.labellg);
40362                     }
40363                     if(this.labelmd > 0){
40364                         label.cls += ' col-md-' + this.labelmd;
40365                         container.cls += ' col-md-' + (12 - this.labelmd);
40366                     }
40367                     if(this.labelsm > 0){
40368                         label.cls += ' col-sm-' + this.labelsm;
40369                         container.cls += ' col-sm-' + (12 - this.labelsm);
40370                     }
40371                     if(this.labelxs > 0){
40372                         label.cls += ' col-xs-' + this.labelxs;
40373                         container.cls += ' col-xs-' + (12 - this.labelxs);
40374                     }
40375                 }
40376             }
40377             
40378             cfg.cn = [
40379                 label,
40380                 container
40381             ];
40382             
40383             var settings = this;
40384             
40385             ['xs','sm','md','lg'].map(function(size){
40386                 if (settings[size]) {
40387                     cfg.cls += ' col-' + size + '-' + settings[size];
40388                 }
40389             });
40390             
40391             this.store = new Roo.data.Store({
40392                 proxy : new Roo.data.MemoryProxy({}),
40393                 reader : new Roo.data.JsonReader({
40394                     fields : [
40395                         {
40396                             'name' : 'name',
40397                             'type' : 'string'
40398                         },
40399                         {
40400                             'name' : 'iso2',
40401                             'type' : 'string'
40402                         },
40403                         {
40404                             'name' : 'dialCode',
40405                             'type' : 'string'
40406                         },
40407                         {
40408                             'name' : 'priority',
40409                             'type' : 'string'
40410                         },
40411                         {
40412                             'name' : 'areaCodes',
40413                             'type' : 'string'
40414                         }
40415                     ]
40416                 })
40417             });
40418             
40419             if(!this.preferedCountries) {
40420                 this.preferedCountries = [
40421                     'hk',
40422                     'gb',
40423                     'us'
40424                 ];
40425             }
40426             
40427             var p = this.preferedCountries.reverse();
40428             
40429             if(p) {
40430                 for (var i = 0; i < p.length; i++) {
40431                     for (var j = 0; j < this.allCountries.length; j++) {
40432                         if(this.allCountries[j].iso2 == p[i]) {
40433                             var t = this.allCountries[j];
40434                             this.allCountries.splice(j,1);
40435                             this.allCountries.unshift(t);
40436                         }
40437                     } 
40438                 }
40439             }
40440             
40441             this.store.proxy.data = {
40442                 success: true,
40443                 data: this.allCountries
40444             };
40445             
40446             return cfg;
40447         },
40448         
40449         initEvents : function()
40450         {
40451             this.createList();
40452             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40453             
40454             this.indicator = this.indicatorEl();
40455             this.flag = this.flagEl();
40456             this.dialCodeHolder = this.dialCodeHolderEl();
40457             
40458             this.trigger = this.el.select('div.flag-box',true).first();
40459             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40460             
40461             var _this = this;
40462             
40463             (function(){
40464                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40465                 _this.list.setWidth(lw);
40466             }).defer(100);
40467             
40468             this.list.on('mouseover', this.onViewOver, this);
40469             this.list.on('mousemove', this.onViewMove, this);
40470             this.inputEl().on("keyup", this.onKeyUp, this);
40471             this.inputEl().on("keypress", this.onKeyPress, this);
40472             
40473             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40474
40475             this.view = new Roo.View(this.list, this.tpl, {
40476                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40477             });
40478             
40479             this.view.on('click', this.onViewClick, this);
40480             this.setValue(this.defaultDialCode);
40481         },
40482         
40483         onTriggerClick : function(e)
40484         {
40485             Roo.log('trigger click');
40486             if(this.disabled){
40487                 return;
40488             }
40489             
40490             if(this.isExpanded()){
40491                 this.collapse();
40492                 this.hasFocus = false;
40493             }else {
40494                 this.store.load({});
40495                 this.hasFocus = true;
40496                 this.expand();
40497             }
40498         },
40499         
40500         isExpanded : function()
40501         {
40502             return this.list.isVisible();
40503         },
40504         
40505         collapse : function()
40506         {
40507             if(!this.isExpanded()){
40508                 return;
40509             }
40510             this.list.hide();
40511             Roo.get(document).un('mousedown', this.collapseIf, this);
40512             Roo.get(document).un('mousewheel', this.collapseIf, this);
40513             this.fireEvent('collapse', this);
40514             this.validate();
40515         },
40516         
40517         expand : function()
40518         {
40519             Roo.log('expand');
40520
40521             if(this.isExpanded() || !this.hasFocus){
40522                 return;
40523             }
40524             
40525             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40526             this.list.setWidth(lw);
40527             
40528             this.list.show();
40529             this.restrictHeight();
40530             
40531             Roo.get(document).on('mousedown', this.collapseIf, this);
40532             Roo.get(document).on('mousewheel', this.collapseIf, this);
40533             
40534             this.fireEvent('expand', this);
40535         },
40536         
40537         restrictHeight : function()
40538         {
40539             this.list.alignTo(this.inputEl(), this.listAlign);
40540             this.list.alignTo(this.inputEl(), this.listAlign);
40541         },
40542         
40543         onViewOver : function(e, t)
40544         {
40545             if(this.inKeyMode){
40546                 return;
40547             }
40548             var item = this.view.findItemFromChild(t);
40549             
40550             if(item){
40551                 var index = this.view.indexOf(item);
40552                 this.select(index, false);
40553             }
40554         },
40555
40556         // private
40557         onViewClick : function(view, doFocus, el, e)
40558         {
40559             var index = this.view.getSelectedIndexes()[0];
40560             
40561             var r = this.store.getAt(index);
40562             
40563             if(r){
40564                 this.onSelect(r, index);
40565             }
40566             if(doFocus !== false && !this.blockFocus){
40567                 this.inputEl().focus();
40568             }
40569         },
40570         
40571         onViewMove : function(e, t)
40572         {
40573             this.inKeyMode = false;
40574         },
40575         
40576         select : function(index, scrollIntoView)
40577         {
40578             this.selectedIndex = index;
40579             this.view.select(index);
40580             if(scrollIntoView !== false){
40581                 var el = this.view.getNode(index);
40582                 if(el){
40583                     this.list.scrollChildIntoView(el, false);
40584                 }
40585             }
40586         },
40587         
40588         createList : function()
40589         {
40590             this.list = Roo.get(document.body).createChild({
40591                 tag: 'ul',
40592                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40593                 style: 'display:none'
40594             });
40595             
40596             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40597         },
40598         
40599         collapseIf : function(e)
40600         {
40601             var in_combo  = e.within(this.el);
40602             var in_list =  e.within(this.list);
40603             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40604             
40605             if (in_combo || in_list || is_list) {
40606                 return;
40607             }
40608             this.collapse();
40609         },
40610         
40611         onSelect : function(record, index)
40612         {
40613             if(this.fireEvent('beforeselect', this, record, index) !== false){
40614                 
40615                 this.setFlagClass(record.data.iso2);
40616                 this.setDialCode(record.data.dialCode);
40617                 this.hasFocus = false;
40618                 this.collapse();
40619                 this.fireEvent('select', this, record, index);
40620             }
40621         },
40622         
40623         flagEl : function()
40624         {
40625             var flag = this.el.select('div.flag',true).first();
40626             if(!flag){
40627                 return false;
40628             }
40629             return flag;
40630         },
40631         
40632         dialCodeHolderEl : function()
40633         {
40634             var d = this.el.select('input.dial-code-holder',true).first();
40635             if(!d){
40636                 return false;
40637             }
40638             return d;
40639         },
40640         
40641         setDialCode : function(v)
40642         {
40643             this.dialCodeHolder.dom.value = '+'+v;
40644         },
40645         
40646         setFlagClass : function(n)
40647         {
40648             this.flag.dom.className = 'flag '+n;
40649         },
40650         
40651         getValue : function()
40652         {
40653             var v = this.inputEl().getValue();
40654             if(this.dialCodeHolder) {
40655                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40656             }
40657             return v;
40658         },
40659         
40660         setValue : function(v)
40661         {
40662             var d = this.getDialCode(v);
40663             
40664             //invalid dial code
40665             if(v.length == 0 || !d || d.length == 0) {
40666                 if(this.rendered){
40667                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40668                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40669                 }
40670                 return;
40671             }
40672             
40673             //valid dial code
40674             this.setFlagClass(this.dialCodeMapping[d].iso2);
40675             this.setDialCode(d);
40676             this.inputEl().dom.value = v.replace('+'+d,'');
40677             this.hiddenEl().dom.value = this.getValue();
40678             
40679             this.validate();
40680         },
40681         
40682         getDialCode : function(v)
40683         {
40684             v = v ||  '';
40685             
40686             if (v.length == 0) {
40687                 return this.dialCodeHolder.dom.value;
40688             }
40689             
40690             var dialCode = "";
40691             if (v.charAt(0) != "+") {
40692                 return false;
40693             }
40694             var numericChars = "";
40695             for (var i = 1; i < v.length; i++) {
40696               var c = v.charAt(i);
40697               if (!isNaN(c)) {
40698                 numericChars += c;
40699                 if (this.dialCodeMapping[numericChars]) {
40700                   dialCode = v.substr(1, i);
40701                 }
40702                 if (numericChars.length == 4) {
40703                   break;
40704                 }
40705               }
40706             }
40707             return dialCode;
40708         },
40709         
40710         reset : function()
40711         {
40712             this.setValue(this.defaultDialCode);
40713             this.validate();
40714         },
40715         
40716         hiddenEl : function()
40717         {
40718             return this.el.select('input.hidden-tel-input',true).first();
40719         },
40720         
40721         // after setting val
40722         onKeyUp : function(e){
40723             this.setValue(this.getValue());
40724         },
40725         
40726         onKeyPress : function(e){
40727             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40728                 e.stopEvent();
40729             }
40730         }
40731         
40732 });
40733 /**
40734  * @class Roo.bootstrap.MoneyField
40735  * @extends Roo.bootstrap.ComboBox
40736  * Bootstrap MoneyField class
40737  * 
40738  * @constructor
40739  * Create a new MoneyField.
40740  * @param {Object} config Configuration options
40741  */
40742
40743 Roo.bootstrap.MoneyField = function(config) {
40744     
40745     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40746     
40747 };
40748
40749 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40750     
40751     /**
40752      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40753      */
40754     allowDecimals : true,
40755     /**
40756      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40757      */
40758     decimalSeparator : ".",
40759     /**
40760      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40761      */
40762     decimalPrecision : 0,
40763     /**
40764      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40765      */
40766     allowNegative : true,
40767     /**
40768      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40769      */
40770     allowZero: true,
40771     /**
40772      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40773      */
40774     minValue : Number.NEGATIVE_INFINITY,
40775     /**
40776      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40777      */
40778     maxValue : Number.MAX_VALUE,
40779     /**
40780      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40781      */
40782     minText : "The minimum value for this field is {0}",
40783     /**
40784      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40785      */
40786     maxText : "The maximum value for this field is {0}",
40787     /**
40788      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40789      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40790      */
40791     nanText : "{0} is not a valid number",
40792     /**
40793      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40794      */
40795     castInt : true,
40796     /**
40797      * @cfg {String} defaults currency of the MoneyField
40798      * value should be in lkey
40799      */
40800     defaultCurrency : false,
40801     /**
40802      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40803      */
40804     thousandsDelimiter : false,
40805     /**
40806      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40807      */
40808     max_length: false,
40809     
40810     inputlg : 9,
40811     inputmd : 9,
40812     inputsm : 9,
40813     inputxs : 6,
40814     
40815     store : false,
40816     
40817     getAutoCreate : function()
40818     {
40819         var align = this.labelAlign || this.parentLabelAlign();
40820         
40821         var id = Roo.id();
40822
40823         var cfg = {
40824             cls: 'form-group',
40825             cn: []
40826         };
40827
40828         var input =  {
40829             tag: 'input',
40830             id : id,
40831             cls : 'form-control roo-money-amount-input',
40832             autocomplete: 'new-password'
40833         };
40834         
40835         var hiddenInput = {
40836             tag: 'input',
40837             type: 'hidden',
40838             id: Roo.id(),
40839             cls: 'hidden-number-input'
40840         };
40841         
40842         if(this.max_length) {
40843             input.maxlength = this.max_length; 
40844         }
40845         
40846         if (this.name) {
40847             hiddenInput.name = this.name;
40848         }
40849
40850         if (this.disabled) {
40851             input.disabled = true;
40852         }
40853
40854         var clg = 12 - this.inputlg;
40855         var cmd = 12 - this.inputmd;
40856         var csm = 12 - this.inputsm;
40857         var cxs = 12 - this.inputxs;
40858         
40859         var container = {
40860             tag : 'div',
40861             cls : 'row roo-money-field',
40862             cn : [
40863                 {
40864                     tag : 'div',
40865                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40866                     cn : [
40867                         {
40868                             tag : 'div',
40869                             cls: 'roo-select2-container input-group',
40870                             cn: [
40871                                 {
40872                                     tag : 'input',
40873                                     cls : 'form-control roo-money-currency-input',
40874                                     autocomplete: 'new-password',
40875                                     readOnly : 1,
40876                                     name : this.currencyName
40877                                 },
40878                                 {
40879                                     tag :'span',
40880                                     cls : 'input-group-addon',
40881                                     cn : [
40882                                         {
40883                                             tag: 'span',
40884                                             cls: 'caret'
40885                                         }
40886                                     ]
40887                                 }
40888                             ]
40889                         }
40890                     ]
40891                 },
40892                 {
40893                     tag : 'div',
40894                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40895                     cn : [
40896                         {
40897                             tag: 'div',
40898                             cls: this.hasFeedback ? 'has-feedback' : '',
40899                             cn: [
40900                                 input
40901                             ]
40902                         }
40903                     ]
40904                 }
40905             ]
40906             
40907         };
40908         
40909         if (this.fieldLabel.length) {
40910             var indicator = {
40911                 tag: 'i',
40912                 tooltip: 'This field is required'
40913             };
40914
40915             var label = {
40916                 tag: 'label',
40917                 'for':  id,
40918                 cls: 'control-label',
40919                 cn: []
40920             };
40921
40922             var label_text = {
40923                 tag: 'span',
40924                 html: this.fieldLabel
40925             };
40926
40927             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40928             label.cn = [
40929                 indicator,
40930                 label_text
40931             ];
40932
40933             if(this.indicatorpos == 'right') {
40934                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40935                 label.cn = [
40936                     label_text,
40937                     indicator
40938                 ];
40939             }
40940
40941             if(align == 'left') {
40942                 container = {
40943                     tag: 'div',
40944                     cn: [
40945                         container
40946                     ]
40947                 };
40948
40949                 if(this.labelWidth > 12){
40950                     label.style = "width: " + this.labelWidth + 'px';
40951                 }
40952                 if(this.labelWidth < 13 && this.labelmd == 0){
40953                     this.labelmd = this.labelWidth;
40954                 }
40955                 if(this.labellg > 0){
40956                     label.cls += ' col-lg-' + this.labellg;
40957                     input.cls += ' col-lg-' + (12 - this.labellg);
40958                 }
40959                 if(this.labelmd > 0){
40960                     label.cls += ' col-md-' + this.labelmd;
40961                     container.cls += ' col-md-' + (12 - this.labelmd);
40962                 }
40963                 if(this.labelsm > 0){
40964                     label.cls += ' col-sm-' + this.labelsm;
40965                     container.cls += ' col-sm-' + (12 - this.labelsm);
40966                 }
40967                 if(this.labelxs > 0){
40968                     label.cls += ' col-xs-' + this.labelxs;
40969                     container.cls += ' col-xs-' + (12 - this.labelxs);
40970                 }
40971             }
40972         }
40973
40974         cfg.cn = [
40975             label,
40976             container,
40977             hiddenInput
40978         ];
40979         
40980         var settings = this;
40981
40982         ['xs','sm','md','lg'].map(function(size){
40983             if (settings[size]) {
40984                 cfg.cls += ' col-' + size + '-' + settings[size];
40985             }
40986         });
40987         
40988         return cfg;
40989     },
40990     
40991     initEvents : function()
40992     {
40993         this.indicator = this.indicatorEl();
40994         
40995         this.initCurrencyEvent();
40996         
40997         this.initNumberEvent();
40998     },
40999     
41000     initCurrencyEvent : function()
41001     {
41002         if (!this.store) {
41003             throw "can not find store for combo";
41004         }
41005         
41006         this.store = Roo.factory(this.store, Roo.data);
41007         this.store.parent = this;
41008         
41009         this.createList();
41010         
41011         this.triggerEl = this.el.select('.input-group-addon', true).first();
41012         
41013         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41014         
41015         var _this = this;
41016         
41017         (function(){
41018             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41019             _this.list.setWidth(lw);
41020         }).defer(100);
41021         
41022         this.list.on('mouseover', this.onViewOver, this);
41023         this.list.on('mousemove', this.onViewMove, this);
41024         this.list.on('scroll', this.onViewScroll, this);
41025         
41026         if(!this.tpl){
41027             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41028         }
41029         
41030         this.view = new Roo.View(this.list, this.tpl, {
41031             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41032         });
41033         
41034         this.view.on('click', this.onViewClick, this);
41035         
41036         this.store.on('beforeload', this.onBeforeLoad, this);
41037         this.store.on('load', this.onLoad, this);
41038         this.store.on('loadexception', this.onLoadException, this);
41039         
41040         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41041             "up" : function(e){
41042                 this.inKeyMode = true;
41043                 this.selectPrev();
41044             },
41045
41046             "down" : function(e){
41047                 if(!this.isExpanded()){
41048                     this.onTriggerClick();
41049                 }else{
41050                     this.inKeyMode = true;
41051                     this.selectNext();
41052                 }
41053             },
41054
41055             "enter" : function(e){
41056                 this.collapse();
41057                 
41058                 if(this.fireEvent("specialkey", this, e)){
41059                     this.onViewClick(false);
41060                 }
41061                 
41062                 return true;
41063             },
41064
41065             "esc" : function(e){
41066                 this.collapse();
41067             },
41068
41069             "tab" : function(e){
41070                 this.collapse();
41071                 
41072                 if(this.fireEvent("specialkey", this, e)){
41073                     this.onViewClick(false);
41074                 }
41075                 
41076                 return true;
41077             },
41078
41079             scope : this,
41080
41081             doRelay : function(foo, bar, hname){
41082                 if(hname == 'down' || this.scope.isExpanded()){
41083                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41084                 }
41085                 return true;
41086             },
41087
41088             forceKeyDown: true
41089         });
41090         
41091         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41092         
41093     },
41094     
41095     initNumberEvent : function(e)
41096     {
41097         this.inputEl().on("keydown" , this.fireKey,  this);
41098         this.inputEl().on("focus", this.onFocus,  this);
41099         this.inputEl().on("blur", this.onBlur,  this);
41100         
41101         this.inputEl().relayEvent('keyup', this);
41102         
41103         if(this.indicator){
41104             this.indicator.addClass('invisible');
41105         }
41106  
41107         this.originalValue = this.getValue();
41108         
41109         if(this.validationEvent == 'keyup'){
41110             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41111             this.inputEl().on('keyup', this.filterValidation, this);
41112         }
41113         else if(this.validationEvent !== false){
41114             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41115         }
41116         
41117         if(this.selectOnFocus){
41118             this.on("focus", this.preFocus, this);
41119             
41120         }
41121         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41122             this.inputEl().on("keypress", this.filterKeys, this);
41123         } else {
41124             this.inputEl().relayEvent('keypress', this);
41125         }
41126         
41127         var allowed = "0123456789";
41128         
41129         if(this.allowDecimals){
41130             allowed += this.decimalSeparator;
41131         }
41132         
41133         if(this.allowNegative){
41134             allowed += "-";
41135         }
41136         
41137         if(this.thousandsDelimiter) {
41138             allowed += ",";
41139         }
41140         
41141         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41142         
41143         var keyPress = function(e){
41144             
41145             var k = e.getKey();
41146             
41147             var c = e.getCharCode();
41148             
41149             if(
41150                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41151                     allowed.indexOf(String.fromCharCode(c)) === -1
41152             ){
41153                 e.stopEvent();
41154                 return;
41155             }
41156             
41157             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41158                 return;
41159             }
41160             
41161             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41162                 e.stopEvent();
41163             }
41164         };
41165         
41166         this.inputEl().on("keypress", keyPress, this);
41167         
41168     },
41169     
41170     onTriggerClick : function(e)
41171     {   
41172         if(this.disabled){
41173             return;
41174         }
41175         
41176         this.page = 0;
41177         this.loadNext = false;
41178         
41179         if(this.isExpanded()){
41180             this.collapse();
41181             return;
41182         }
41183         
41184         this.hasFocus = true;
41185         
41186         if(this.triggerAction == 'all') {
41187             this.doQuery(this.allQuery, true);
41188             return;
41189         }
41190         
41191         this.doQuery(this.getRawValue());
41192     },
41193     
41194     getCurrency : function()
41195     {   
41196         var v = this.currencyEl().getValue();
41197         
41198         return v;
41199     },
41200     
41201     restrictHeight : function()
41202     {
41203         this.list.alignTo(this.currencyEl(), this.listAlign);
41204         this.list.alignTo(this.currencyEl(), this.listAlign);
41205     },
41206     
41207     onViewClick : function(view, doFocus, el, e)
41208     {
41209         var index = this.view.getSelectedIndexes()[0];
41210         
41211         var r = this.store.getAt(index);
41212         
41213         if(r){
41214             this.onSelect(r, index);
41215         }
41216     },
41217     
41218     onSelect : function(record, index){
41219         
41220         if(this.fireEvent('beforeselect', this, record, index) !== false){
41221         
41222             this.setFromCurrencyData(index > -1 ? record.data : false);
41223             
41224             this.collapse();
41225             
41226             this.fireEvent('select', this, record, index);
41227         }
41228     },
41229     
41230     setFromCurrencyData : function(o)
41231     {
41232         var currency = '';
41233         
41234         this.lastCurrency = o;
41235         
41236         if (this.currencyField) {
41237             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41238         } else {
41239             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41240         }
41241         
41242         this.lastSelectionText = currency;
41243         
41244         //setting default currency
41245         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41246             this.setCurrency(this.defaultCurrency);
41247             return;
41248         }
41249         
41250         this.setCurrency(currency);
41251     },
41252     
41253     setFromData : function(o)
41254     {
41255         var c = {};
41256         
41257         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41258         
41259         this.setFromCurrencyData(c);
41260         
41261         var value = '';
41262         
41263         if (this.name) {
41264             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41265         } else {
41266             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41267         }
41268         
41269         this.setValue(value);
41270         
41271     },
41272     
41273     setCurrency : function(v)
41274     {   
41275         this.currencyValue = v;
41276         
41277         if(this.rendered){
41278             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41279             this.validate();
41280         }
41281     },
41282     
41283     setValue : function(v)
41284     {
41285         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41286         
41287         this.value = v;
41288         
41289         if(this.rendered){
41290             
41291             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41292             
41293             this.inputEl().dom.value = (v == '') ? '' :
41294                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41295             
41296             if(!this.allowZero && v === '0') {
41297                 this.hiddenEl().dom.value = '';
41298                 this.inputEl().dom.value = '';
41299             }
41300             
41301             this.validate();
41302         }
41303     },
41304     
41305     getRawValue : function()
41306     {
41307         var v = this.inputEl().getValue();
41308         
41309         return v;
41310     },
41311     
41312     getValue : function()
41313     {
41314         return this.fixPrecision(this.parseValue(this.getRawValue()));
41315     },
41316     
41317     parseValue : function(value)
41318     {
41319         if(this.thousandsDelimiter) {
41320             value += "";
41321             r = new RegExp(",", "g");
41322             value = value.replace(r, "");
41323         }
41324         
41325         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41326         return isNaN(value) ? '' : value;
41327         
41328     },
41329     
41330     fixPrecision : function(value)
41331     {
41332         if(this.thousandsDelimiter) {
41333             value += "";
41334             r = new RegExp(",", "g");
41335             value = value.replace(r, "");
41336         }
41337         
41338         var nan = isNaN(value);
41339         
41340         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41341             return nan ? '' : value;
41342         }
41343         return parseFloat(value).toFixed(this.decimalPrecision);
41344     },
41345     
41346     decimalPrecisionFcn : function(v)
41347     {
41348         return Math.floor(v);
41349     },
41350     
41351     validateValue : function(value)
41352     {
41353         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41354             return false;
41355         }
41356         
41357         var num = this.parseValue(value);
41358         
41359         if(isNaN(num)){
41360             this.markInvalid(String.format(this.nanText, value));
41361             return false;
41362         }
41363         
41364         if(num < this.minValue){
41365             this.markInvalid(String.format(this.minText, this.minValue));
41366             return false;
41367         }
41368         
41369         if(num > this.maxValue){
41370             this.markInvalid(String.format(this.maxText, this.maxValue));
41371             return false;
41372         }
41373         
41374         return true;
41375     },
41376     
41377     validate : function()
41378     {
41379         if(this.disabled || this.allowBlank){
41380             this.markValid();
41381             return true;
41382         }
41383         
41384         var currency = this.getCurrency();
41385         
41386         if(this.validateValue(this.getRawValue()) && currency.length){
41387             this.markValid();
41388             return true;
41389         }
41390         
41391         this.markInvalid();
41392         return false;
41393     },
41394     
41395     getName: function()
41396     {
41397         return this.name;
41398     },
41399     
41400     beforeBlur : function()
41401     {
41402         if(!this.castInt){
41403             return;
41404         }
41405         
41406         var v = this.parseValue(this.getRawValue());
41407         
41408         if(v || v == 0){
41409             this.setValue(v);
41410         }
41411     },
41412     
41413     onBlur : function()
41414     {
41415         this.beforeBlur();
41416         
41417         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41418             //this.el.removeClass(this.focusClass);
41419         }
41420         
41421         this.hasFocus = false;
41422         
41423         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41424             this.validate();
41425         }
41426         
41427         var v = this.getValue();
41428         
41429         if(String(v) !== String(this.startValue)){
41430             this.fireEvent('change', this, v, this.startValue);
41431         }
41432         
41433         this.fireEvent("blur", this);
41434     },
41435     
41436     inputEl : function()
41437     {
41438         return this.el.select('.roo-money-amount-input', true).first();
41439     },
41440     
41441     currencyEl : function()
41442     {
41443         return this.el.select('.roo-money-currency-input', true).first();
41444     },
41445     
41446     hiddenEl : function()
41447     {
41448         return this.el.select('input.hidden-number-input',true).first();
41449     }
41450     
41451 });