4f5603062765f895fb10295bb8111b539211c89f
[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
2913     resize : function()
2914     {
2915         this.maskEl.setSize(
2916             Roo.lib.Dom.getViewWidth(true),
2917             Roo.lib.Dom.getViewHeight(true)
2918         );
2919         
2920         if (this.fitwindow) {
2921             
2922            
2923             this.setSize(
2924                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2925                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
2926             );
2927             return;
2928         }
2929         
2930         if(this.max_width !== 0) {
2931             
2932             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2933             
2934             if(this.height) {
2935                 this.setSize(w, this.height);
2936                 return;
2937             }
2938             
2939             if(this.max_height) {
2940                 this.setSize(w,Math.min(
2941                     this.max_height,
2942                     Roo.lib.Dom.getViewportHeight(true) - 60
2943                 ));
2944                 
2945                 return;
2946             }
2947             
2948             if(!this.fit_content) {
2949                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2950                 return;
2951             }
2952             
2953             this.setSize(w, Math.min(
2954                 60 +
2955                 this.headerEl.getHeight() + 
2956                 this.footerEl.getHeight() + 
2957                 this.getChildHeight(this.bodyEl.dom.childNodes),
2958                 Roo.lib.Dom.getViewportHeight(true) - 60)
2959             );
2960         }
2961         
2962     },
2963
2964     setSize : function(w,h)
2965     {
2966         if (!w && !h) {
2967             return;
2968         }
2969         
2970         this.resizeTo(w,h);
2971     },
2972
2973     show : function() {
2974
2975         if (!this.rendered) {
2976             this.render();
2977         }
2978
2979         //this.el.setStyle('display', 'block');
2980         this.el.removeClass('hideing');
2981         this.el.dom.style.display='block';
2982         
2983         Roo.get(document.body).addClass('modal-open');
2984  
2985         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2986             
2987             (function(){
2988                 this.el.addClass('show');
2989                 this.el.addClass('in');
2990             }).defer(50, this);
2991         }else{
2992             this.el.addClass('show');
2993             this.el.addClass('in');
2994         }
2995
2996         // not sure how we can show data in here..
2997         //if (this.tmpl) {
2998         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2999         //}
3000
3001         Roo.get(document.body).addClass("x-body-masked");
3002         
3003         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
3004         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3005         this.maskEl.dom.style.display = 'block';
3006         this.maskEl.addClass('show');
3007         
3008         
3009         this.resize();
3010         
3011         this.fireEvent('show', this);
3012
3013         // set zindex here - otherwise it appears to be ignored...
3014         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3015
3016         (function () {
3017             this.items.forEach( function(e) {
3018                 e.layout ? e.layout() : false;
3019
3020             });
3021         }).defer(100,this);
3022
3023     },
3024     hide : function()
3025     {
3026         if(this.fireEvent("beforehide", this) !== false){
3027             
3028             this.maskEl.removeClass('show');
3029             
3030             this.maskEl.dom.style.display = '';
3031             Roo.get(document.body).removeClass("x-body-masked");
3032             this.el.removeClass('in');
3033             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3034
3035             if(this.animate){ // why
3036                 this.el.addClass('hideing');
3037                 this.el.removeClass('show');
3038                 (function(){
3039                     if (!this.el.hasClass('hideing')) {
3040                         return; // it's been shown again...
3041                     }
3042                     
3043                     this.el.dom.style.display='';
3044
3045                     Roo.get(document.body).removeClass('modal-open');
3046                     this.el.removeClass('hideing');
3047                 }).defer(150,this);
3048                 
3049             }else{
3050                 this.el.removeClass('show');
3051                 this.el.dom.style.display='';
3052                 Roo.get(document.body).removeClass('modal-open');
3053
3054             }
3055             this.fireEvent('hide', this);
3056         }
3057     },
3058     isVisible : function()
3059     {
3060         
3061         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3062         
3063     },
3064
3065     addButton : function(str, cb)
3066     {
3067
3068
3069         var b = Roo.apply({}, { html : str } );
3070         b.xns = b.xns || Roo.bootstrap;
3071         b.xtype = b.xtype || 'Button';
3072         if (typeof(b.listeners) == 'undefined') {
3073             b.listeners = { click : cb.createDelegate(this)  };
3074         }
3075
3076         var btn = Roo.factory(b);
3077
3078         btn.render(this.getButtonContainer());
3079
3080         return btn;
3081
3082     },
3083
3084     setDefaultButton : function(btn)
3085     {
3086         //this.el.select('.modal-footer').()
3087     },
3088
3089     resizeTo: function(w,h)
3090     {
3091         this.dialogEl.setWidth(w);
3092         
3093         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
3094
3095         this.bodyEl.setHeight(h - diff);
3096         
3097         this.fireEvent('resize', this);
3098     },
3099     
3100     setContentSize  : function(w, h)
3101     {
3102
3103     },
3104     onButtonClick: function(btn,e)
3105     {
3106         //Roo.log([a,b,c]);
3107         this.fireEvent('btnclick', btn.name, e);
3108     },
3109      /**
3110      * Set the title of the Dialog
3111      * @param {String} str new Title
3112      */
3113     setTitle: function(str) {
3114         this.titleEl.dom.innerHTML = str;
3115     },
3116     /**
3117      * Set the body of the Dialog
3118      * @param {String} str new Title
3119      */
3120     setBody: function(str) {
3121         this.bodyEl.dom.innerHTML = str;
3122     },
3123     /**
3124      * Set the body of the Dialog using the template
3125      * @param {Obj} data - apply this data to the template and replace the body contents.
3126      */
3127     applyBody: function(obj)
3128     {
3129         if (!this.tmpl) {
3130             Roo.log("Error - using apply Body without a template");
3131             //code
3132         }
3133         this.tmpl.overwrite(this.bodyEl, obj);
3134     },
3135     
3136     getChildHeight : function(child_nodes)
3137     {
3138         if(
3139             !child_nodes ||
3140             child_nodes.length == 0
3141         ) {
3142             return;
3143         }
3144         
3145         var child_height = 0;
3146         
3147         for(var i = 0; i < child_nodes.length; i++) {
3148             
3149             /*
3150             * for modal with tabs...
3151             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3152                 
3153                 var layout_childs = child_nodes[i].childNodes;
3154                 
3155                 for(var j = 0; j < layout_childs.length; j++) {
3156                     
3157                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3158                         
3159                         var layout_body_childs = layout_childs[j].childNodes;
3160                         
3161                         for(var k = 0; k < layout_body_childs.length; k++) {
3162                             
3163                             if(layout_body_childs[k].classList.contains('navbar')) {
3164                                 child_height += layout_body_childs[k].offsetHeight;
3165                                 continue;
3166                             }
3167                             
3168                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3169                                 
3170                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3171                                 
3172                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3173                                     
3174                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3175                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3176                                         continue;
3177                                     }
3178                                     
3179                                 }
3180                                 
3181                             }
3182                             
3183                         }
3184                     }
3185                 }
3186                 continue;
3187             }
3188             */
3189             
3190             child_height += child_nodes[i].offsetHeight;
3191             // Roo.log(child_nodes[i].offsetHeight);
3192         }
3193         
3194         return child_height;
3195     }
3196
3197 });
3198
3199
3200 Roo.apply(Roo.bootstrap.Modal,  {
3201     /**
3202          * Button config that displays a single OK button
3203          * @type Object
3204          */
3205         OK :  [{
3206             name : 'ok',
3207             weight : 'primary',
3208             html : 'OK'
3209         }],
3210         /**
3211          * Button config that displays Yes and No buttons
3212          * @type Object
3213          */
3214         YESNO : [
3215             {
3216                 name  : 'no',
3217                 html : 'No'
3218             },
3219             {
3220                 name  :'yes',
3221                 weight : 'primary',
3222                 html : 'Yes'
3223             }
3224         ],
3225
3226         /**
3227          * Button config that displays OK and Cancel buttons
3228          * @type Object
3229          */
3230         OKCANCEL : [
3231             {
3232                name : 'cancel',
3233                 html : 'Cancel'
3234             },
3235             {
3236                 name : 'ok',
3237                 weight : 'primary',
3238                 html : 'OK'
3239             }
3240         ],
3241         /**
3242          * Button config that displays Yes, No and Cancel buttons
3243          * @type Object
3244          */
3245         YESNOCANCEL : [
3246             {
3247                 name : 'yes',
3248                 weight : 'primary',
3249                 html : 'Yes'
3250             },
3251             {
3252                 name : 'no',
3253                 html : 'No'
3254             },
3255             {
3256                 name : 'cancel',
3257                 html : 'Cancel'
3258             }
3259         ],
3260         
3261         zIndex : 10001
3262 });
3263 /*
3264  * - LGPL
3265  *
3266  * messagebox - can be used as a replace
3267  * 
3268  */
3269 /**
3270  * @class Roo.MessageBox
3271  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3272  * Example usage:
3273  *<pre><code>
3274 // Basic alert:
3275 Roo.Msg.alert('Status', 'Changes saved successfully.');
3276
3277 // Prompt for user data:
3278 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3279     if (btn == 'ok'){
3280         // process text value...
3281     }
3282 });
3283
3284 // Show a dialog using config options:
3285 Roo.Msg.show({
3286    title:'Save Changes?',
3287    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3288    buttons: Roo.Msg.YESNOCANCEL,
3289    fn: processResult,
3290    animEl: 'elId'
3291 });
3292 </code></pre>
3293  * @singleton
3294  */
3295 Roo.bootstrap.MessageBox = function(){
3296     var dlg, opt, mask, waitTimer;
3297     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3298     var buttons, activeTextEl, bwidth;
3299
3300     
3301     // private
3302     var handleButton = function(button){
3303         dlg.hide();
3304         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3305     };
3306
3307     // private
3308     var handleHide = function(){
3309         if(opt && opt.cls){
3310             dlg.el.removeClass(opt.cls);
3311         }
3312         //if(waitTimer){
3313         //    Roo.TaskMgr.stop(waitTimer);
3314         //    waitTimer = null;
3315         //}
3316     };
3317
3318     // private
3319     var updateButtons = function(b){
3320         var width = 0;
3321         if(!b){
3322             buttons["ok"].hide();
3323             buttons["cancel"].hide();
3324             buttons["yes"].hide();
3325             buttons["no"].hide();
3326             dlg.footerEl.hide();
3327             
3328             return width;
3329         }
3330         dlg.footerEl.show();
3331         for(var k in buttons){
3332             if(typeof buttons[k] != "function"){
3333                 if(b[k]){
3334                     buttons[k].show();
3335                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3336                     width += buttons[k].el.getWidth()+15;
3337                 }else{
3338                     buttons[k].hide();
3339                 }
3340             }
3341         }
3342         return width;
3343     };
3344
3345     // private
3346     var handleEsc = function(d, k, e){
3347         if(opt && opt.closable !== false){
3348             dlg.hide();
3349         }
3350         if(e){
3351             e.stopEvent();
3352         }
3353     };
3354
3355     return {
3356         /**
3357          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3358          * @return {Roo.BasicDialog} The BasicDialog element
3359          */
3360         getDialog : function(){
3361            if(!dlg){
3362                 dlg = new Roo.bootstrap.Modal( {
3363                     //draggable: true,
3364                     //resizable:false,
3365                     //constraintoviewport:false,
3366                     //fixedcenter:true,
3367                     //collapsible : false,
3368                     //shim:true,
3369                     //modal: true,
3370                 //    width: 'auto',
3371                   //  height:100,
3372                     //buttonAlign:"center",
3373                     closeClick : function(){
3374                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3375                             handleButton("no");
3376                         }else{
3377                             handleButton("cancel");
3378                         }
3379                     }
3380                 });
3381                 dlg.render();
3382                 dlg.on("hide", handleHide);
3383                 mask = dlg.mask;
3384                 //dlg.addKeyListener(27, handleEsc);
3385                 buttons = {};
3386                 this.buttons = buttons;
3387                 var bt = this.buttonText;
3388                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3389                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3390                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3391                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3392                 //Roo.log(buttons);
3393                 bodyEl = dlg.bodyEl.createChild({
3394
3395                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3396                         '<textarea class="roo-mb-textarea"></textarea>' +
3397                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3398                 });
3399                 msgEl = bodyEl.dom.firstChild;
3400                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3401                 textboxEl.enableDisplayMode();
3402                 textboxEl.addKeyListener([10,13], function(){
3403                     if(dlg.isVisible() && opt && opt.buttons){
3404                         if(opt.buttons.ok){
3405                             handleButton("ok");
3406                         }else if(opt.buttons.yes){
3407                             handleButton("yes");
3408                         }
3409                     }
3410                 });
3411                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3412                 textareaEl.enableDisplayMode();
3413                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3414                 progressEl.enableDisplayMode();
3415                 
3416                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3417                 var pf = progressEl.dom.firstChild;
3418                 if (pf) {
3419                     pp = Roo.get(pf.firstChild);
3420                     pp.setHeight(pf.offsetHeight);
3421                 }
3422                 
3423             }
3424             return dlg;
3425         },
3426
3427         /**
3428          * Updates the message box body text
3429          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3430          * the XHTML-compliant non-breaking space character '&amp;#160;')
3431          * @return {Roo.MessageBox} This message box
3432          */
3433         updateText : function(text)
3434         {
3435             if(!dlg.isVisible() && !opt.width){
3436                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3437                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3438             }
3439             msgEl.innerHTML = text || '&#160;';
3440       
3441             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3442             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3443             var w = Math.max(
3444                     Math.min(opt.width || cw , this.maxWidth), 
3445                     Math.max(opt.minWidth || this.minWidth, bwidth)
3446             );
3447             if(opt.prompt){
3448                 activeTextEl.setWidth(w);
3449             }
3450             if(dlg.isVisible()){
3451                 dlg.fixedcenter = false;
3452             }
3453             // to big, make it scroll. = But as usual stupid IE does not support
3454             // !important..
3455             
3456             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3457                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3458                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3459             } else {
3460                 bodyEl.dom.style.height = '';
3461                 bodyEl.dom.style.overflowY = '';
3462             }
3463             if (cw > w) {
3464                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3465             } else {
3466                 bodyEl.dom.style.overflowX = '';
3467             }
3468             
3469             dlg.setContentSize(w, bodyEl.getHeight());
3470             if(dlg.isVisible()){
3471                 dlg.fixedcenter = true;
3472             }
3473             return this;
3474         },
3475
3476         /**
3477          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3478          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3479          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3480          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3481          * @return {Roo.MessageBox} This message box
3482          */
3483         updateProgress : function(value, text){
3484             if(text){
3485                 this.updateText(text);
3486             }
3487             
3488             if (pp) { // weird bug on my firefox - for some reason this is not defined
3489                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3490                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3491             }
3492             return this;
3493         },        
3494
3495         /**
3496          * Returns true if the message box is currently displayed
3497          * @return {Boolean} True if the message box is visible, else false
3498          */
3499         isVisible : function(){
3500             return dlg && dlg.isVisible();  
3501         },
3502
3503         /**
3504          * Hides the message box if it is displayed
3505          */
3506         hide : function(){
3507             if(this.isVisible()){
3508                 dlg.hide();
3509             }  
3510         },
3511
3512         /**
3513          * Displays a new message box, or reinitializes an existing message box, based on the config options
3514          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3515          * The following config object properties are supported:
3516          * <pre>
3517 Property    Type             Description
3518 ----------  ---------------  ------------------------------------------------------------------------------------
3519 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3520                                    closes (defaults to undefined)
3521 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3522                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3523 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3524                                    progress and wait dialogs will ignore this property and always hide the
3525                                    close button as they can only be closed programmatically.
3526 cls               String           A custom CSS class to apply to the message box element
3527 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3528                                    displayed (defaults to 75)
3529 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3530                                    function will be btn (the name of the button that was clicked, if applicable,
3531                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3532                                    Progress and wait dialogs will ignore this option since they do not respond to
3533                                    user actions and can only be closed programmatically, so any required function
3534                                    should be called by the same code after it closes the dialog.
3535 icon              String           A CSS class that provides a background image to be used as an icon for
3536                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3537 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3538 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3539 modal             Boolean          False to allow user interaction with the page while the message box is
3540                                    displayed (defaults to true)
3541 msg               String           A string that will replace the existing message box body text (defaults
3542                                    to the XHTML-compliant non-breaking space character '&#160;')
3543 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3544 progress          Boolean          True to display a progress bar (defaults to false)
3545 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3546 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3547 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3548 title             String           The title text
3549 value             String           The string value to set into the active textbox element if displayed
3550 wait              Boolean          True to display a progress bar (defaults to false)
3551 width             Number           The width of the dialog in pixels
3552 </pre>
3553          *
3554          * Example usage:
3555          * <pre><code>
3556 Roo.Msg.show({
3557    title: 'Address',
3558    msg: 'Please enter your address:',
3559    width: 300,
3560    buttons: Roo.MessageBox.OKCANCEL,
3561    multiline: true,
3562    fn: saveAddress,
3563    animEl: 'addAddressBtn'
3564 });
3565 </code></pre>
3566          * @param {Object} config Configuration options
3567          * @return {Roo.MessageBox} This message box
3568          */
3569         show : function(options)
3570         {
3571             
3572             // this causes nightmares if you show one dialog after another
3573             // especially on callbacks..
3574              
3575             if(this.isVisible()){
3576                 
3577                 this.hide();
3578                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3579                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3580                 Roo.log("New Dialog Message:" +  options.msg )
3581                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3582                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3583                 
3584             }
3585             var d = this.getDialog();
3586             opt = options;
3587             d.setTitle(opt.title || "&#160;");
3588             d.closeEl.setDisplayed(opt.closable !== false);
3589             activeTextEl = textboxEl;
3590             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3591             if(opt.prompt){
3592                 if(opt.multiline){
3593                     textboxEl.hide();
3594                     textareaEl.show();
3595                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3596                         opt.multiline : this.defaultTextHeight);
3597                     activeTextEl = textareaEl;
3598                 }else{
3599                     textboxEl.show();
3600                     textareaEl.hide();
3601                 }
3602             }else{
3603                 textboxEl.hide();
3604                 textareaEl.hide();
3605             }
3606             progressEl.setDisplayed(opt.progress === true);
3607             if (opt.progress) {
3608                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3609             }
3610             this.updateProgress(0);
3611             activeTextEl.dom.value = opt.value || "";
3612             if(opt.prompt){
3613                 dlg.setDefaultButton(activeTextEl);
3614             }else{
3615                 var bs = opt.buttons;
3616                 var db = null;
3617                 if(bs && bs.ok){
3618                     db = buttons["ok"];
3619                 }else if(bs && bs.yes){
3620                     db = buttons["yes"];
3621                 }
3622                 dlg.setDefaultButton(db);
3623             }
3624             bwidth = updateButtons(opt.buttons);
3625             this.updateText(opt.msg);
3626             if(opt.cls){
3627                 d.el.addClass(opt.cls);
3628             }
3629             d.proxyDrag = opt.proxyDrag === true;
3630             d.modal = opt.modal !== false;
3631             d.mask = opt.modal !== false ? mask : false;
3632             if(!d.isVisible()){
3633                 // force it to the end of the z-index stack so it gets a cursor in FF
3634                 document.body.appendChild(dlg.el.dom);
3635                 d.animateTarget = null;
3636                 d.show(options.animEl);
3637             }
3638             return this;
3639         },
3640
3641         /**
3642          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3643          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3644          * and closing the message box when the process is complete.
3645          * @param {String} title The title bar text
3646          * @param {String} msg The message box body text
3647          * @return {Roo.MessageBox} This message box
3648          */
3649         progress : function(title, msg){
3650             this.show({
3651                 title : title,
3652                 msg : msg,
3653                 buttons: false,
3654                 progress:true,
3655                 closable:false,
3656                 minWidth: this.minProgressWidth,
3657                 modal : true
3658             });
3659             return this;
3660         },
3661
3662         /**
3663          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3664          * If a callback function is passed it will be called after the user clicks the button, and the
3665          * id of the button that was clicked will be passed as the only parameter to the callback
3666          * (could also be the top-right close button).
3667          * @param {String} title The title bar text
3668          * @param {String} msg The message box body text
3669          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3670          * @param {Object} scope (optional) The scope of the callback function
3671          * @return {Roo.MessageBox} This message box
3672          */
3673         alert : function(title, msg, fn, scope)
3674         {
3675             this.show({
3676                 title : title,
3677                 msg : msg,
3678                 buttons: this.OK,
3679                 fn: fn,
3680                 closable : false,
3681                 scope : scope,
3682                 modal : true
3683             });
3684             return this;
3685         },
3686
3687         /**
3688          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3689          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3690          * You are responsible for closing the message box when the process is complete.
3691          * @param {String} msg The message box body text
3692          * @param {String} title (optional) The title bar text
3693          * @return {Roo.MessageBox} This message box
3694          */
3695         wait : function(msg, title){
3696             this.show({
3697                 title : title,
3698                 msg : msg,
3699                 buttons: false,
3700                 closable:false,
3701                 progress:true,
3702                 modal:true,
3703                 width:300,
3704                 wait:true
3705             });
3706             waitTimer = Roo.TaskMgr.start({
3707                 run: function(i){
3708                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3709                 },
3710                 interval: 1000
3711             });
3712             return this;
3713         },
3714
3715         /**
3716          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3717          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3718          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3719          * @param {String} title The title bar text
3720          * @param {String} msg The message box body text
3721          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3722          * @param {Object} scope (optional) The scope of the callback function
3723          * @return {Roo.MessageBox} This message box
3724          */
3725         confirm : function(title, msg, fn, scope){
3726             this.show({
3727                 title : title,
3728                 msg : msg,
3729                 buttons: this.YESNO,
3730                 fn: fn,
3731                 scope : scope,
3732                 modal : true
3733             });
3734             return this;
3735         },
3736
3737         /**
3738          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3739          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3740          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3741          * (could also be the top-right close button) and the text that was entered will be passed as the two
3742          * parameters to the callback.
3743          * @param {String} title The title bar text
3744          * @param {String} msg The message box body text
3745          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3746          * @param {Object} scope (optional) The scope of the callback function
3747          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3748          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3749          * @return {Roo.MessageBox} This message box
3750          */
3751         prompt : function(title, msg, fn, scope, multiline){
3752             this.show({
3753                 title : title,
3754                 msg : msg,
3755                 buttons: this.OKCANCEL,
3756                 fn: fn,
3757                 minWidth:250,
3758                 scope : scope,
3759                 prompt:true,
3760                 multiline: multiline,
3761                 modal : true
3762             });
3763             return this;
3764         },
3765
3766         /**
3767          * Button config that displays a single OK button
3768          * @type Object
3769          */
3770         OK : {ok:true},
3771         /**
3772          * Button config that displays Yes and No buttons
3773          * @type Object
3774          */
3775         YESNO : {yes:true, no:true},
3776         /**
3777          * Button config that displays OK and Cancel buttons
3778          * @type Object
3779          */
3780         OKCANCEL : {ok:true, cancel:true},
3781         /**
3782          * Button config that displays Yes, No and Cancel buttons
3783          * @type Object
3784          */
3785         YESNOCANCEL : {yes:true, no:true, cancel:true},
3786
3787         /**
3788          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3789          * @type Number
3790          */
3791         defaultTextHeight : 75,
3792         /**
3793          * The maximum width in pixels of the message box (defaults to 600)
3794          * @type Number
3795          */
3796         maxWidth : 600,
3797         /**
3798          * The minimum width in pixels of the message box (defaults to 100)
3799          * @type Number
3800          */
3801         minWidth : 100,
3802         /**
3803          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3804          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3805          * @type Number
3806          */
3807         minProgressWidth : 250,
3808         /**
3809          * An object containing the default button text strings that can be overriden for localized language support.
3810          * Supported properties are: ok, cancel, yes and no.
3811          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3812          * @type Object
3813          */
3814         buttonText : {
3815             ok : "OK",
3816             cancel : "Cancel",
3817             yes : "Yes",
3818             no : "No"
3819         }
3820     };
3821 }();
3822
3823 /**
3824  * Shorthand for {@link Roo.MessageBox}
3825  */
3826 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3827 Roo.Msg = Roo.Msg || Roo.MessageBox;
3828 /*
3829  * - LGPL
3830  *
3831  * navbar
3832  * 
3833  */
3834
3835 /**
3836  * @class Roo.bootstrap.Navbar
3837  * @extends Roo.bootstrap.Component
3838  * Bootstrap Navbar class
3839
3840  * @constructor
3841  * Create a new Navbar
3842  * @param {Object} config The config object
3843  */
3844
3845
3846 Roo.bootstrap.Navbar = function(config){
3847     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3848     this.addEvents({
3849         // raw events
3850         /**
3851          * @event beforetoggle
3852          * Fire before toggle the menu
3853          * @param {Roo.EventObject} e
3854          */
3855         "beforetoggle" : true
3856     });
3857 };
3858
3859 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3860     
3861     
3862    
3863     // private
3864     navItems : false,
3865     loadMask : false,
3866     
3867     
3868     getAutoCreate : function(){
3869         
3870         
3871         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3872         
3873     },
3874     
3875     initEvents :function ()
3876     {
3877         //Roo.log(this.el.select('.navbar-toggle',true));
3878         this.el.select('.navbar-toggle',true).on('click', function() {
3879             if(this.fireEvent('beforetoggle', this) !== false){
3880                 var ce = this.el.select('.navbar-collapse',true).first();
3881                 ce.toggleClass('in'); // old...
3882                 if (ce.hasClass('collapse')) {
3883                     // show it...
3884                     ce.removeClass('collapse');
3885                     ce.addClass('show');
3886                     var h = ce.getHeight();
3887                     Roo.log(h);
3888                     ce.removeClass('show');
3889                     // at this point we should be able to see it..
3890                     ce.addClass('collapsing');
3891                     
3892                     ce.setHeight(0); // resize it ...
3893                     ce.on('transitionend', function() {
3894                         Roo.log('done transition');
3895                         ce.removeClass('collapsing');
3896                         ce.addClass('show');
3897                         ce.removeClass('collapse');
3898
3899                         ce.dom.style.height = '';
3900                     }, this, { single: true} );
3901                     ce.setHeight(h);
3902                     
3903                 } else {
3904                     ce.setHeight(ce.getHeight());
3905                     ce.removeClass('show');
3906                     ce.addClass('collapsing');
3907                     
3908                     ce.on('transitionend', function() {
3909                         ce.dom.style.height = '';
3910                         ce.removeClass('collapsing');
3911                         ce.addClass('collapse');
3912                     }, this, { single: true} );
3913                     ce.setHeight(0);
3914                 }
3915             }
3916             
3917         }, this);
3918         
3919         var mark = {
3920             tag: "div",
3921             cls:"x-dlg-mask"
3922         };
3923         
3924         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3925         
3926         var size = this.el.getSize();
3927         this.maskEl.setSize(size.width, size.height);
3928         this.maskEl.enableDisplayMode("block");
3929         this.maskEl.hide();
3930         
3931         if(this.loadMask){
3932             this.maskEl.show();
3933         }
3934     },
3935     
3936     
3937     getChildContainer : function()
3938     {
3939         if (this.el.select('.collapse').getCount()) {
3940             return this.el.select('.collapse',true).first();
3941         }
3942         
3943         return this.el;
3944     },
3945     
3946     mask : function()
3947     {
3948         this.maskEl.show();
3949     },
3950     
3951     unmask : function()
3952     {
3953         this.maskEl.hide();
3954     } 
3955     
3956     
3957     
3958     
3959 });
3960
3961
3962
3963  
3964
3965  /*
3966  * - LGPL
3967  *
3968  * navbar
3969  * 
3970  */
3971
3972 /**
3973  * @class Roo.bootstrap.NavSimplebar
3974  * @extends Roo.bootstrap.Navbar
3975  * Bootstrap Sidebar class
3976  *
3977  * @cfg {Boolean} inverse is inverted color
3978  * 
3979  * @cfg {String} type (nav | pills | tabs)
3980  * @cfg {Boolean} arrangement stacked | justified
3981  * @cfg {String} align (left | right) alignment
3982  * 
3983  * @cfg {Boolean} main (true|false) main nav bar? default false
3984  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3985  * 
3986  * @cfg {String} tag (header|footer|nav|div) default is nav 
3987
3988  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3989  * 
3990  * 
3991  * @constructor
3992  * Create a new Sidebar
3993  * @param {Object} config The config object
3994  */
3995
3996
3997 Roo.bootstrap.NavSimplebar = function(config){
3998     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3999 };
4000
4001 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
4002     
4003     inverse: false,
4004     
4005     type: false,
4006     arrangement: '',
4007     align : false,
4008     
4009     weight : 'light',
4010     
4011     main : false,
4012     
4013     
4014     tag : false,
4015     
4016     
4017     getAutoCreate : function(){
4018         
4019         
4020         var cfg = {
4021             tag : this.tag || 'div',
4022             cls : 'navbar navbar-expand-lg roo-navbar-simple'
4023         };
4024         if (['light','white'].indexOf(this.weight) > -1) {
4025             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4026         }
4027         cfg.cls += ' bg-' + this.weight;
4028         
4029         if (this.inverse) {
4030             cfg.cls += ' navbar-inverse';
4031             
4032         }
4033         
4034         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4035         
4036         //if (Roo.bootstrap.version == 4) {
4037         //    return cfg;
4038         //}
4039         
4040         cfg.cn = [
4041             {
4042                 cls: 'nav',
4043                 tag : 'ul'
4044             }
4045         ];
4046         
4047          
4048         this.type = this.type || 'nav';
4049         if (['tabs','pills'].indexOf(this.type) != -1) {
4050             cfg.cn[0].cls += ' nav-' + this.type
4051         
4052         
4053         } else {
4054             if (this.type!=='nav') {
4055                 Roo.log('nav type must be nav/tabs/pills')
4056             }
4057             cfg.cn[0].cls += ' navbar-nav'
4058         }
4059         
4060         
4061         
4062         
4063         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4064             cfg.cn[0].cls += ' nav-' + this.arrangement;
4065         }
4066         
4067         
4068         if (this.align === 'right') {
4069             cfg.cn[0].cls += ' navbar-right';
4070         }
4071         
4072         
4073         
4074         
4075         return cfg;
4076     
4077         
4078     }
4079     
4080     
4081     
4082 });
4083
4084
4085
4086  
4087
4088  
4089        /*
4090  * - LGPL
4091  *
4092  * navbar
4093  * navbar-fixed-top
4094  * navbar-expand-md  fixed-top 
4095  */
4096
4097 /**
4098  * @class Roo.bootstrap.NavHeaderbar
4099  * @extends Roo.bootstrap.NavSimplebar
4100  * Bootstrap Sidebar class
4101  *
4102  * @cfg {String} brand what is brand
4103  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4104  * @cfg {String} brand_href href of the brand
4105  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4106  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4107  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4108  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4109  * 
4110  * @constructor
4111  * Create a new Sidebar
4112  * @param {Object} config The config object
4113  */
4114
4115
4116 Roo.bootstrap.NavHeaderbar = function(config){
4117     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4118       
4119 };
4120
4121 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4122     
4123     position: '',
4124     brand: '',
4125     brand_href: false,
4126     srButton : true,
4127     autohide : false,
4128     desktopCenter : false,
4129    
4130     
4131     getAutoCreate : function(){
4132         
4133         var   cfg = {
4134             tag: this.nav || 'nav',
4135             cls: 'navbar navbar-expand-md',
4136             role: 'navigation',
4137             cn: []
4138         };
4139         
4140         var cn = cfg.cn;
4141         if (this.desktopCenter) {
4142             cn.push({cls : 'container', cn : []});
4143             cn = cn[0].cn;
4144         }
4145         
4146         if(this.srButton){
4147             var btn = {
4148                 tag: 'button',
4149                 type: 'button',
4150                 cls: 'navbar-toggle navbar-toggler',
4151                 'data-toggle': 'collapse',
4152                 cn: [
4153                     {
4154                         tag: 'span',
4155                         cls: 'sr-only',
4156                         html: 'Toggle navigation'
4157                     },
4158                     {
4159                         tag: 'span',
4160                         cls: 'icon-bar navbar-toggler-icon'
4161                     },
4162                     {
4163                         tag: 'span',
4164                         cls: 'icon-bar'
4165                     },
4166                     {
4167                         tag: 'span',
4168                         cls: 'icon-bar'
4169                     }
4170                 ]
4171             };
4172             
4173             cn.push( Roo.bootstrap.version == 4 ? btn : {
4174                 tag: 'div',
4175                 cls: 'navbar-header',
4176                 cn: [
4177                     btn
4178                 ]
4179             });
4180         }
4181         
4182         cn.push({
4183             tag: 'div',
4184             cls: 'collapse navbar-collapse',
4185             cn : []
4186         });
4187         
4188         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4189         
4190         if (['light','white'].indexOf(this.weight) > -1) {
4191             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4192         }
4193         cfg.cls += ' bg-' + this.weight;
4194         
4195         
4196         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4197             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4198             
4199             // tag can override this..
4200             
4201             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4202         }
4203         
4204         if (this.brand !== '') {
4205             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4206             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4207                 tag: 'a',
4208                 href: this.brand_href ? this.brand_href : '#',
4209                 cls: 'navbar-brand',
4210                 cn: [
4211                 this.brand
4212                 ]
4213             });
4214         }
4215         
4216         if(this.main){
4217             cfg.cls += ' main-nav';
4218         }
4219         
4220         
4221         return cfg;
4222
4223         
4224     },
4225     getHeaderChildContainer : function()
4226     {
4227         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4228             return this.el.select('.navbar-header',true).first();
4229         }
4230         
4231         return this.getChildContainer();
4232     },
4233     
4234     
4235     initEvents : function()
4236     {
4237         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4238         
4239         if (this.autohide) {
4240             
4241             var prevScroll = 0;
4242             var ft = this.el;
4243             
4244             Roo.get(document).on('scroll',function(e) {
4245                 var ns = Roo.get(document).getScroll().top;
4246                 var os = prevScroll;
4247                 prevScroll = ns;
4248                 
4249                 if(ns > os){
4250                     ft.removeClass('slideDown');
4251                     ft.addClass('slideUp');
4252                     return;
4253                 }
4254                 ft.removeClass('slideUp');
4255                 ft.addClass('slideDown');
4256                  
4257               
4258           },this);
4259         }
4260     }    
4261     
4262 });
4263
4264
4265
4266  
4267
4268  /*
4269  * - LGPL
4270  *
4271  * navbar
4272  * 
4273  */
4274
4275 /**
4276  * @class Roo.bootstrap.NavSidebar
4277  * @extends Roo.bootstrap.Navbar
4278  * Bootstrap Sidebar class
4279  * 
4280  * @constructor
4281  * Create a new Sidebar
4282  * @param {Object} config The config object
4283  */
4284
4285
4286 Roo.bootstrap.NavSidebar = function(config){
4287     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4288 };
4289
4290 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4291     
4292     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4293     
4294     getAutoCreate : function(){
4295         
4296         
4297         return  {
4298             tag: 'div',
4299             cls: 'sidebar sidebar-nav'
4300         };
4301     
4302         
4303     }
4304     
4305     
4306     
4307 });
4308
4309
4310
4311  
4312
4313  /*
4314  * - LGPL
4315  *
4316  * nav group
4317  * 
4318  */
4319
4320 /**
4321  * @class Roo.bootstrap.NavGroup
4322  * @extends Roo.bootstrap.Component
4323  * Bootstrap NavGroup class
4324  * @cfg {String} align (left|right)
4325  * @cfg {Boolean} inverse
4326  * @cfg {String} type (nav|pills|tab) default nav
4327  * @cfg {String} navId - reference Id for navbar.
4328
4329  * 
4330  * @constructor
4331  * Create a new nav group
4332  * @param {Object} config The config object
4333  */
4334
4335 Roo.bootstrap.NavGroup = function(config){
4336     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4337     this.navItems = [];
4338    
4339     Roo.bootstrap.NavGroup.register(this);
4340      this.addEvents({
4341         /**
4342              * @event changed
4343              * Fires when the active item changes
4344              * @param {Roo.bootstrap.NavGroup} this
4345              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4346              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4347          */
4348         'changed': true
4349      });
4350     
4351 };
4352
4353 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4354     
4355     align: '',
4356     inverse: false,
4357     form: false,
4358     type: 'nav',
4359     navId : '',
4360     // private
4361     
4362     navItems : false, 
4363     
4364     getAutoCreate : function()
4365     {
4366         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4367         
4368         cfg = {
4369             tag : 'ul',
4370             cls: 'nav' 
4371         };
4372         if (Roo.bootstrap.version == 4) {
4373             if (['tabs','pills'].indexOf(this.type) != -1) {
4374                 cfg.cls += ' nav-' + this.type; 
4375             } else {
4376                 cfg.cls += ' navbar-nav';
4377             }
4378         } else {
4379             if (['tabs','pills'].indexOf(this.type) != -1) {
4380                 cfg.cls += ' nav-' + this.type
4381             } else {
4382                 if (this.type !== 'nav') {
4383                     Roo.log('nav type must be nav/tabs/pills')
4384                 }
4385                 cfg.cls += ' navbar-nav'
4386             }
4387         }
4388         
4389         if (this.parent() && this.parent().sidebar) {
4390             cfg = {
4391                 tag: 'ul',
4392                 cls: 'dashboard-menu sidebar-menu'
4393             };
4394             
4395             return cfg;
4396         }
4397         
4398         if (this.form === true) {
4399             cfg = {
4400                 tag: 'form',
4401                 cls: 'navbar-form form-inline'
4402             };
4403             
4404             if (this.align === 'right') {
4405                 cfg.cls += ' navbar-right ml-md-auto';
4406             } else {
4407                 cfg.cls += ' navbar-left';
4408             }
4409         }
4410         
4411         if (this.align === 'right') {
4412             cfg.cls += ' navbar-right ml-md-auto';
4413         } else {
4414             cfg.cls += ' mr-auto';
4415         }
4416         
4417         if (this.inverse) {
4418             cfg.cls += ' navbar-inverse';
4419             
4420         }
4421         
4422         
4423         return cfg;
4424     },
4425     /**
4426     * sets the active Navigation item
4427     * @param {Roo.bootstrap.NavItem} the new current navitem
4428     */
4429     setActiveItem : function(item)
4430     {
4431         var prev = false;
4432         Roo.each(this.navItems, function(v){
4433             if (v == item) {
4434                 return ;
4435             }
4436             if (v.isActive()) {
4437                 v.setActive(false, true);
4438                 prev = v;
4439                 
4440             }
4441             
4442         });
4443
4444         item.setActive(true, true);
4445         this.fireEvent('changed', this, item, prev);
4446         
4447         
4448     },
4449     /**
4450     * gets the active Navigation item
4451     * @return {Roo.bootstrap.NavItem} the current navitem
4452     */
4453     getActive : function()
4454     {
4455         
4456         var prev = false;
4457         Roo.each(this.navItems, function(v){
4458             
4459             if (v.isActive()) {
4460                 prev = v;
4461                 
4462             }
4463             
4464         });
4465         return prev;
4466     },
4467     
4468     indexOfNav : function()
4469     {
4470         
4471         var prev = false;
4472         Roo.each(this.navItems, function(v,i){
4473             
4474             if (v.isActive()) {
4475                 prev = i;
4476                 
4477             }
4478             
4479         });
4480         return prev;
4481     },
4482     /**
4483     * adds a Navigation item
4484     * @param {Roo.bootstrap.NavItem} the navitem to add
4485     */
4486     addItem : function(cfg)
4487     {
4488         if (this.form && Roo.bootstrap.version == 4) {
4489             cfg.tag = 'div';
4490         }
4491         var cn = new Roo.bootstrap.NavItem(cfg);
4492         this.register(cn);
4493         cn.parentId = this.id;
4494         cn.onRender(this.el, null);
4495         return cn;
4496     },
4497     /**
4498     * register a Navigation item
4499     * @param {Roo.bootstrap.NavItem} the navitem to add
4500     */
4501     register : function(item)
4502     {
4503         this.navItems.push( item);
4504         item.navId = this.navId;
4505     
4506     },
4507     
4508     /**
4509     * clear all the Navigation item
4510     */
4511    
4512     clearAll : function()
4513     {
4514         this.navItems = [];
4515         this.el.dom.innerHTML = '';
4516     },
4517     
4518     getNavItem: function(tabId)
4519     {
4520         var ret = false;
4521         Roo.each(this.navItems, function(e) {
4522             if (e.tabId == tabId) {
4523                ret =  e;
4524                return false;
4525             }
4526             return true;
4527             
4528         });
4529         return ret;
4530     },
4531     
4532     setActiveNext : function()
4533     {
4534         var i = this.indexOfNav(this.getActive());
4535         if (i > this.navItems.length) {
4536             return;
4537         }
4538         this.setActiveItem(this.navItems[i+1]);
4539     },
4540     setActivePrev : function()
4541     {
4542         var i = this.indexOfNav(this.getActive());
4543         if (i  < 1) {
4544             return;
4545         }
4546         this.setActiveItem(this.navItems[i-1]);
4547     },
4548     clearWasActive : function(except) {
4549         Roo.each(this.navItems, function(e) {
4550             if (e.tabId != except.tabId && e.was_active) {
4551                e.was_active = false;
4552                return false;
4553             }
4554             return true;
4555             
4556         });
4557     },
4558     getWasActive : function ()
4559     {
4560         var r = false;
4561         Roo.each(this.navItems, function(e) {
4562             if (e.was_active) {
4563                r = e;
4564                return false;
4565             }
4566             return true;
4567             
4568         });
4569         return r;
4570     }
4571     
4572     
4573 });
4574
4575  
4576 Roo.apply(Roo.bootstrap.NavGroup, {
4577     
4578     groups: {},
4579      /**
4580     * register a Navigation Group
4581     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4582     */
4583     register : function(navgrp)
4584     {
4585         this.groups[navgrp.navId] = navgrp;
4586         
4587     },
4588     /**
4589     * fetch a Navigation Group based on the navigation ID
4590     * @param {string} the navgroup to add
4591     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4592     */
4593     get: function(navId) {
4594         if (typeof(this.groups[navId]) == 'undefined') {
4595             return false;
4596             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4597         }
4598         return this.groups[navId] ;
4599     }
4600     
4601     
4602     
4603 });
4604
4605  /*
4606  * - LGPL
4607  *
4608  * row
4609  * 
4610  */
4611
4612 /**
4613  * @class Roo.bootstrap.NavItem
4614  * @extends Roo.bootstrap.Component
4615  * Bootstrap Navbar.NavItem class
4616  * @cfg {String} href  link to
4617  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
4618
4619  * @cfg {String} html content of button
4620  * @cfg {String} badge text inside badge
4621  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4622  * @cfg {String} glyphicon DEPRICATED - use fa
4623  * @cfg {String} icon DEPRICATED - use fa
4624  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4625  * @cfg {Boolean} active Is item active
4626  * @cfg {Boolean} disabled Is item disabled
4627  
4628  * @cfg {Boolean} preventDefault (true | false) default false
4629  * @cfg {String} tabId the tab that this item activates.
4630  * @cfg {String} tagtype (a|span) render as a href or span?
4631  * @cfg {Boolean} animateRef (true|false) link to element default false  
4632   
4633  * @constructor
4634  * Create a new Navbar Item
4635  * @param {Object} config The config object
4636  */
4637 Roo.bootstrap.NavItem = function(config){
4638     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4639     this.addEvents({
4640         // raw events
4641         /**
4642          * @event click
4643          * The raw click event for the entire grid.
4644          * @param {Roo.EventObject} e
4645          */
4646         "click" : true,
4647          /**
4648             * @event changed
4649             * Fires when the active item active state changes
4650             * @param {Roo.bootstrap.NavItem} this
4651             * @param {boolean} state the new state
4652              
4653          */
4654         'changed': true,
4655         /**
4656             * @event scrollto
4657             * Fires when scroll to element
4658             * @param {Roo.bootstrap.NavItem} this
4659             * @param {Object} options
4660             * @param {Roo.EventObject} e
4661              
4662          */
4663         'scrollto': true
4664     });
4665    
4666 };
4667
4668 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4669     
4670     href: false,
4671     html: '',
4672     badge: '',
4673     icon: false,
4674     fa : false,
4675     glyphicon: false,
4676     active: false,
4677     preventDefault : false,
4678     tabId : false,
4679     tagtype : 'a',
4680     tag: 'li',
4681     disabled : false,
4682     animateRef : false,
4683     was_active : false,
4684     button_weight : '',
4685     button_outline : false,
4686     
4687     navLink: false,
4688     
4689     getAutoCreate : function(){
4690          
4691         var cfg = {
4692             tag: this.tag,
4693             cls: 'nav-item'
4694         };
4695         
4696         if (this.active) {
4697             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4698         }
4699         if (this.disabled) {
4700             cfg.cls += ' disabled';
4701         }
4702         
4703         // BS4 only?
4704         if (this.button_weight.length) {
4705             cfg.tag = this.href ? 'a' : 'button';
4706             cfg.html = this.html || '';
4707             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
4708             if (this.href) {
4709                 cfg.href = this.href;
4710             }
4711             if (this.fa) {
4712                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
4713             }
4714             
4715             // menu .. should add dropdown-menu class - so no need for carat..
4716             
4717             if (this.badge !== '') {
4718                  
4719                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4720             }
4721             return cfg;
4722         }
4723         
4724         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4725             cfg.cn = [
4726                 {
4727                     tag: this.tagtype,
4728                     href : this.href || "#",
4729                     html: this.html || ''
4730                 }
4731             ];
4732             if (this.tagtype == 'a') {
4733                 cfg.cn[0].cls = 'nav-link';
4734             }
4735             if (this.icon) {
4736                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
4737             }
4738             if (this.fa) {
4739                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
4740             }
4741             if(this.glyphicon) {
4742                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4743             }
4744             
4745             if (this.menu) {
4746                 
4747                 cfg.cn[0].html += " <span class='caret'></span>";
4748              
4749             }
4750             
4751             if (this.badge !== '') {
4752                  
4753                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4754             }
4755         }
4756         
4757         
4758         
4759         return cfg;
4760     },
4761     onRender : function(ct, position)
4762     {
4763        // Roo.log("Call onRender: " + this.xtype);
4764         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
4765             this.tag = 'div';
4766         }
4767         
4768         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
4769         this.navLink = this.el.select('.nav-link',true).first();
4770         return ret;
4771     },
4772       
4773     
4774     initEvents: function() 
4775     {
4776         if (typeof (this.menu) != 'undefined') {
4777             this.menu.parentType = this.xtype;
4778             this.menu.triggerEl = this.el;
4779             this.menu = this.addxtype(Roo.apply({}, this.menu));
4780         }
4781         
4782         this.el.select('a',true).on('click', this.onClick, this);
4783         
4784         if(this.tagtype == 'span'){
4785             this.el.select('span',true).on('click', this.onClick, this);
4786         }
4787        
4788         // at this point parent should be available..
4789         this.parent().register(this);
4790     },
4791     
4792     onClick : function(e)
4793     {
4794         if (e.getTarget('.dropdown-menu-item')) {
4795             // did you click on a menu itemm.... - then don't trigger onclick..
4796             return;
4797         }
4798         
4799         if(
4800                 this.preventDefault || 
4801                 this.href == '#' 
4802         ){
4803             Roo.log("NavItem - prevent Default?");
4804             e.preventDefault();
4805         }
4806         
4807         if (this.disabled) {
4808             return;
4809         }
4810         
4811         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4812         if (tg && tg.transition) {
4813             Roo.log("waiting for the transitionend");
4814             return;
4815         }
4816         
4817         
4818         
4819         //Roo.log("fire event clicked");
4820         if(this.fireEvent('click', this, e) === false){
4821             return;
4822         };
4823         
4824         if(this.tagtype == 'span'){
4825             return;
4826         }
4827         
4828         //Roo.log(this.href);
4829         var ael = this.el.select('a',true).first();
4830         //Roo.log(ael);
4831         
4832         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4833             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4834             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4835                 return; // ignore... - it's a 'hash' to another page.
4836             }
4837             Roo.log("NavItem - prevent Default?");
4838             e.preventDefault();
4839             this.scrollToElement(e);
4840         }
4841         
4842         
4843         var p =  this.parent();
4844    
4845         if (['tabs','pills'].indexOf(p.type)!==-1) {
4846             if (typeof(p.setActiveItem) !== 'undefined') {
4847                 p.setActiveItem(this);
4848             }
4849         }
4850         
4851         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4852         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4853             // remove the collapsed menu expand...
4854             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4855         }
4856     },
4857     
4858     isActive: function () {
4859         return this.active
4860     },
4861     setActive : function(state, fire, is_was_active)
4862     {
4863         if (this.active && !state && this.navId) {
4864             this.was_active = true;
4865             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4866             if (nv) {
4867                 nv.clearWasActive(this);
4868             }
4869             
4870         }
4871         this.active = state;
4872         
4873         if (!state ) {
4874             this.el.removeClass('active');
4875             this.navLink ? this.navLink.removeClass('active') : false;
4876         } else if (!this.el.hasClass('active')) {
4877             
4878             this.el.addClass('active');
4879             if (Roo.bootstrap.version == 4 && this.navLink ) {
4880                 this.navLink.addClass('active');
4881             }
4882             
4883         }
4884         if (fire) {
4885             this.fireEvent('changed', this, state);
4886         }
4887         
4888         // show a panel if it's registered and related..
4889         
4890         if (!this.navId || !this.tabId || !state || is_was_active) {
4891             return;
4892         }
4893         
4894         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4895         if (!tg) {
4896             return;
4897         }
4898         var pan = tg.getPanelByName(this.tabId);
4899         if (!pan) {
4900             return;
4901         }
4902         // if we can not flip to new panel - go back to old nav highlight..
4903         if (false == tg.showPanel(pan)) {
4904             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4905             if (nv) {
4906                 var onav = nv.getWasActive();
4907                 if (onav) {
4908                     onav.setActive(true, false, true);
4909                 }
4910             }
4911             
4912         }
4913         
4914         
4915         
4916     },
4917      // this should not be here...
4918     setDisabled : function(state)
4919     {
4920         this.disabled = state;
4921         if (!state ) {
4922             this.el.removeClass('disabled');
4923         } else if (!this.el.hasClass('disabled')) {
4924             this.el.addClass('disabled');
4925         }
4926         
4927     },
4928     
4929     /**
4930      * Fetch the element to display the tooltip on.
4931      * @return {Roo.Element} defaults to this.el
4932      */
4933     tooltipEl : function()
4934     {
4935         return this.el.select('' + this.tagtype + '', true).first();
4936     },
4937     
4938     scrollToElement : function(e)
4939     {
4940         var c = document.body;
4941         
4942         /*
4943          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4944          */
4945         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4946             c = document.documentElement;
4947         }
4948         
4949         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4950         
4951         if(!target){
4952             return;
4953         }
4954
4955         var o = target.calcOffsetsTo(c);
4956         
4957         var options = {
4958             target : target,
4959             value : o[1]
4960         };
4961         
4962         this.fireEvent('scrollto', this, options, e);
4963         
4964         Roo.get(c).scrollTo('top', options.value, true);
4965         
4966         return;
4967     }
4968 });
4969  
4970
4971  /*
4972  * - LGPL
4973  *
4974  * sidebar item
4975  *
4976  *  li
4977  *    <span> icon </span>
4978  *    <span> text </span>
4979  *    <span>badge </span>
4980  */
4981
4982 /**
4983  * @class Roo.bootstrap.NavSidebarItem
4984  * @extends Roo.bootstrap.NavItem
4985  * Bootstrap Navbar.NavSidebarItem class
4986  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4987  * {Boolean} open is the menu open
4988  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4989  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4990  * {String} buttonSize (sm|md|lg)the extra classes for the button
4991  * {Boolean} showArrow show arrow next to the text (default true)
4992  * @constructor
4993  * Create a new Navbar Button
4994  * @param {Object} config The config object
4995  */
4996 Roo.bootstrap.NavSidebarItem = function(config){
4997     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4998     this.addEvents({
4999         // raw events
5000         /**
5001          * @event click
5002          * The raw click event for the entire grid.
5003          * @param {Roo.EventObject} e
5004          */
5005         "click" : true,
5006          /**
5007             * @event changed
5008             * Fires when the active item active state changes
5009             * @param {Roo.bootstrap.NavSidebarItem} this
5010             * @param {boolean} state the new state
5011              
5012          */
5013         'changed': true
5014     });
5015    
5016 };
5017
5018 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
5019     
5020     badgeWeight : 'default',
5021     
5022     open: false,
5023     
5024     buttonView : false,
5025     
5026     buttonWeight : 'default',
5027     
5028     buttonSize : 'md',
5029     
5030     showArrow : true,
5031     
5032     getAutoCreate : function(){
5033         
5034         
5035         var a = {
5036                 tag: 'a',
5037                 href : this.href || '#',
5038                 cls: '',
5039                 html : '',
5040                 cn : []
5041         };
5042         
5043         if(this.buttonView){
5044             a = {
5045                 tag: 'button',
5046                 href : this.href || '#',
5047                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5048                 html : this.html,
5049                 cn : []
5050             };
5051         }
5052         
5053         var cfg = {
5054             tag: 'li',
5055             cls: '',
5056             cn: [ a ]
5057         };
5058         
5059         if (this.active) {
5060             cfg.cls += ' active';
5061         }
5062         
5063         if (this.disabled) {
5064             cfg.cls += ' disabled';
5065         }
5066         if (this.open) {
5067             cfg.cls += ' open x-open';
5068         }
5069         // left icon..
5070         if (this.glyphicon || this.icon) {
5071             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5072             a.cn.push({ tag : 'i', cls : c }) ;
5073         }
5074         
5075         if(!this.buttonView){
5076             var span = {
5077                 tag: 'span',
5078                 html : this.html || ''
5079             };
5080
5081             a.cn.push(span);
5082             
5083         }
5084         
5085         if (this.badge !== '') {
5086             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5087         }
5088         
5089         if (this.menu) {
5090             
5091             if(this.showArrow){
5092                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5093             }
5094             
5095             a.cls += ' dropdown-toggle treeview' ;
5096         }
5097         
5098         return cfg;
5099     },
5100     
5101     initEvents : function()
5102     { 
5103         if (typeof (this.menu) != 'undefined') {
5104             this.menu.parentType = this.xtype;
5105             this.menu.triggerEl = this.el;
5106             this.menu = this.addxtype(Roo.apply({}, this.menu));
5107         }
5108         
5109         this.el.on('click', this.onClick, this);
5110         
5111         if(this.badge !== ''){
5112             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5113         }
5114         
5115     },
5116     
5117     onClick : function(e)
5118     {
5119         if(this.disabled){
5120             e.preventDefault();
5121             return;
5122         }
5123         
5124         if(this.preventDefault){
5125             e.preventDefault();
5126         }
5127         
5128         this.fireEvent('click', this);
5129     },
5130     
5131     disable : function()
5132     {
5133         this.setDisabled(true);
5134     },
5135     
5136     enable : function()
5137     {
5138         this.setDisabled(false);
5139     },
5140     
5141     setDisabled : function(state)
5142     {
5143         if(this.disabled == state){
5144             return;
5145         }
5146         
5147         this.disabled = state;
5148         
5149         if (state) {
5150             this.el.addClass('disabled');
5151             return;
5152         }
5153         
5154         this.el.removeClass('disabled');
5155         
5156         return;
5157     },
5158     
5159     setActive : function(state)
5160     {
5161         if(this.active == state){
5162             return;
5163         }
5164         
5165         this.active = state;
5166         
5167         if (state) {
5168             this.el.addClass('active');
5169             return;
5170         }
5171         
5172         this.el.removeClass('active');
5173         
5174         return;
5175     },
5176     
5177     isActive: function () 
5178     {
5179         return this.active;
5180     },
5181     
5182     setBadge : function(str)
5183     {
5184         if(!this.badgeEl){
5185             return;
5186         }
5187         
5188         this.badgeEl.dom.innerHTML = str;
5189     }
5190     
5191    
5192      
5193  
5194 });
5195  
5196
5197  /*
5198  * - LGPL
5199  *
5200  * row
5201  * 
5202  */
5203
5204 /**
5205  * @class Roo.bootstrap.Row
5206  * @extends Roo.bootstrap.Component
5207  * Bootstrap Row class (contains columns...)
5208  * 
5209  * @constructor
5210  * Create a new Row
5211  * @param {Object} config The config object
5212  */
5213
5214 Roo.bootstrap.Row = function(config){
5215     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5216 };
5217
5218 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5219     
5220     getAutoCreate : function(){
5221        return {
5222             cls: 'row clearfix'
5223        };
5224     }
5225     
5226     
5227 });
5228
5229  
5230
5231  /*
5232  * - LGPL
5233  *
5234  * element
5235  * 
5236  */
5237
5238 /**
5239  * @class Roo.bootstrap.Element
5240  * @extends Roo.bootstrap.Component
5241  * Bootstrap Element class
5242  * @cfg {String} html contents of the element
5243  * @cfg {String} tag tag of the element
5244  * @cfg {String} cls class of the element
5245  * @cfg {Boolean} preventDefault (true|false) default false
5246  * @cfg {Boolean} clickable (true|false) default false
5247  * 
5248  * @constructor
5249  * Create a new Element
5250  * @param {Object} config The config object
5251  */
5252
5253 Roo.bootstrap.Element = function(config){
5254     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5255     
5256     this.addEvents({
5257         // raw events
5258         /**
5259          * @event click
5260          * When a element is chick
5261          * @param {Roo.bootstrap.Element} this
5262          * @param {Roo.EventObject} e
5263          */
5264         "click" : true
5265     });
5266 };
5267
5268 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5269     
5270     tag: 'div',
5271     cls: '',
5272     html: '',
5273     preventDefault: false, 
5274     clickable: false,
5275     
5276     getAutoCreate : function(){
5277         
5278         var cfg = {
5279             tag: this.tag,
5280             // cls: this.cls, double assign in parent class Component.js :: onRender
5281             html: this.html
5282         };
5283         
5284         return cfg;
5285     },
5286     
5287     initEvents: function() 
5288     {
5289         Roo.bootstrap.Element.superclass.initEvents.call(this);
5290         
5291         if(this.clickable){
5292             this.el.on('click', this.onClick, this);
5293         }
5294         
5295     },
5296     
5297     onClick : function(e)
5298     {
5299         if(this.preventDefault){
5300             e.preventDefault();
5301         }
5302         
5303         this.fireEvent('click', this, e);
5304     },
5305     
5306     getValue : function()
5307     {
5308         return this.el.dom.innerHTML;
5309     },
5310     
5311     setValue : function(value)
5312     {
5313         this.el.dom.innerHTML = value;
5314     }
5315    
5316 });
5317
5318  
5319
5320  /*
5321  * - LGPL
5322  *
5323  * pagination
5324  * 
5325  */
5326
5327 /**
5328  * @class Roo.bootstrap.Pagination
5329  * @extends Roo.bootstrap.Component
5330  * Bootstrap Pagination class
5331  * @cfg {String} size xs | sm | md | lg
5332  * @cfg {Boolean} inverse false | true
5333  * 
5334  * @constructor
5335  * Create a new Pagination
5336  * @param {Object} config The config object
5337  */
5338
5339 Roo.bootstrap.Pagination = function(config){
5340     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5341 };
5342
5343 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5344     
5345     cls: false,
5346     size: false,
5347     inverse: false,
5348     
5349     getAutoCreate : function(){
5350         var cfg = {
5351             tag: 'ul',
5352                 cls: 'pagination'
5353         };
5354         if (this.inverse) {
5355             cfg.cls += ' inverse';
5356         }
5357         if (this.html) {
5358             cfg.html=this.html;
5359         }
5360         if (this.cls) {
5361             cfg.cls += " " + this.cls;
5362         }
5363         return cfg;
5364     }
5365    
5366 });
5367
5368  
5369
5370  /*
5371  * - LGPL
5372  *
5373  * Pagination item
5374  * 
5375  */
5376
5377
5378 /**
5379  * @class Roo.bootstrap.PaginationItem
5380  * @extends Roo.bootstrap.Component
5381  * Bootstrap PaginationItem class
5382  * @cfg {String} html text
5383  * @cfg {String} href the link
5384  * @cfg {Boolean} preventDefault (true | false) default true
5385  * @cfg {Boolean} active (true | false) default false
5386  * @cfg {Boolean} disabled default false
5387  * 
5388  * 
5389  * @constructor
5390  * Create a new PaginationItem
5391  * @param {Object} config The config object
5392  */
5393
5394
5395 Roo.bootstrap.PaginationItem = function(config){
5396     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5397     this.addEvents({
5398         // raw events
5399         /**
5400          * @event click
5401          * The raw click event for the entire grid.
5402          * @param {Roo.EventObject} e
5403          */
5404         "click" : true
5405     });
5406 };
5407
5408 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5409     
5410     href : false,
5411     html : false,
5412     preventDefault: true,
5413     active : false,
5414     cls : false,
5415     disabled: false,
5416     
5417     getAutoCreate : function(){
5418         var cfg= {
5419             tag: 'li',
5420             cn: [
5421                 {
5422                     tag : 'a',
5423                     href : this.href ? this.href : '#',
5424                     html : this.html ? this.html : ''
5425                 }
5426             ]
5427         };
5428         
5429         if(this.cls){
5430             cfg.cls = this.cls;
5431         }
5432         
5433         if(this.disabled){
5434             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5435         }
5436         
5437         if(this.active){
5438             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5439         }
5440         
5441         return cfg;
5442     },
5443     
5444     initEvents: function() {
5445         
5446         this.el.on('click', this.onClick, this);
5447         
5448     },
5449     onClick : function(e)
5450     {
5451         Roo.log('PaginationItem on click ');
5452         if(this.preventDefault){
5453             e.preventDefault();
5454         }
5455         
5456         if(this.disabled){
5457             return;
5458         }
5459         
5460         this.fireEvent('click', this, e);
5461     }
5462    
5463 });
5464
5465  
5466
5467  /*
5468  * - LGPL
5469  *
5470  * slider
5471  * 
5472  */
5473
5474
5475 /**
5476  * @class Roo.bootstrap.Slider
5477  * @extends Roo.bootstrap.Component
5478  * Bootstrap Slider class
5479  *    
5480  * @constructor
5481  * Create a new Slider
5482  * @param {Object} config The config object
5483  */
5484
5485 Roo.bootstrap.Slider = function(config){
5486     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5487 };
5488
5489 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5490     
5491     getAutoCreate : function(){
5492         
5493         var cfg = {
5494             tag: 'div',
5495             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5496             cn: [
5497                 {
5498                     tag: 'a',
5499                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5500                 }
5501             ]
5502         };
5503         
5504         return cfg;
5505     }
5506    
5507 });
5508
5509  /*
5510  * Based on:
5511  * Ext JS Library 1.1.1
5512  * Copyright(c) 2006-2007, Ext JS, LLC.
5513  *
5514  * Originally Released Under LGPL - original licence link has changed is not relivant.
5515  *
5516  * Fork - LGPL
5517  * <script type="text/javascript">
5518  */
5519  
5520
5521 /**
5522  * @class Roo.grid.ColumnModel
5523  * @extends Roo.util.Observable
5524  * This is the default implementation of a ColumnModel used by the Grid. It defines
5525  * the columns in the grid.
5526  * <br>Usage:<br>
5527  <pre><code>
5528  var colModel = new Roo.grid.ColumnModel([
5529         {header: "Ticker", width: 60, sortable: true, locked: true},
5530         {header: "Company Name", width: 150, sortable: true},
5531         {header: "Market Cap.", width: 100, sortable: true},
5532         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5533         {header: "Employees", width: 100, sortable: true, resizable: false}
5534  ]);
5535  </code></pre>
5536  * <p>
5537  
5538  * The config options listed for this class are options which may appear in each
5539  * individual column definition.
5540  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5541  * @constructor
5542  * @param {Object} config An Array of column config objects. See this class's
5543  * config objects for details.
5544 */
5545 Roo.grid.ColumnModel = function(config){
5546         /**
5547      * The config passed into the constructor
5548      */
5549     this.config = config;
5550     this.lookup = {};
5551
5552     // if no id, create one
5553     // if the column does not have a dataIndex mapping,
5554     // map it to the order it is in the config
5555     for(var i = 0, len = config.length; i < len; i++){
5556         var c = config[i];
5557         if(typeof c.dataIndex == "undefined"){
5558             c.dataIndex = i;
5559         }
5560         if(typeof c.renderer == "string"){
5561             c.renderer = Roo.util.Format[c.renderer];
5562         }
5563         if(typeof c.id == "undefined"){
5564             c.id = Roo.id();
5565         }
5566         if(c.editor && c.editor.xtype){
5567             c.editor  = Roo.factory(c.editor, Roo.grid);
5568         }
5569         if(c.editor && c.editor.isFormField){
5570             c.editor = new Roo.grid.GridEditor(c.editor);
5571         }
5572         this.lookup[c.id] = c;
5573     }
5574
5575     /**
5576      * The width of columns which have no width specified (defaults to 100)
5577      * @type Number
5578      */
5579     this.defaultWidth = 100;
5580
5581     /**
5582      * Default sortable of columns which have no sortable specified (defaults to false)
5583      * @type Boolean
5584      */
5585     this.defaultSortable = false;
5586
5587     this.addEvents({
5588         /**
5589              * @event widthchange
5590              * Fires when the width of a column changes.
5591              * @param {ColumnModel} this
5592              * @param {Number} columnIndex The column index
5593              * @param {Number} newWidth The new width
5594              */
5595             "widthchange": true,
5596         /**
5597              * @event headerchange
5598              * Fires when the text of a header changes.
5599              * @param {ColumnModel} this
5600              * @param {Number} columnIndex The column index
5601              * @param {Number} newText The new header text
5602              */
5603             "headerchange": true,
5604         /**
5605              * @event hiddenchange
5606              * Fires when a column is hidden or "unhidden".
5607              * @param {ColumnModel} this
5608              * @param {Number} columnIndex The column index
5609              * @param {Boolean} hidden true if hidden, false otherwise
5610              */
5611             "hiddenchange": true,
5612             /**
5613          * @event columnmoved
5614          * Fires when a column is moved.
5615          * @param {ColumnModel} this
5616          * @param {Number} oldIndex
5617          * @param {Number} newIndex
5618          */
5619         "columnmoved" : true,
5620         /**
5621          * @event columlockchange
5622          * Fires when a column's locked state is changed
5623          * @param {ColumnModel} this
5624          * @param {Number} colIndex
5625          * @param {Boolean} locked true if locked
5626          */
5627         "columnlockchange" : true
5628     });
5629     Roo.grid.ColumnModel.superclass.constructor.call(this);
5630 };
5631 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5632     /**
5633      * @cfg {String} header The header text to display in the Grid view.
5634      */
5635     /**
5636      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5637      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5638      * specified, the column's index is used as an index into the Record's data Array.
5639      */
5640     /**
5641      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5642      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5643      */
5644     /**
5645      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5646      * Defaults to the value of the {@link #defaultSortable} property.
5647      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5648      */
5649     /**
5650      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5651      */
5652     /**
5653      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5654      */
5655     /**
5656      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5657      */
5658     /**
5659      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5660      */
5661     /**
5662      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5663      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5664      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5665      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5666      */
5667        /**
5668      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5669      */
5670     /**
5671      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5672      */
5673     /**
5674      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5675      */
5676     /**
5677      * @cfg {String} cursor (Optional)
5678      */
5679     /**
5680      * @cfg {String} tooltip (Optional)
5681      */
5682     /**
5683      * @cfg {Number} xs (Optional)
5684      */
5685     /**
5686      * @cfg {Number} sm (Optional)
5687      */
5688     /**
5689      * @cfg {Number} md (Optional)
5690      */
5691     /**
5692      * @cfg {Number} lg (Optional)
5693      */
5694     /**
5695      * Returns the id of the column at the specified index.
5696      * @param {Number} index The column index
5697      * @return {String} the id
5698      */
5699     getColumnId : function(index){
5700         return this.config[index].id;
5701     },
5702
5703     /**
5704      * Returns the column for a specified id.
5705      * @param {String} id The column id
5706      * @return {Object} the column
5707      */
5708     getColumnById : function(id){
5709         return this.lookup[id];
5710     },
5711
5712     
5713     /**
5714      * Returns the column for a specified dataIndex.
5715      * @param {String} dataIndex The column dataIndex
5716      * @return {Object|Boolean} the column or false if not found
5717      */
5718     getColumnByDataIndex: function(dataIndex){
5719         var index = this.findColumnIndex(dataIndex);
5720         return index > -1 ? this.config[index] : false;
5721     },
5722     
5723     /**
5724      * Returns the index for a specified column id.
5725      * @param {String} id The column id
5726      * @return {Number} the index, or -1 if not found
5727      */
5728     getIndexById : function(id){
5729         for(var i = 0, len = this.config.length; i < len; i++){
5730             if(this.config[i].id == id){
5731                 return i;
5732             }
5733         }
5734         return -1;
5735     },
5736     
5737     /**
5738      * Returns the index for a specified column dataIndex.
5739      * @param {String} dataIndex The column dataIndex
5740      * @return {Number} the index, or -1 if not found
5741      */
5742     
5743     findColumnIndex : function(dataIndex){
5744         for(var i = 0, len = this.config.length; i < len; i++){
5745             if(this.config[i].dataIndex == dataIndex){
5746                 return i;
5747             }
5748         }
5749         return -1;
5750     },
5751     
5752     
5753     moveColumn : function(oldIndex, newIndex){
5754         var c = this.config[oldIndex];
5755         this.config.splice(oldIndex, 1);
5756         this.config.splice(newIndex, 0, c);
5757         this.dataMap = null;
5758         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5759     },
5760
5761     isLocked : function(colIndex){
5762         return this.config[colIndex].locked === true;
5763     },
5764
5765     setLocked : function(colIndex, value, suppressEvent){
5766         if(this.isLocked(colIndex) == value){
5767             return;
5768         }
5769         this.config[colIndex].locked = value;
5770         if(!suppressEvent){
5771             this.fireEvent("columnlockchange", this, colIndex, value);
5772         }
5773     },
5774
5775     getTotalLockedWidth : function(){
5776         var totalWidth = 0;
5777         for(var i = 0; i < this.config.length; i++){
5778             if(this.isLocked(i) && !this.isHidden(i)){
5779                 this.totalWidth += this.getColumnWidth(i);
5780             }
5781         }
5782         return totalWidth;
5783     },
5784
5785     getLockedCount : function(){
5786         for(var i = 0, len = this.config.length; i < len; i++){
5787             if(!this.isLocked(i)){
5788                 return i;
5789             }
5790         }
5791         
5792         return this.config.length;
5793     },
5794
5795     /**
5796      * Returns the number of columns.
5797      * @return {Number}
5798      */
5799     getColumnCount : function(visibleOnly){
5800         if(visibleOnly === true){
5801             var c = 0;
5802             for(var i = 0, len = this.config.length; i < len; i++){
5803                 if(!this.isHidden(i)){
5804                     c++;
5805                 }
5806             }
5807             return c;
5808         }
5809         return this.config.length;
5810     },
5811
5812     /**
5813      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5814      * @param {Function} fn
5815      * @param {Object} scope (optional)
5816      * @return {Array} result
5817      */
5818     getColumnsBy : function(fn, scope){
5819         var r = [];
5820         for(var i = 0, len = this.config.length; i < len; i++){
5821             var c = this.config[i];
5822             if(fn.call(scope||this, c, i) === true){
5823                 r[r.length] = c;
5824             }
5825         }
5826         return r;
5827     },
5828
5829     /**
5830      * Returns true if the specified column is sortable.
5831      * @param {Number} col The column index
5832      * @return {Boolean}
5833      */
5834     isSortable : function(col){
5835         if(typeof this.config[col].sortable == "undefined"){
5836             return this.defaultSortable;
5837         }
5838         return this.config[col].sortable;
5839     },
5840
5841     /**
5842      * Returns the rendering (formatting) function defined for the column.
5843      * @param {Number} col The column index.
5844      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5845      */
5846     getRenderer : function(col){
5847         if(!this.config[col].renderer){
5848             return Roo.grid.ColumnModel.defaultRenderer;
5849         }
5850         return this.config[col].renderer;
5851     },
5852
5853     /**
5854      * Sets the rendering (formatting) function for a column.
5855      * @param {Number} col The column index
5856      * @param {Function} fn The function to use to process the cell's raw data
5857      * to return HTML markup for the grid view. The render function is called with
5858      * the following parameters:<ul>
5859      * <li>Data value.</li>
5860      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5861      * <li>css A CSS style string to apply to the table cell.</li>
5862      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5863      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5864      * <li>Row index</li>
5865      * <li>Column index</li>
5866      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5867      */
5868     setRenderer : function(col, fn){
5869         this.config[col].renderer = fn;
5870     },
5871
5872     /**
5873      * Returns the width for the specified column.
5874      * @param {Number} col The column index
5875      * @return {Number}
5876      */
5877     getColumnWidth : function(col){
5878         return this.config[col].width * 1 || this.defaultWidth;
5879     },
5880
5881     /**
5882      * Sets the width for a column.
5883      * @param {Number} col The column index
5884      * @param {Number} width The new width
5885      */
5886     setColumnWidth : function(col, width, suppressEvent){
5887         this.config[col].width = width;
5888         this.totalWidth = null;
5889         if(!suppressEvent){
5890              this.fireEvent("widthchange", this, col, width);
5891         }
5892     },
5893
5894     /**
5895      * Returns the total width of all columns.
5896      * @param {Boolean} includeHidden True to include hidden column widths
5897      * @return {Number}
5898      */
5899     getTotalWidth : function(includeHidden){
5900         if(!this.totalWidth){
5901             this.totalWidth = 0;
5902             for(var i = 0, len = this.config.length; i < len; i++){
5903                 if(includeHidden || !this.isHidden(i)){
5904                     this.totalWidth += this.getColumnWidth(i);
5905                 }
5906             }
5907         }
5908         return this.totalWidth;
5909     },
5910
5911     /**
5912      * Returns the header for the specified column.
5913      * @param {Number} col The column index
5914      * @return {String}
5915      */
5916     getColumnHeader : function(col){
5917         return this.config[col].header;
5918     },
5919
5920     /**
5921      * Sets the header for a column.
5922      * @param {Number} col The column index
5923      * @param {String} header The new header
5924      */
5925     setColumnHeader : function(col, header){
5926         this.config[col].header = header;
5927         this.fireEvent("headerchange", this, col, header);
5928     },
5929
5930     /**
5931      * Returns the tooltip for the specified column.
5932      * @param {Number} col The column index
5933      * @return {String}
5934      */
5935     getColumnTooltip : function(col){
5936             return this.config[col].tooltip;
5937     },
5938     /**
5939      * Sets the tooltip for a column.
5940      * @param {Number} col The column index
5941      * @param {String} tooltip The new tooltip
5942      */
5943     setColumnTooltip : function(col, tooltip){
5944             this.config[col].tooltip = tooltip;
5945     },
5946
5947     /**
5948      * Returns the dataIndex for the specified column.
5949      * @param {Number} col The column index
5950      * @return {Number}
5951      */
5952     getDataIndex : function(col){
5953         return this.config[col].dataIndex;
5954     },
5955
5956     /**
5957      * Sets the dataIndex for a column.
5958      * @param {Number} col The column index
5959      * @param {Number} dataIndex The new dataIndex
5960      */
5961     setDataIndex : function(col, dataIndex){
5962         this.config[col].dataIndex = dataIndex;
5963     },
5964
5965     
5966     
5967     /**
5968      * Returns true if the cell is editable.
5969      * @param {Number} colIndex The column index
5970      * @param {Number} rowIndex The row index - this is nto actually used..?
5971      * @return {Boolean}
5972      */
5973     isCellEditable : function(colIndex, rowIndex){
5974         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5975     },
5976
5977     /**
5978      * Returns the editor defined for the cell/column.
5979      * return false or null to disable editing.
5980      * @param {Number} colIndex The column index
5981      * @param {Number} rowIndex The row index
5982      * @return {Object}
5983      */
5984     getCellEditor : function(colIndex, rowIndex){
5985         return this.config[colIndex].editor;
5986     },
5987
5988     /**
5989      * Sets if a column is editable.
5990      * @param {Number} col The column index
5991      * @param {Boolean} editable True if the column is editable
5992      */
5993     setEditable : function(col, editable){
5994         this.config[col].editable = editable;
5995     },
5996
5997
5998     /**
5999      * Returns true if the column is hidden.
6000      * @param {Number} colIndex The column index
6001      * @return {Boolean}
6002      */
6003     isHidden : function(colIndex){
6004         return this.config[colIndex].hidden;
6005     },
6006
6007
6008     /**
6009      * Returns true if the column width cannot be changed
6010      */
6011     isFixed : function(colIndex){
6012         return this.config[colIndex].fixed;
6013     },
6014
6015     /**
6016      * Returns true if the column can be resized
6017      * @return {Boolean}
6018      */
6019     isResizable : function(colIndex){
6020         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6021     },
6022     /**
6023      * Sets if a column is hidden.
6024      * @param {Number} colIndex The column index
6025      * @param {Boolean} hidden True if the column is hidden
6026      */
6027     setHidden : function(colIndex, hidden){
6028         this.config[colIndex].hidden = hidden;
6029         this.totalWidth = null;
6030         this.fireEvent("hiddenchange", this, colIndex, hidden);
6031     },
6032
6033     /**
6034      * Sets the editor for a column.
6035      * @param {Number} col The column index
6036      * @param {Object} editor The editor object
6037      */
6038     setEditor : function(col, editor){
6039         this.config[col].editor = editor;
6040     }
6041 });
6042
6043 Roo.grid.ColumnModel.defaultRenderer = function(value)
6044 {
6045     if(typeof value == "object") {
6046         return value;
6047     }
6048         if(typeof value == "string" && value.length < 1){
6049             return "&#160;";
6050         }
6051     
6052         return String.format("{0}", value);
6053 };
6054
6055 // Alias for backwards compatibility
6056 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6057 /*
6058  * Based on:
6059  * Ext JS Library 1.1.1
6060  * Copyright(c) 2006-2007, Ext JS, LLC.
6061  *
6062  * Originally Released Under LGPL - original licence link has changed is not relivant.
6063  *
6064  * Fork - LGPL
6065  * <script type="text/javascript">
6066  */
6067  
6068 /**
6069  * @class Roo.LoadMask
6070  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6071  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6072  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6073  * element's UpdateManager load indicator and will be destroyed after the initial load.
6074  * @constructor
6075  * Create a new LoadMask
6076  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6077  * @param {Object} config The config object
6078  */
6079 Roo.LoadMask = function(el, config){
6080     this.el = Roo.get(el);
6081     Roo.apply(this, config);
6082     if(this.store){
6083         this.store.on('beforeload', this.onBeforeLoad, this);
6084         this.store.on('load', this.onLoad, this);
6085         this.store.on('loadexception', this.onLoadException, this);
6086         this.removeMask = false;
6087     }else{
6088         var um = this.el.getUpdateManager();
6089         um.showLoadIndicator = false; // disable the default indicator
6090         um.on('beforeupdate', this.onBeforeLoad, this);
6091         um.on('update', this.onLoad, this);
6092         um.on('failure', this.onLoad, this);
6093         this.removeMask = true;
6094     }
6095 };
6096
6097 Roo.LoadMask.prototype = {
6098     /**
6099      * @cfg {Boolean} removeMask
6100      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6101      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6102      */
6103     /**
6104      * @cfg {String} msg
6105      * The text to display in a centered loading message box (defaults to 'Loading...')
6106      */
6107     msg : 'Loading...',
6108     /**
6109      * @cfg {String} msgCls
6110      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6111      */
6112     msgCls : 'x-mask-loading',
6113
6114     /**
6115      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6116      * @type Boolean
6117      */
6118     disabled: false,
6119
6120     /**
6121      * Disables the mask to prevent it from being displayed
6122      */
6123     disable : function(){
6124        this.disabled = true;
6125     },
6126
6127     /**
6128      * Enables the mask so that it can be displayed
6129      */
6130     enable : function(){
6131         this.disabled = false;
6132     },
6133     
6134     onLoadException : function()
6135     {
6136         Roo.log(arguments);
6137         
6138         if (typeof(arguments[3]) != 'undefined') {
6139             Roo.MessageBox.alert("Error loading",arguments[3]);
6140         } 
6141         /*
6142         try {
6143             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6144                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6145             }   
6146         } catch(e) {
6147             
6148         }
6149         */
6150     
6151         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6152     },
6153     // private
6154     onLoad : function()
6155     {
6156         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6157     },
6158
6159     // private
6160     onBeforeLoad : function(){
6161         if(!this.disabled){
6162             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6163         }
6164     },
6165
6166     // private
6167     destroy : function(){
6168         if(this.store){
6169             this.store.un('beforeload', this.onBeforeLoad, this);
6170             this.store.un('load', this.onLoad, this);
6171             this.store.un('loadexception', this.onLoadException, this);
6172         }else{
6173             var um = this.el.getUpdateManager();
6174             um.un('beforeupdate', this.onBeforeLoad, this);
6175             um.un('update', this.onLoad, this);
6176             um.un('failure', this.onLoad, this);
6177         }
6178     }
6179 };/*
6180  * - LGPL
6181  *
6182  * table
6183  * 
6184  */
6185
6186 /**
6187  * @class Roo.bootstrap.Table
6188  * @extends Roo.bootstrap.Component
6189  * Bootstrap Table class
6190  * @cfg {String} cls table class
6191  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6192  * @cfg {String} bgcolor Specifies the background color for a table
6193  * @cfg {Number} border Specifies whether the table cells should have borders or not
6194  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6195  * @cfg {Number} cellspacing Specifies the space between cells
6196  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6197  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6198  * @cfg {String} sortable Specifies that the table should be sortable
6199  * @cfg {String} summary Specifies a summary of the content of a table
6200  * @cfg {Number} width Specifies the width of a table
6201  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6202  * 
6203  * @cfg {boolean} striped Should the rows be alternative striped
6204  * @cfg {boolean} bordered Add borders to the table
6205  * @cfg {boolean} hover Add hover highlighting
6206  * @cfg {boolean} condensed Format condensed
6207  * @cfg {boolean} responsive Format condensed
6208  * @cfg {Boolean} loadMask (true|false) default false
6209  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6210  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6211  * @cfg {Boolean} rowSelection (true|false) default false
6212  * @cfg {Boolean} cellSelection (true|false) default false
6213  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6214  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6215  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6216  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6217  
6218  * 
6219  * @constructor
6220  * Create a new Table
6221  * @param {Object} config The config object
6222  */
6223
6224 Roo.bootstrap.Table = function(config){
6225     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6226     
6227   
6228     
6229     // BC...
6230     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6231     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6232     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6233     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6234     
6235     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6236     if (this.sm) {
6237         this.sm.grid = this;
6238         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6239         this.sm = this.selModel;
6240         this.sm.xmodule = this.xmodule || false;
6241     }
6242     
6243     if (this.cm && typeof(this.cm.config) == 'undefined') {
6244         this.colModel = new Roo.grid.ColumnModel(this.cm);
6245         this.cm = this.colModel;
6246         this.cm.xmodule = this.xmodule || false;
6247     }
6248     if (this.store) {
6249         this.store= Roo.factory(this.store, Roo.data);
6250         this.ds = this.store;
6251         this.ds.xmodule = this.xmodule || false;
6252          
6253     }
6254     if (this.footer && this.store) {
6255         this.footer.dataSource = this.ds;
6256         this.footer = Roo.factory(this.footer);
6257     }
6258     
6259     /** @private */
6260     this.addEvents({
6261         /**
6262          * @event cellclick
6263          * Fires when a cell is clicked
6264          * @param {Roo.bootstrap.Table} this
6265          * @param {Roo.Element} el
6266          * @param {Number} rowIndex
6267          * @param {Number} columnIndex
6268          * @param {Roo.EventObject} e
6269          */
6270         "cellclick" : true,
6271         /**
6272          * @event celldblclick
6273          * Fires when a cell is double clicked
6274          * @param {Roo.bootstrap.Table} this
6275          * @param {Roo.Element} el
6276          * @param {Number} rowIndex
6277          * @param {Number} columnIndex
6278          * @param {Roo.EventObject} e
6279          */
6280         "celldblclick" : true,
6281         /**
6282          * @event rowclick
6283          * Fires when a row is clicked
6284          * @param {Roo.bootstrap.Table} this
6285          * @param {Roo.Element} el
6286          * @param {Number} rowIndex
6287          * @param {Roo.EventObject} e
6288          */
6289         "rowclick" : true,
6290         /**
6291          * @event rowdblclick
6292          * Fires when a row is double clicked
6293          * @param {Roo.bootstrap.Table} this
6294          * @param {Roo.Element} el
6295          * @param {Number} rowIndex
6296          * @param {Roo.EventObject} e
6297          */
6298         "rowdblclick" : true,
6299         /**
6300          * @event mouseover
6301          * Fires when a mouseover occur
6302          * @param {Roo.bootstrap.Table} this
6303          * @param {Roo.Element} el
6304          * @param {Number} rowIndex
6305          * @param {Number} columnIndex
6306          * @param {Roo.EventObject} e
6307          */
6308         "mouseover" : true,
6309         /**
6310          * @event mouseout
6311          * Fires when a mouseout occur
6312          * @param {Roo.bootstrap.Table} this
6313          * @param {Roo.Element} el
6314          * @param {Number} rowIndex
6315          * @param {Number} columnIndex
6316          * @param {Roo.EventObject} e
6317          */
6318         "mouseout" : true,
6319         /**
6320          * @event rowclass
6321          * Fires when a row is rendered, so you can change add a style to it.
6322          * @param {Roo.bootstrap.Table} this
6323          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6324          */
6325         'rowclass' : true,
6326           /**
6327          * @event rowsrendered
6328          * Fires when all the  rows have been rendered
6329          * @param {Roo.bootstrap.Table} this
6330          */
6331         'rowsrendered' : true,
6332         /**
6333          * @event contextmenu
6334          * The raw contextmenu event for the entire grid.
6335          * @param {Roo.EventObject} e
6336          */
6337         "contextmenu" : true,
6338         /**
6339          * @event rowcontextmenu
6340          * Fires when a row is right clicked
6341          * @param {Roo.bootstrap.Table} this
6342          * @param {Number} rowIndex
6343          * @param {Roo.EventObject} e
6344          */
6345         "rowcontextmenu" : true,
6346         /**
6347          * @event cellcontextmenu
6348          * Fires when a cell is right clicked
6349          * @param {Roo.bootstrap.Table} this
6350          * @param {Number} rowIndex
6351          * @param {Number} cellIndex
6352          * @param {Roo.EventObject} e
6353          */
6354          "cellcontextmenu" : true,
6355          /**
6356          * @event headercontextmenu
6357          * Fires when a header is right clicked
6358          * @param {Roo.bootstrap.Table} this
6359          * @param {Number} columnIndex
6360          * @param {Roo.EventObject} e
6361          */
6362         "headercontextmenu" : true
6363     });
6364 };
6365
6366 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6367     
6368     cls: false,
6369     align: false,
6370     bgcolor: false,
6371     border: false,
6372     cellpadding: false,
6373     cellspacing: false,
6374     frame: false,
6375     rules: false,
6376     sortable: false,
6377     summary: false,
6378     width: false,
6379     striped : false,
6380     scrollBody : false,
6381     bordered: false,
6382     hover:  false,
6383     condensed : false,
6384     responsive : false,
6385     sm : false,
6386     cm : false,
6387     store : false,
6388     loadMask : false,
6389     footerShow : true,
6390     headerShow : true,
6391   
6392     rowSelection : false,
6393     cellSelection : false,
6394     layout : false,
6395     
6396     // Roo.Element - the tbody
6397     mainBody: false,
6398     // Roo.Element - thead element
6399     mainHead: false,
6400     
6401     container: false, // used by gridpanel...
6402     
6403     lazyLoad : false,
6404     
6405     CSS : Roo.util.CSS,
6406     
6407     auto_hide_footer : false,
6408     
6409     getAutoCreate : function()
6410     {
6411         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6412         
6413         cfg = {
6414             tag: 'table',
6415             cls : 'table',
6416             cn : []
6417         };
6418         if (this.scrollBody) {
6419             cfg.cls += ' table-body-fixed';
6420         }    
6421         if (this.striped) {
6422             cfg.cls += ' table-striped';
6423         }
6424         
6425         if (this.hover) {
6426             cfg.cls += ' table-hover';
6427         }
6428         if (this.bordered) {
6429             cfg.cls += ' table-bordered';
6430         }
6431         if (this.condensed) {
6432             cfg.cls += ' table-condensed';
6433         }
6434         if (this.responsive) {
6435             cfg.cls += ' table-responsive';
6436         }
6437         
6438         if (this.cls) {
6439             cfg.cls+=  ' ' +this.cls;
6440         }
6441         
6442         // this lot should be simplifed...
6443         var _t = this;
6444         var cp = [
6445             'align',
6446             'bgcolor',
6447             'border',
6448             'cellpadding',
6449             'cellspacing',
6450             'frame',
6451             'rules',
6452             'sortable',
6453             'summary',
6454             'width'
6455         ].forEach(function(k) {
6456             if (_t[k]) {
6457                 cfg[k] = _t[k];
6458             }
6459         });
6460         
6461         
6462         if (this.layout) {
6463             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6464         }
6465         
6466         if(this.store || this.cm){
6467             if(this.headerShow){
6468                 cfg.cn.push(this.renderHeader());
6469             }
6470             
6471             cfg.cn.push(this.renderBody());
6472             
6473             if(this.footerShow){
6474                 cfg.cn.push(this.renderFooter());
6475             }
6476             // where does this come from?
6477             //cfg.cls+=  ' TableGrid';
6478         }
6479         
6480         return { cn : [ cfg ] };
6481     },
6482     
6483     initEvents : function()
6484     {   
6485         if(!this.store || !this.cm){
6486             return;
6487         }
6488         if (this.selModel) {
6489             this.selModel.initEvents();
6490         }
6491         
6492         
6493         //Roo.log('initEvents with ds!!!!');
6494         
6495         this.mainBody = this.el.select('tbody', true).first();
6496         this.mainHead = this.el.select('thead', true).first();
6497         this.mainFoot = this.el.select('tfoot', true).first();
6498         
6499         
6500         
6501         var _this = this;
6502         
6503         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6504             e.on('click', _this.sort, _this);
6505         });
6506         
6507         this.mainBody.on("click", this.onClick, this);
6508         this.mainBody.on("dblclick", this.onDblClick, this);
6509         
6510         // why is this done????? = it breaks dialogs??
6511         //this.parent().el.setStyle('position', 'relative');
6512         
6513         
6514         if (this.footer) {
6515             this.footer.parentId = this.id;
6516             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6517             
6518             if(this.lazyLoad){
6519                 this.el.select('tfoot tr td').first().addClass('hide');
6520             }
6521         } 
6522         
6523         if(this.loadMask) {
6524             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6525         }
6526         
6527         this.store.on('load', this.onLoad, this);
6528         this.store.on('beforeload', this.onBeforeLoad, this);
6529         this.store.on('update', this.onUpdate, this);
6530         this.store.on('add', this.onAdd, this);
6531         this.store.on("clear", this.clear, this);
6532         
6533         this.el.on("contextmenu", this.onContextMenu, this);
6534         
6535         this.mainBody.on('scroll', this.onBodyScroll, this);
6536         
6537         this.cm.on("headerchange", this.onHeaderChange, this);
6538         
6539         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6540         
6541     },
6542     
6543     onContextMenu : function(e, t)
6544     {
6545         this.processEvent("contextmenu", e);
6546     },
6547     
6548     processEvent : function(name, e)
6549     {
6550         if (name != 'touchstart' ) {
6551             this.fireEvent(name, e);    
6552         }
6553         
6554         var t = e.getTarget();
6555         
6556         var cell = Roo.get(t);
6557         
6558         if(!cell){
6559             return;
6560         }
6561         
6562         if(cell.findParent('tfoot', false, true)){
6563             return;
6564         }
6565         
6566         if(cell.findParent('thead', false, true)){
6567             
6568             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6569                 cell = Roo.get(t).findParent('th', false, true);
6570                 if (!cell) {
6571                     Roo.log("failed to find th in thead?");
6572                     Roo.log(e.getTarget());
6573                     return;
6574                 }
6575             }
6576             
6577             var cellIndex = cell.dom.cellIndex;
6578             
6579             var ename = name == 'touchstart' ? 'click' : name;
6580             this.fireEvent("header" + ename, this, cellIndex, e);
6581             
6582             return;
6583         }
6584         
6585         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6586             cell = Roo.get(t).findParent('td', false, true);
6587             if (!cell) {
6588                 Roo.log("failed to find th in tbody?");
6589                 Roo.log(e.getTarget());
6590                 return;
6591             }
6592         }
6593         
6594         var row = cell.findParent('tr', false, true);
6595         var cellIndex = cell.dom.cellIndex;
6596         var rowIndex = row.dom.rowIndex - 1;
6597         
6598         if(row !== false){
6599             
6600             this.fireEvent("row" + name, this, rowIndex, e);
6601             
6602             if(cell !== false){
6603             
6604                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6605             }
6606         }
6607         
6608     },
6609     
6610     onMouseover : function(e, el)
6611     {
6612         var cell = Roo.get(el);
6613         
6614         if(!cell){
6615             return;
6616         }
6617         
6618         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6619             cell = cell.findParent('td', false, true);
6620         }
6621         
6622         var row = cell.findParent('tr', false, true);
6623         var cellIndex = cell.dom.cellIndex;
6624         var rowIndex = row.dom.rowIndex - 1; // start from 0
6625         
6626         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6627         
6628     },
6629     
6630     onMouseout : function(e, el)
6631     {
6632         var cell = Roo.get(el);
6633         
6634         if(!cell){
6635             return;
6636         }
6637         
6638         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6639             cell = cell.findParent('td', false, true);
6640         }
6641         
6642         var row = cell.findParent('tr', false, true);
6643         var cellIndex = cell.dom.cellIndex;
6644         var rowIndex = row.dom.rowIndex - 1; // start from 0
6645         
6646         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6647         
6648     },
6649     
6650     onClick : function(e, el)
6651     {
6652         var cell = Roo.get(el);
6653         
6654         if(!cell || (!this.cellSelection && !this.rowSelection)){
6655             return;
6656         }
6657         
6658         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6659             cell = cell.findParent('td', false, true);
6660         }
6661         
6662         if(!cell || typeof(cell) == 'undefined'){
6663             return;
6664         }
6665         
6666         var row = cell.findParent('tr', false, true);
6667         
6668         if(!row || typeof(row) == 'undefined'){
6669             return;
6670         }
6671         
6672         var cellIndex = cell.dom.cellIndex;
6673         var rowIndex = this.getRowIndex(row);
6674         
6675         // why??? - should these not be based on SelectionModel?
6676         if(this.cellSelection){
6677             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6678         }
6679         
6680         if(this.rowSelection){
6681             this.fireEvent('rowclick', this, row, rowIndex, e);
6682         }
6683         
6684         
6685     },
6686         
6687     onDblClick : function(e,el)
6688     {
6689         var cell = Roo.get(el);
6690         
6691         if(!cell || (!this.cellSelection && !this.rowSelection)){
6692             return;
6693         }
6694         
6695         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6696             cell = cell.findParent('td', false, true);
6697         }
6698         
6699         if(!cell || typeof(cell) == 'undefined'){
6700             return;
6701         }
6702         
6703         var row = cell.findParent('tr', false, true);
6704         
6705         if(!row || typeof(row) == 'undefined'){
6706             return;
6707         }
6708         
6709         var cellIndex = cell.dom.cellIndex;
6710         var rowIndex = this.getRowIndex(row);
6711         
6712         if(this.cellSelection){
6713             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6714         }
6715         
6716         if(this.rowSelection){
6717             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6718         }
6719     },
6720     
6721     sort : function(e,el)
6722     {
6723         var col = Roo.get(el);
6724         
6725         if(!col.hasClass('sortable')){
6726             return;
6727         }
6728         
6729         var sort = col.attr('sort');
6730         var dir = 'ASC';
6731         
6732         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6733             dir = 'DESC';
6734         }
6735         
6736         this.store.sortInfo = {field : sort, direction : dir};
6737         
6738         if (this.footer) {
6739             Roo.log("calling footer first");
6740             this.footer.onClick('first');
6741         } else {
6742         
6743             this.store.load({ params : { start : 0 } });
6744         }
6745     },
6746     
6747     renderHeader : function()
6748     {
6749         var header = {
6750             tag: 'thead',
6751             cn : []
6752         };
6753         
6754         var cm = this.cm;
6755         this.totalWidth = 0;
6756         
6757         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6758             
6759             var config = cm.config[i];
6760             
6761             var c = {
6762                 tag: 'th',
6763                 cls : 'x-hcol-' + i,
6764                 style : '',
6765                 html: cm.getColumnHeader(i)
6766             };
6767             
6768             var hh = '';
6769             
6770             if(typeof(config.sortable) != 'undefined' && config.sortable){
6771                 c.cls = 'sortable';
6772                 c.html = '<i class="glyphicon"></i>' + c.html;
6773             }
6774             
6775             if(typeof(config.lgHeader) != 'undefined'){
6776                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6777             }
6778             
6779             if(typeof(config.mdHeader) != 'undefined'){
6780                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6781             }
6782             
6783             if(typeof(config.smHeader) != 'undefined'){
6784                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6785             }
6786             
6787             if(typeof(config.xsHeader) != 'undefined'){
6788                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6789             }
6790             
6791             if(hh.length){
6792                 c.html = hh;
6793             }
6794             
6795             if(typeof(config.tooltip) != 'undefined'){
6796                 c.tooltip = config.tooltip;
6797             }
6798             
6799             if(typeof(config.colspan) != 'undefined'){
6800                 c.colspan = config.colspan;
6801             }
6802             
6803             if(typeof(config.hidden) != 'undefined' && config.hidden){
6804                 c.style += ' display:none;';
6805             }
6806             
6807             if(typeof(config.dataIndex) != 'undefined'){
6808                 c.sort = config.dataIndex;
6809             }
6810             
6811            
6812             
6813             if(typeof(config.align) != 'undefined' && config.align.length){
6814                 c.style += ' text-align:' + config.align + ';';
6815             }
6816             
6817             if(typeof(config.width) != 'undefined'){
6818                 c.style += ' width:' + config.width + 'px;';
6819                 this.totalWidth += config.width;
6820             } else {
6821                 this.totalWidth += 100; // assume minimum of 100 per column?
6822             }
6823             
6824             if(typeof(config.cls) != 'undefined'){
6825                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6826             }
6827             
6828             ['xs','sm','md','lg'].map(function(size){
6829                 
6830                 if(typeof(config[size]) == 'undefined'){
6831                     return;
6832                 }
6833                 
6834                 if (!config[size]) { // 0 = hidden
6835                     c.cls += ' hidden-' + size;
6836                     return;
6837                 }
6838                 
6839                 c.cls += ' col-' + size + '-' + config[size];
6840
6841             });
6842             
6843             header.cn.push(c)
6844         }
6845         
6846         return header;
6847     },
6848     
6849     renderBody : function()
6850     {
6851         var body = {
6852             tag: 'tbody',
6853             cn : [
6854                 {
6855                     tag: 'tr',
6856                     cn : [
6857                         {
6858                             tag : 'td',
6859                             colspan :  this.cm.getColumnCount()
6860                         }
6861                     ]
6862                 }
6863             ]
6864         };
6865         
6866         return body;
6867     },
6868     
6869     renderFooter : function()
6870     {
6871         var footer = {
6872             tag: 'tfoot',
6873             cn : [
6874                 {
6875                     tag: 'tr',
6876                     cn : [
6877                         {
6878                             tag : 'td',
6879                             colspan :  this.cm.getColumnCount()
6880                         }
6881                     ]
6882                 }
6883             ]
6884         };
6885         
6886         return footer;
6887     },
6888     
6889     
6890     
6891     onLoad : function()
6892     {
6893 //        Roo.log('ds onload');
6894         this.clear();
6895         
6896         var _this = this;
6897         var cm = this.cm;
6898         var ds = this.store;
6899         
6900         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6901             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6902             if (_this.store.sortInfo) {
6903                     
6904                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6905                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6906                 }
6907                 
6908                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6909                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6910                 }
6911             }
6912         });
6913         
6914         var tbody =  this.mainBody;
6915               
6916         if(ds.getCount() > 0){
6917             ds.data.each(function(d,rowIndex){
6918                 var row =  this.renderRow(cm, ds, rowIndex);
6919                 
6920                 tbody.createChild(row);
6921                 
6922                 var _this = this;
6923                 
6924                 if(row.cellObjects.length){
6925                     Roo.each(row.cellObjects, function(r){
6926                         _this.renderCellObject(r);
6927                     })
6928                 }
6929                 
6930             }, this);
6931         }
6932         
6933         var tfoot = this.el.select('tfoot', true).first();
6934         
6935         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6936             
6937             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6938             
6939             var total = this.ds.getTotalCount();
6940             
6941             if(this.footer.pageSize < total){
6942                 this.mainFoot.show();
6943             }
6944         }
6945         
6946         Roo.each(this.el.select('tbody td', true).elements, function(e){
6947             e.on('mouseover', _this.onMouseover, _this);
6948         });
6949         
6950         Roo.each(this.el.select('tbody td', true).elements, function(e){
6951             e.on('mouseout', _this.onMouseout, _this);
6952         });
6953         this.fireEvent('rowsrendered', this);
6954         
6955         this.autoSize();
6956     },
6957     
6958     
6959     onUpdate : function(ds,record)
6960     {
6961         this.refreshRow(record);
6962         this.autoSize();
6963     },
6964     
6965     onRemove : function(ds, record, index, isUpdate){
6966         if(isUpdate !== true){
6967             this.fireEvent("beforerowremoved", this, index, record);
6968         }
6969         var bt = this.mainBody.dom;
6970         
6971         var rows = this.el.select('tbody > tr', true).elements;
6972         
6973         if(typeof(rows[index]) != 'undefined'){
6974             bt.removeChild(rows[index].dom);
6975         }
6976         
6977 //        if(bt.rows[index]){
6978 //            bt.removeChild(bt.rows[index]);
6979 //        }
6980         
6981         if(isUpdate !== true){
6982             //this.stripeRows(index);
6983             //this.syncRowHeights(index, index);
6984             //this.layout();
6985             this.fireEvent("rowremoved", this, index, record);
6986         }
6987     },
6988     
6989     onAdd : function(ds, records, rowIndex)
6990     {
6991         //Roo.log('on Add called');
6992         // - note this does not handle multiple adding very well..
6993         var bt = this.mainBody.dom;
6994         for (var i =0 ; i < records.length;i++) {
6995             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6996             //Roo.log(records[i]);
6997             //Roo.log(this.store.getAt(rowIndex+i));
6998             this.insertRow(this.store, rowIndex + i, false);
6999             return;
7000         }
7001         
7002     },
7003     
7004     
7005     refreshRow : function(record){
7006         var ds = this.store, index;
7007         if(typeof record == 'number'){
7008             index = record;
7009             record = ds.getAt(index);
7010         }else{
7011             index = ds.indexOf(record);
7012         }
7013         this.insertRow(ds, index, true);
7014         this.autoSize();
7015         this.onRemove(ds, record, index+1, true);
7016         this.autoSize();
7017         //this.syncRowHeights(index, index);
7018         //this.layout();
7019         this.fireEvent("rowupdated", this, index, record);
7020     },
7021     
7022     insertRow : function(dm, rowIndex, isUpdate){
7023         
7024         if(!isUpdate){
7025             this.fireEvent("beforerowsinserted", this, rowIndex);
7026         }
7027             //var s = this.getScrollState();
7028         var row = this.renderRow(this.cm, this.store, rowIndex);
7029         // insert before rowIndex..
7030         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7031         
7032         var _this = this;
7033                 
7034         if(row.cellObjects.length){
7035             Roo.each(row.cellObjects, function(r){
7036                 _this.renderCellObject(r);
7037             })
7038         }
7039             
7040         if(!isUpdate){
7041             this.fireEvent("rowsinserted", this, rowIndex);
7042             //this.syncRowHeights(firstRow, lastRow);
7043             //this.stripeRows(firstRow);
7044             //this.layout();
7045         }
7046         
7047     },
7048     
7049     
7050     getRowDom : function(rowIndex)
7051     {
7052         var rows = this.el.select('tbody > tr', true).elements;
7053         
7054         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7055         
7056     },
7057     // returns the object tree for a tr..
7058   
7059     
7060     renderRow : function(cm, ds, rowIndex) 
7061     {
7062         var d = ds.getAt(rowIndex);
7063         
7064         var row = {
7065             tag : 'tr',
7066             cls : 'x-row-' + rowIndex,
7067             cn : []
7068         };
7069             
7070         var cellObjects = [];
7071         
7072         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7073             var config = cm.config[i];
7074             
7075             var renderer = cm.getRenderer(i);
7076             var value = '';
7077             var id = false;
7078             
7079             if(typeof(renderer) !== 'undefined'){
7080                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7081             }
7082             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7083             // and are rendered into the cells after the row is rendered - using the id for the element.
7084             
7085             if(typeof(value) === 'object'){
7086                 id = Roo.id();
7087                 cellObjects.push({
7088                     container : id,
7089                     cfg : value 
7090                 })
7091             }
7092             
7093             var rowcfg = {
7094                 record: d,
7095                 rowIndex : rowIndex,
7096                 colIndex : i,
7097                 rowClass : ''
7098             };
7099
7100             this.fireEvent('rowclass', this, rowcfg);
7101             
7102             var td = {
7103                 tag: 'td',
7104                 cls : rowcfg.rowClass + ' x-col-' + i,
7105                 style: '',
7106                 html: (typeof(value) === 'object') ? '' : value
7107             };
7108             
7109             if (id) {
7110                 td.id = id;
7111             }
7112             
7113             if(typeof(config.colspan) != 'undefined'){
7114                 td.colspan = config.colspan;
7115             }
7116             
7117             if(typeof(config.hidden) != 'undefined' && config.hidden){
7118                 td.style += ' display:none;';
7119             }
7120             
7121             if(typeof(config.align) != 'undefined' && config.align.length){
7122                 td.style += ' text-align:' + config.align + ';';
7123             }
7124             if(typeof(config.valign) != 'undefined' && config.valign.length){
7125                 td.style += ' vertical-align:' + config.valign + ';';
7126             }
7127             
7128             if(typeof(config.width) != 'undefined'){
7129                 td.style += ' width:' +  config.width + 'px;';
7130             }
7131             
7132             if(typeof(config.cursor) != 'undefined'){
7133                 td.style += ' cursor:' +  config.cursor + ';';
7134             }
7135             
7136             if(typeof(config.cls) != 'undefined'){
7137                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7138             }
7139             
7140             ['xs','sm','md','lg'].map(function(size){
7141                 
7142                 if(typeof(config[size]) == 'undefined'){
7143                     return;
7144                 }
7145                 
7146                 if (!config[size]) { // 0 = hidden
7147                     td.cls += ' hidden-' + size;
7148                     return;
7149                 }
7150                 
7151                 td.cls += ' col-' + size + '-' + config[size];
7152
7153             });
7154             
7155             row.cn.push(td);
7156            
7157         }
7158         
7159         row.cellObjects = cellObjects;
7160         
7161         return row;
7162           
7163     },
7164     
7165     
7166     
7167     onBeforeLoad : function()
7168     {
7169         
7170     },
7171      /**
7172      * Remove all rows
7173      */
7174     clear : function()
7175     {
7176         this.el.select('tbody', true).first().dom.innerHTML = '';
7177     },
7178     /**
7179      * Show or hide a row.
7180      * @param {Number} rowIndex to show or hide
7181      * @param {Boolean} state hide
7182      */
7183     setRowVisibility : function(rowIndex, state)
7184     {
7185         var bt = this.mainBody.dom;
7186         
7187         var rows = this.el.select('tbody > tr', true).elements;
7188         
7189         if(typeof(rows[rowIndex]) == 'undefined'){
7190             return;
7191         }
7192         rows[rowIndex].dom.style.display = state ? '' : 'none';
7193     },
7194     
7195     
7196     getSelectionModel : function(){
7197         if(!this.selModel){
7198             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7199         }
7200         return this.selModel;
7201     },
7202     /*
7203      * Render the Roo.bootstrap object from renderder
7204      */
7205     renderCellObject : function(r)
7206     {
7207         var _this = this;
7208         
7209         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7210         
7211         var t = r.cfg.render(r.container);
7212         
7213         if(r.cfg.cn){
7214             Roo.each(r.cfg.cn, function(c){
7215                 var child = {
7216                     container: t.getChildContainer(),
7217                     cfg: c
7218                 };
7219                 _this.renderCellObject(child);
7220             })
7221         }
7222     },
7223     
7224     getRowIndex : function(row)
7225     {
7226         var rowIndex = -1;
7227         
7228         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7229             if(el != row){
7230                 return;
7231             }
7232             
7233             rowIndex = index;
7234         });
7235         
7236         return rowIndex;
7237     },
7238      /**
7239      * Returns the grid's underlying element = used by panel.Grid
7240      * @return {Element} The element
7241      */
7242     getGridEl : function(){
7243         return this.el;
7244     },
7245      /**
7246      * Forces a resize - used by panel.Grid
7247      * @return {Element} The element
7248      */
7249     autoSize : function()
7250     {
7251         //var ctr = Roo.get(this.container.dom.parentElement);
7252         var ctr = Roo.get(this.el.dom);
7253         
7254         var thd = this.getGridEl().select('thead',true).first();
7255         var tbd = this.getGridEl().select('tbody', true).first();
7256         var tfd = this.getGridEl().select('tfoot', true).first();
7257         
7258         var cw = ctr.getWidth();
7259         
7260         if (tbd) {
7261             
7262             tbd.setSize(ctr.getWidth(),
7263                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7264             );
7265             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7266             cw -= barsize;
7267         }
7268         cw = Math.max(cw, this.totalWidth);
7269         this.getGridEl().select('tr',true).setWidth(cw);
7270         // resize 'expandable coloumn?
7271         
7272         return; // we doe not have a view in this design..
7273         
7274     },
7275     onBodyScroll: function()
7276     {
7277         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7278         if(this.mainHead){
7279             this.mainHead.setStyle({
7280                 'position' : 'relative',
7281                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7282             });
7283         }
7284         
7285         if(this.lazyLoad){
7286             
7287             var scrollHeight = this.mainBody.dom.scrollHeight;
7288             
7289             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7290             
7291             var height = this.mainBody.getHeight();
7292             
7293             if(scrollHeight - height == scrollTop) {
7294                 
7295                 var total = this.ds.getTotalCount();
7296                 
7297                 if(this.footer.cursor + this.footer.pageSize < total){
7298                     
7299                     this.footer.ds.load({
7300                         params : {
7301                             start : this.footer.cursor + this.footer.pageSize,
7302                             limit : this.footer.pageSize
7303                         },
7304                         add : true
7305                     });
7306                 }
7307             }
7308             
7309         }
7310     },
7311     
7312     onHeaderChange : function()
7313     {
7314         var header = this.renderHeader();
7315         var table = this.el.select('table', true).first();
7316         
7317         this.mainHead.remove();
7318         this.mainHead = table.createChild(header, this.mainBody, false);
7319     },
7320     
7321     onHiddenChange : function(colModel, colIndex, hidden)
7322     {
7323         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7324         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7325         
7326         this.CSS.updateRule(thSelector, "display", "");
7327         this.CSS.updateRule(tdSelector, "display", "");
7328         
7329         if(hidden){
7330             this.CSS.updateRule(thSelector, "display", "none");
7331             this.CSS.updateRule(tdSelector, "display", "none");
7332         }
7333         
7334         this.onHeaderChange();
7335         this.onLoad();
7336     },
7337     
7338     setColumnWidth: function(col_index, width)
7339     {
7340         // width = "md-2 xs-2..."
7341         if(!this.colModel.config[col_index]) {
7342             return;
7343         }
7344         
7345         var w = width.split(" ");
7346         
7347         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7348         
7349         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7350         
7351         
7352         for(var j = 0; j < w.length; j++) {
7353             
7354             if(!w[j]) {
7355                 continue;
7356             }
7357             
7358             var size_cls = w[j].split("-");
7359             
7360             if(!Number.isInteger(size_cls[1] * 1)) {
7361                 continue;
7362             }
7363             
7364             if(!this.colModel.config[col_index][size_cls[0]]) {
7365                 continue;
7366             }
7367             
7368             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7369                 continue;
7370             }
7371             
7372             h_row[0].classList.replace(
7373                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7374                 "col-"+size_cls[0]+"-"+size_cls[1]
7375             );
7376             
7377             for(var i = 0; i < rows.length; i++) {
7378                 
7379                 var size_cls = w[j].split("-");
7380                 
7381                 if(!Number.isInteger(size_cls[1] * 1)) {
7382                     continue;
7383                 }
7384                 
7385                 if(!this.colModel.config[col_index][size_cls[0]]) {
7386                     continue;
7387                 }
7388                 
7389                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7390                     continue;
7391                 }
7392                 
7393                 rows[i].classList.replace(
7394                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7395                     "col-"+size_cls[0]+"-"+size_cls[1]
7396                 );
7397             }
7398             
7399             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7400         }
7401     }
7402 });
7403
7404  
7405
7406  /*
7407  * - LGPL
7408  *
7409  * table cell
7410  * 
7411  */
7412
7413 /**
7414  * @class Roo.bootstrap.TableCell
7415  * @extends Roo.bootstrap.Component
7416  * Bootstrap TableCell class
7417  * @cfg {String} html cell contain text
7418  * @cfg {String} cls cell class
7419  * @cfg {String} tag cell tag (td|th) default td
7420  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7421  * @cfg {String} align Aligns the content in a cell
7422  * @cfg {String} axis Categorizes cells
7423  * @cfg {String} bgcolor Specifies the background color of a cell
7424  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7425  * @cfg {Number} colspan Specifies the number of columns a cell should span
7426  * @cfg {String} headers Specifies one or more header cells a cell is related to
7427  * @cfg {Number} height Sets the height of a cell
7428  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7429  * @cfg {Number} rowspan Sets the number of rows a cell should span
7430  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7431  * @cfg {String} valign Vertical aligns the content in a cell
7432  * @cfg {Number} width Specifies the width of a cell
7433  * 
7434  * @constructor
7435  * Create a new TableCell
7436  * @param {Object} config The config object
7437  */
7438
7439 Roo.bootstrap.TableCell = function(config){
7440     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7441 };
7442
7443 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7444     
7445     html: false,
7446     cls: false,
7447     tag: false,
7448     abbr: false,
7449     align: false,
7450     axis: false,
7451     bgcolor: false,
7452     charoff: false,
7453     colspan: false,
7454     headers: false,
7455     height: false,
7456     nowrap: false,
7457     rowspan: false,
7458     scope: false,
7459     valign: false,
7460     width: false,
7461     
7462     
7463     getAutoCreate : function(){
7464         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7465         
7466         cfg = {
7467             tag: 'td'
7468         };
7469         
7470         if(this.tag){
7471             cfg.tag = this.tag;
7472         }
7473         
7474         if (this.html) {
7475             cfg.html=this.html
7476         }
7477         if (this.cls) {
7478             cfg.cls=this.cls
7479         }
7480         if (this.abbr) {
7481             cfg.abbr=this.abbr
7482         }
7483         if (this.align) {
7484             cfg.align=this.align
7485         }
7486         if (this.axis) {
7487             cfg.axis=this.axis
7488         }
7489         if (this.bgcolor) {
7490             cfg.bgcolor=this.bgcolor
7491         }
7492         if (this.charoff) {
7493             cfg.charoff=this.charoff
7494         }
7495         if (this.colspan) {
7496             cfg.colspan=this.colspan
7497         }
7498         if (this.headers) {
7499             cfg.headers=this.headers
7500         }
7501         if (this.height) {
7502             cfg.height=this.height
7503         }
7504         if (this.nowrap) {
7505             cfg.nowrap=this.nowrap
7506         }
7507         if (this.rowspan) {
7508             cfg.rowspan=this.rowspan
7509         }
7510         if (this.scope) {
7511             cfg.scope=this.scope
7512         }
7513         if (this.valign) {
7514             cfg.valign=this.valign
7515         }
7516         if (this.width) {
7517             cfg.width=this.width
7518         }
7519         
7520         
7521         return cfg;
7522     }
7523    
7524 });
7525
7526  
7527
7528  /*
7529  * - LGPL
7530  *
7531  * table row
7532  * 
7533  */
7534
7535 /**
7536  * @class Roo.bootstrap.TableRow
7537  * @extends Roo.bootstrap.Component
7538  * Bootstrap TableRow class
7539  * @cfg {String} cls row class
7540  * @cfg {String} align Aligns the content in a table row
7541  * @cfg {String} bgcolor Specifies a background color for a table row
7542  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7543  * @cfg {String} valign Vertical aligns the content in a table row
7544  * 
7545  * @constructor
7546  * Create a new TableRow
7547  * @param {Object} config The config object
7548  */
7549
7550 Roo.bootstrap.TableRow = function(config){
7551     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7552 };
7553
7554 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7555     
7556     cls: false,
7557     align: false,
7558     bgcolor: false,
7559     charoff: false,
7560     valign: false,
7561     
7562     getAutoCreate : function(){
7563         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7564         
7565         cfg = {
7566             tag: 'tr'
7567         };
7568             
7569         if(this.cls){
7570             cfg.cls = this.cls;
7571         }
7572         if(this.align){
7573             cfg.align = this.align;
7574         }
7575         if(this.bgcolor){
7576             cfg.bgcolor = this.bgcolor;
7577         }
7578         if(this.charoff){
7579             cfg.charoff = this.charoff;
7580         }
7581         if(this.valign){
7582             cfg.valign = this.valign;
7583         }
7584         
7585         return cfg;
7586     }
7587    
7588 });
7589
7590  
7591
7592  /*
7593  * - LGPL
7594  *
7595  * table body
7596  * 
7597  */
7598
7599 /**
7600  * @class Roo.bootstrap.TableBody
7601  * @extends Roo.bootstrap.Component
7602  * Bootstrap TableBody class
7603  * @cfg {String} cls element class
7604  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7605  * @cfg {String} align Aligns the content inside the element
7606  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7607  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7608  * 
7609  * @constructor
7610  * Create a new TableBody
7611  * @param {Object} config The config object
7612  */
7613
7614 Roo.bootstrap.TableBody = function(config){
7615     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7616 };
7617
7618 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7619     
7620     cls: false,
7621     tag: false,
7622     align: false,
7623     charoff: false,
7624     valign: false,
7625     
7626     getAutoCreate : function(){
7627         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7628         
7629         cfg = {
7630             tag: 'tbody'
7631         };
7632             
7633         if (this.cls) {
7634             cfg.cls=this.cls
7635         }
7636         if(this.tag){
7637             cfg.tag = this.tag;
7638         }
7639         
7640         if(this.align){
7641             cfg.align = this.align;
7642         }
7643         if(this.charoff){
7644             cfg.charoff = this.charoff;
7645         }
7646         if(this.valign){
7647             cfg.valign = this.valign;
7648         }
7649         
7650         return cfg;
7651     }
7652     
7653     
7654 //    initEvents : function()
7655 //    {
7656 //        
7657 //        if(!this.store){
7658 //            return;
7659 //        }
7660 //        
7661 //        this.store = Roo.factory(this.store, Roo.data);
7662 //        this.store.on('load', this.onLoad, this);
7663 //        
7664 //        this.store.load();
7665 //        
7666 //    },
7667 //    
7668 //    onLoad: function () 
7669 //    {   
7670 //        this.fireEvent('load', this);
7671 //    }
7672 //    
7673 //   
7674 });
7675
7676  
7677
7678  /*
7679  * Based on:
7680  * Ext JS Library 1.1.1
7681  * Copyright(c) 2006-2007, Ext JS, LLC.
7682  *
7683  * Originally Released Under LGPL - original licence link has changed is not relivant.
7684  *
7685  * Fork - LGPL
7686  * <script type="text/javascript">
7687  */
7688
7689 // as we use this in bootstrap.
7690 Roo.namespace('Roo.form');
7691  /**
7692  * @class Roo.form.Action
7693  * Internal Class used to handle form actions
7694  * @constructor
7695  * @param {Roo.form.BasicForm} el The form element or its id
7696  * @param {Object} config Configuration options
7697  */
7698
7699  
7700  
7701 // define the action interface
7702 Roo.form.Action = function(form, options){
7703     this.form = form;
7704     this.options = options || {};
7705 };
7706 /**
7707  * Client Validation Failed
7708  * @const 
7709  */
7710 Roo.form.Action.CLIENT_INVALID = 'client';
7711 /**
7712  * Server Validation Failed
7713  * @const 
7714  */
7715 Roo.form.Action.SERVER_INVALID = 'server';
7716  /**
7717  * Connect to Server Failed
7718  * @const 
7719  */
7720 Roo.form.Action.CONNECT_FAILURE = 'connect';
7721 /**
7722  * Reading Data from Server Failed
7723  * @const 
7724  */
7725 Roo.form.Action.LOAD_FAILURE = 'load';
7726
7727 Roo.form.Action.prototype = {
7728     type : 'default',
7729     failureType : undefined,
7730     response : undefined,
7731     result : undefined,
7732
7733     // interface method
7734     run : function(options){
7735
7736     },
7737
7738     // interface method
7739     success : function(response){
7740
7741     },
7742
7743     // interface method
7744     handleResponse : function(response){
7745
7746     },
7747
7748     // default connection failure
7749     failure : function(response){
7750         
7751         this.response = response;
7752         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7753         this.form.afterAction(this, false);
7754     },
7755
7756     processResponse : function(response){
7757         this.response = response;
7758         if(!response.responseText){
7759             return true;
7760         }
7761         this.result = this.handleResponse(response);
7762         return this.result;
7763     },
7764
7765     // utility functions used internally
7766     getUrl : function(appendParams){
7767         var url = this.options.url || this.form.url || this.form.el.dom.action;
7768         if(appendParams){
7769             var p = this.getParams();
7770             if(p){
7771                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7772             }
7773         }
7774         return url;
7775     },
7776
7777     getMethod : function(){
7778         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7779     },
7780
7781     getParams : function(){
7782         var bp = this.form.baseParams;
7783         var p = this.options.params;
7784         if(p){
7785             if(typeof p == "object"){
7786                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7787             }else if(typeof p == 'string' && bp){
7788                 p += '&' + Roo.urlEncode(bp);
7789             }
7790         }else if(bp){
7791             p = Roo.urlEncode(bp);
7792         }
7793         return p;
7794     },
7795
7796     createCallback : function(){
7797         return {
7798             success: this.success,
7799             failure: this.failure,
7800             scope: this,
7801             timeout: (this.form.timeout*1000),
7802             upload: this.form.fileUpload ? this.success : undefined
7803         };
7804     }
7805 };
7806
7807 Roo.form.Action.Submit = function(form, options){
7808     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7809 };
7810
7811 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7812     type : 'submit',
7813
7814     haveProgress : false,
7815     uploadComplete : false,
7816     
7817     // uploadProgress indicator.
7818     uploadProgress : function()
7819     {
7820         if (!this.form.progressUrl) {
7821             return;
7822         }
7823         
7824         if (!this.haveProgress) {
7825             Roo.MessageBox.progress("Uploading", "Uploading");
7826         }
7827         if (this.uploadComplete) {
7828            Roo.MessageBox.hide();
7829            return;
7830         }
7831         
7832         this.haveProgress = true;
7833    
7834         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7835         
7836         var c = new Roo.data.Connection();
7837         c.request({
7838             url : this.form.progressUrl,
7839             params: {
7840                 id : uid
7841             },
7842             method: 'GET',
7843             success : function(req){
7844                //console.log(data);
7845                 var rdata = false;
7846                 var edata;
7847                 try  {
7848                    rdata = Roo.decode(req.responseText)
7849                 } catch (e) {
7850                     Roo.log("Invalid data from server..");
7851                     Roo.log(edata);
7852                     return;
7853                 }
7854                 if (!rdata || !rdata.success) {
7855                     Roo.log(rdata);
7856                     Roo.MessageBox.alert(Roo.encode(rdata));
7857                     return;
7858                 }
7859                 var data = rdata.data;
7860                 
7861                 if (this.uploadComplete) {
7862                    Roo.MessageBox.hide();
7863                    return;
7864                 }
7865                    
7866                 if (data){
7867                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7868                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7869                     );
7870                 }
7871                 this.uploadProgress.defer(2000,this);
7872             },
7873        
7874             failure: function(data) {
7875                 Roo.log('progress url failed ');
7876                 Roo.log(data);
7877             },
7878             scope : this
7879         });
7880            
7881     },
7882     
7883     
7884     run : function()
7885     {
7886         // run get Values on the form, so it syncs any secondary forms.
7887         this.form.getValues();
7888         
7889         var o = this.options;
7890         var method = this.getMethod();
7891         var isPost = method == 'POST';
7892         if(o.clientValidation === false || this.form.isValid()){
7893             
7894             if (this.form.progressUrl) {
7895                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7896                     (new Date() * 1) + '' + Math.random());
7897                     
7898             } 
7899             
7900             
7901             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7902                 form:this.form.el.dom,
7903                 url:this.getUrl(!isPost),
7904                 method: method,
7905                 params:isPost ? this.getParams() : null,
7906                 isUpload: this.form.fileUpload,
7907                 formData : this.form.formData
7908             }));
7909             
7910             this.uploadProgress();
7911
7912         }else if (o.clientValidation !== false){ // client validation failed
7913             this.failureType = Roo.form.Action.CLIENT_INVALID;
7914             this.form.afterAction(this, false);
7915         }
7916     },
7917
7918     success : function(response)
7919     {
7920         this.uploadComplete= true;
7921         if (this.haveProgress) {
7922             Roo.MessageBox.hide();
7923         }
7924         
7925         
7926         var result = this.processResponse(response);
7927         if(result === true || result.success){
7928             this.form.afterAction(this, true);
7929             return;
7930         }
7931         if(result.errors){
7932             this.form.markInvalid(result.errors);
7933             this.failureType = Roo.form.Action.SERVER_INVALID;
7934         }
7935         this.form.afterAction(this, false);
7936     },
7937     failure : function(response)
7938     {
7939         this.uploadComplete= true;
7940         if (this.haveProgress) {
7941             Roo.MessageBox.hide();
7942         }
7943         
7944         this.response = response;
7945         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7946         this.form.afterAction(this, false);
7947     },
7948     
7949     handleResponse : function(response){
7950         if(this.form.errorReader){
7951             var rs = this.form.errorReader.read(response);
7952             var errors = [];
7953             if(rs.records){
7954                 for(var i = 0, len = rs.records.length; i < len; i++) {
7955                     var r = rs.records[i];
7956                     errors[i] = r.data;
7957                 }
7958             }
7959             if(errors.length < 1){
7960                 errors = null;
7961             }
7962             return {
7963                 success : rs.success,
7964                 errors : errors
7965             };
7966         }
7967         var ret = false;
7968         try {
7969             ret = Roo.decode(response.responseText);
7970         } catch (e) {
7971             ret = {
7972                 success: false,
7973                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7974                 errors : []
7975             };
7976         }
7977         return ret;
7978         
7979     }
7980 });
7981
7982
7983 Roo.form.Action.Load = function(form, options){
7984     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7985     this.reader = this.form.reader;
7986 };
7987
7988 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7989     type : 'load',
7990
7991     run : function(){
7992         
7993         Roo.Ajax.request(Roo.apply(
7994                 this.createCallback(), {
7995                     method:this.getMethod(),
7996                     url:this.getUrl(false),
7997                     params:this.getParams()
7998         }));
7999     },
8000
8001     success : function(response){
8002         
8003         var result = this.processResponse(response);
8004         if(result === true || !result.success || !result.data){
8005             this.failureType = Roo.form.Action.LOAD_FAILURE;
8006             this.form.afterAction(this, false);
8007             return;
8008         }
8009         this.form.clearInvalid();
8010         this.form.setValues(result.data);
8011         this.form.afterAction(this, true);
8012     },
8013
8014     handleResponse : function(response){
8015         if(this.form.reader){
8016             var rs = this.form.reader.read(response);
8017             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8018             return {
8019                 success : rs.success,
8020                 data : data
8021             };
8022         }
8023         return Roo.decode(response.responseText);
8024     }
8025 });
8026
8027 Roo.form.Action.ACTION_TYPES = {
8028     'load' : Roo.form.Action.Load,
8029     'submit' : Roo.form.Action.Submit
8030 };/*
8031  * - LGPL
8032  *
8033  * form
8034  *
8035  */
8036
8037 /**
8038  * @class Roo.bootstrap.Form
8039  * @extends Roo.bootstrap.Component
8040  * Bootstrap Form class
8041  * @cfg {String} method  GET | POST (default POST)
8042  * @cfg {String} labelAlign top | left (default top)
8043  * @cfg {String} align left  | right - for navbars
8044  * @cfg {Boolean} loadMask load mask when submit (default true)
8045
8046  *
8047  * @constructor
8048  * Create a new Form
8049  * @param {Object} config The config object
8050  */
8051
8052
8053 Roo.bootstrap.Form = function(config){
8054     
8055     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8056     
8057     Roo.bootstrap.Form.popover.apply();
8058     
8059     this.addEvents({
8060         /**
8061          * @event clientvalidation
8062          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8063          * @param {Form} this
8064          * @param {Boolean} valid true if the form has passed client-side validation
8065          */
8066         clientvalidation: true,
8067         /**
8068          * @event beforeaction
8069          * Fires before any action is performed. Return false to cancel the action.
8070          * @param {Form} this
8071          * @param {Action} action The action to be performed
8072          */
8073         beforeaction: true,
8074         /**
8075          * @event actionfailed
8076          * Fires when an action fails.
8077          * @param {Form} this
8078          * @param {Action} action The action that failed
8079          */
8080         actionfailed : true,
8081         /**
8082          * @event actioncomplete
8083          * Fires when an action is completed.
8084          * @param {Form} this
8085          * @param {Action} action The action that completed
8086          */
8087         actioncomplete : true
8088     });
8089 };
8090
8091 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8092
8093      /**
8094      * @cfg {String} method
8095      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8096      */
8097     method : 'POST',
8098     /**
8099      * @cfg {String} url
8100      * The URL to use for form actions if one isn't supplied in the action options.
8101      */
8102     /**
8103      * @cfg {Boolean} fileUpload
8104      * Set to true if this form is a file upload.
8105      */
8106
8107     /**
8108      * @cfg {Object} baseParams
8109      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8110      */
8111
8112     /**
8113      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8114      */
8115     timeout: 30,
8116     /**
8117      * @cfg {Sting} align (left|right) for navbar forms
8118      */
8119     align : 'left',
8120
8121     // private
8122     activeAction : null,
8123
8124     /**
8125      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8126      * element by passing it or its id or mask the form itself by passing in true.
8127      * @type Mixed
8128      */
8129     waitMsgTarget : false,
8130
8131     loadMask : true,
8132     
8133     /**
8134      * @cfg {Boolean} errorMask (true|false) default false
8135      */
8136     errorMask : false,
8137     
8138     /**
8139      * @cfg {Number} maskOffset Default 100
8140      */
8141     maskOffset : 100,
8142     
8143     /**
8144      * @cfg {Boolean} maskBody
8145      */
8146     maskBody : false,
8147
8148     getAutoCreate : function(){
8149
8150         var cfg = {
8151             tag: 'form',
8152             method : this.method || 'POST',
8153             id : this.id || Roo.id(),
8154             cls : ''
8155         };
8156         if (this.parent().xtype.match(/^Nav/)) {
8157             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8158
8159         }
8160
8161         if (this.labelAlign == 'left' ) {
8162             cfg.cls += ' form-horizontal';
8163         }
8164
8165
8166         return cfg;
8167     },
8168     initEvents : function()
8169     {
8170         this.el.on('submit', this.onSubmit, this);
8171         // this was added as random key presses on the form where triggering form submit.
8172         this.el.on('keypress', function(e) {
8173             if (e.getCharCode() != 13) {
8174                 return true;
8175             }
8176             // we might need to allow it for textareas.. and some other items.
8177             // check e.getTarget().
8178
8179             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8180                 return true;
8181             }
8182
8183             Roo.log("keypress blocked");
8184
8185             e.preventDefault();
8186             return false;
8187         });
8188         
8189     },
8190     // private
8191     onSubmit : function(e){
8192         e.stopEvent();
8193     },
8194
8195      /**
8196      * Returns true if client-side validation on the form is successful.
8197      * @return Boolean
8198      */
8199     isValid : function(){
8200         var items = this.getItems();
8201         var valid = true;
8202         var target = false;
8203         
8204         items.each(function(f){
8205             
8206             if(f.validate()){
8207                 return;
8208             }
8209             
8210             Roo.log('invalid field: ' + f.name);
8211             
8212             valid = false;
8213
8214             if(!target && f.el.isVisible(true)){
8215                 target = f;
8216             }
8217            
8218         });
8219         
8220         if(this.errorMask && !valid){
8221             Roo.bootstrap.Form.popover.mask(this, target);
8222         }
8223         
8224         return valid;
8225     },
8226     
8227     /**
8228      * Returns true if any fields in this form have changed since their original load.
8229      * @return Boolean
8230      */
8231     isDirty : function(){
8232         var dirty = false;
8233         var items = this.getItems();
8234         items.each(function(f){
8235            if(f.isDirty()){
8236                dirty = true;
8237                return false;
8238            }
8239            return true;
8240         });
8241         return dirty;
8242     },
8243      /**
8244      * Performs a predefined action (submit or load) or custom actions you define on this form.
8245      * @param {String} actionName The name of the action type
8246      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8247      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8248      * accept other config options):
8249      * <pre>
8250 Property          Type             Description
8251 ----------------  ---------------  ----------------------------------------------------------------------------------
8252 url               String           The url for the action (defaults to the form's url)
8253 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8254 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8255 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8256                                    validate the form on the client (defaults to false)
8257      * </pre>
8258      * @return {BasicForm} this
8259      */
8260     doAction : function(action, options){
8261         if(typeof action == 'string'){
8262             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8263         }
8264         if(this.fireEvent('beforeaction', this, action) !== false){
8265             this.beforeAction(action);
8266             action.run.defer(100, action);
8267         }
8268         return this;
8269     },
8270
8271     // private
8272     beforeAction : function(action){
8273         var o = action.options;
8274         
8275         if(this.loadMask){
8276             
8277             if(this.maskBody){
8278                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8279             } else {
8280                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8281             }
8282         }
8283         // not really supported yet.. ??
8284
8285         //if(this.waitMsgTarget === true){
8286         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8287         //}else if(this.waitMsgTarget){
8288         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8289         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8290         //}else {
8291         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8292        // }
8293
8294     },
8295
8296     // private
8297     afterAction : function(action, success){
8298         this.activeAction = null;
8299         var o = action.options;
8300
8301         if(this.loadMask){
8302             
8303             if(this.maskBody){
8304                 Roo.get(document.body).unmask();
8305             } else {
8306                 this.el.unmask();
8307             }
8308         }
8309         
8310         //if(this.waitMsgTarget === true){
8311 //            this.el.unmask();
8312         //}else if(this.waitMsgTarget){
8313         //    this.waitMsgTarget.unmask();
8314         //}else{
8315         //    Roo.MessageBox.updateProgress(1);
8316         //    Roo.MessageBox.hide();
8317        // }
8318         //
8319         if(success){
8320             if(o.reset){
8321                 this.reset();
8322             }
8323             Roo.callback(o.success, o.scope, [this, action]);
8324             this.fireEvent('actioncomplete', this, action);
8325
8326         }else{
8327
8328             // failure condition..
8329             // we have a scenario where updates need confirming.
8330             // eg. if a locking scenario exists..
8331             // we look for { errors : { needs_confirm : true }} in the response.
8332             if (
8333                 (typeof(action.result) != 'undefined')  &&
8334                 (typeof(action.result.errors) != 'undefined')  &&
8335                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8336            ){
8337                 var _t = this;
8338                 Roo.log("not supported yet");
8339                  /*
8340
8341                 Roo.MessageBox.confirm(
8342                     "Change requires confirmation",
8343                     action.result.errorMsg,
8344                     function(r) {
8345                         if (r != 'yes') {
8346                             return;
8347                         }
8348                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8349                     }
8350
8351                 );
8352                 */
8353
8354
8355                 return;
8356             }
8357
8358             Roo.callback(o.failure, o.scope, [this, action]);
8359             // show an error message if no failed handler is set..
8360             if (!this.hasListener('actionfailed')) {
8361                 Roo.log("need to add dialog support");
8362                 /*
8363                 Roo.MessageBox.alert("Error",
8364                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8365                         action.result.errorMsg :
8366                         "Saving Failed, please check your entries or try again"
8367                 );
8368                 */
8369             }
8370
8371             this.fireEvent('actionfailed', this, action);
8372         }
8373
8374     },
8375     /**
8376      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8377      * @param {String} id The value to search for
8378      * @return Field
8379      */
8380     findField : function(id){
8381         var items = this.getItems();
8382         var field = items.get(id);
8383         if(!field){
8384              items.each(function(f){
8385                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8386                     field = f;
8387                     return false;
8388                 }
8389                 return true;
8390             });
8391         }
8392         return field || null;
8393     },
8394      /**
8395      * Mark fields in this form invalid in bulk.
8396      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8397      * @return {BasicForm} this
8398      */
8399     markInvalid : function(errors){
8400         if(errors instanceof Array){
8401             for(var i = 0, len = errors.length; i < len; i++){
8402                 var fieldError = errors[i];
8403                 var f = this.findField(fieldError.id);
8404                 if(f){
8405                     f.markInvalid(fieldError.msg);
8406                 }
8407             }
8408         }else{
8409             var field, id;
8410             for(id in errors){
8411                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8412                     field.markInvalid(errors[id]);
8413                 }
8414             }
8415         }
8416         //Roo.each(this.childForms || [], function (f) {
8417         //    f.markInvalid(errors);
8418         //});
8419
8420         return this;
8421     },
8422
8423     /**
8424      * Set values for fields in this form in bulk.
8425      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8426      * @return {BasicForm} this
8427      */
8428     setValues : function(values){
8429         if(values instanceof Array){ // array of objects
8430             for(var i = 0, len = values.length; i < len; i++){
8431                 var v = values[i];
8432                 var f = this.findField(v.id);
8433                 if(f){
8434                     f.setValue(v.value);
8435                     if(this.trackResetOnLoad){
8436                         f.originalValue = f.getValue();
8437                     }
8438                 }
8439             }
8440         }else{ // object hash
8441             var field, id;
8442             for(id in values){
8443                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8444
8445                     if (field.setFromData &&
8446                         field.valueField &&
8447                         field.displayField &&
8448                         // combos' with local stores can
8449                         // be queried via setValue()
8450                         // to set their value..
8451                         (field.store && !field.store.isLocal)
8452                         ) {
8453                         // it's a combo
8454                         var sd = { };
8455                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8456                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8457                         field.setFromData(sd);
8458
8459                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8460                         
8461                         field.setFromData(values);
8462                         
8463                     } else {
8464                         field.setValue(values[id]);
8465                     }
8466
8467
8468                     if(this.trackResetOnLoad){
8469                         field.originalValue = field.getValue();
8470                     }
8471                 }
8472             }
8473         }
8474
8475         //Roo.each(this.childForms || [], function (f) {
8476         //    f.setValues(values);
8477         //});
8478
8479         return this;
8480     },
8481
8482     /**
8483      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8484      * they are returned as an array.
8485      * @param {Boolean} asString
8486      * @return {Object}
8487      */
8488     getValues : function(asString){
8489         //if (this.childForms) {
8490             // copy values from the child forms
8491         //    Roo.each(this.childForms, function (f) {
8492         //        this.setValues(f.getValues());
8493         //    }, this);
8494         //}
8495
8496
8497
8498         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8499         if(asString === true){
8500             return fs;
8501         }
8502         return Roo.urlDecode(fs);
8503     },
8504
8505     /**
8506      * Returns the fields in this form as an object with key/value pairs.
8507      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8508      * @return {Object}
8509      */
8510     getFieldValues : function(with_hidden)
8511     {
8512         var items = this.getItems();
8513         var ret = {};
8514         items.each(function(f){
8515             
8516             if (!f.getName()) {
8517                 return;
8518             }
8519             
8520             var v = f.getValue();
8521             
8522             if (f.inputType =='radio') {
8523                 if (typeof(ret[f.getName()]) == 'undefined') {
8524                     ret[f.getName()] = ''; // empty..
8525                 }
8526
8527                 if (!f.el.dom.checked) {
8528                     return;
8529
8530                 }
8531                 v = f.el.dom.value;
8532
8533             }
8534             
8535             if(f.xtype == 'MoneyField'){
8536                 ret[f.currencyName] = f.getCurrency();
8537             }
8538
8539             // not sure if this supported any more..
8540             if ((typeof(v) == 'object') && f.getRawValue) {
8541                 v = f.getRawValue() ; // dates..
8542             }
8543             // combo boxes where name != hiddenName...
8544             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8545                 ret[f.name] = f.getRawValue();
8546             }
8547             ret[f.getName()] = v;
8548         });
8549
8550         return ret;
8551     },
8552
8553     /**
8554      * Clears all invalid messages in this form.
8555      * @return {BasicForm} this
8556      */
8557     clearInvalid : function(){
8558         var items = this.getItems();
8559
8560         items.each(function(f){
8561            f.clearInvalid();
8562         });
8563
8564         return this;
8565     },
8566
8567     /**
8568      * Resets this form.
8569      * @return {BasicForm} this
8570      */
8571     reset : function(){
8572         var items = this.getItems();
8573         items.each(function(f){
8574             f.reset();
8575         });
8576
8577         Roo.each(this.childForms || [], function (f) {
8578             f.reset();
8579         });
8580
8581
8582         return this;
8583     },
8584     
8585     getItems : function()
8586     {
8587         var r=new Roo.util.MixedCollection(false, function(o){
8588             return o.id || (o.id = Roo.id());
8589         });
8590         var iter = function(el) {
8591             if (el.inputEl) {
8592                 r.add(el);
8593             }
8594             if (!el.items) {
8595                 return;
8596             }
8597             Roo.each(el.items,function(e) {
8598                 iter(e);
8599             });
8600         };
8601
8602         iter(this);
8603         return r;
8604     },
8605     
8606     hideFields : function(items)
8607     {
8608         Roo.each(items, function(i){
8609             
8610             var f = this.findField(i);
8611             
8612             if(!f){
8613                 return;
8614             }
8615             
8616             f.hide();
8617             
8618         }, this);
8619     },
8620     
8621     showFields : function(items)
8622     {
8623         Roo.each(items, function(i){
8624             
8625             var f = this.findField(i);
8626             
8627             if(!f){
8628                 return;
8629             }
8630             
8631             f.show();
8632             
8633         }, this);
8634     }
8635
8636 });
8637
8638 Roo.apply(Roo.bootstrap.Form, {
8639     
8640     popover : {
8641         
8642         padding : 5,
8643         
8644         isApplied : false,
8645         
8646         isMasked : false,
8647         
8648         form : false,
8649         
8650         target : false,
8651         
8652         toolTip : false,
8653         
8654         intervalID : false,
8655         
8656         maskEl : false,
8657         
8658         apply : function()
8659         {
8660             if(this.isApplied){
8661                 return;
8662             }
8663             
8664             this.maskEl = {
8665                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8666                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8667                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8668                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8669             };
8670             
8671             this.maskEl.top.enableDisplayMode("block");
8672             this.maskEl.left.enableDisplayMode("block");
8673             this.maskEl.bottom.enableDisplayMode("block");
8674             this.maskEl.right.enableDisplayMode("block");
8675             
8676             this.toolTip = new Roo.bootstrap.Tooltip({
8677                 cls : 'roo-form-error-popover',
8678                 alignment : {
8679                     'left' : ['r-l', [-2,0], 'right'],
8680                     'right' : ['l-r', [2,0], 'left'],
8681                     'bottom' : ['tl-bl', [0,2], 'top'],
8682                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8683                 }
8684             });
8685             
8686             this.toolTip.render(Roo.get(document.body));
8687
8688             this.toolTip.el.enableDisplayMode("block");
8689             
8690             Roo.get(document.body).on('click', function(){
8691                 this.unmask();
8692             }, this);
8693             
8694             Roo.get(document.body).on('touchstart', function(){
8695                 this.unmask();
8696             }, this);
8697             
8698             this.isApplied = true
8699         },
8700         
8701         mask : function(form, target)
8702         {
8703             this.form = form;
8704             
8705             this.target = target;
8706             
8707             if(!this.form.errorMask || !target.el){
8708                 return;
8709             }
8710             
8711             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8712             
8713             Roo.log(scrollable);
8714             
8715             var ot = this.target.el.calcOffsetsTo(scrollable);
8716             
8717             var scrollTo = ot[1] - this.form.maskOffset;
8718             
8719             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8720             
8721             scrollable.scrollTo('top', scrollTo);
8722             
8723             var box = this.target.el.getBox();
8724             Roo.log(box);
8725             var zIndex = Roo.bootstrap.Modal.zIndex++;
8726
8727             
8728             this.maskEl.top.setStyle('position', 'absolute');
8729             this.maskEl.top.setStyle('z-index', zIndex);
8730             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8731             this.maskEl.top.setLeft(0);
8732             this.maskEl.top.setTop(0);
8733             this.maskEl.top.show();
8734             
8735             this.maskEl.left.setStyle('position', 'absolute');
8736             this.maskEl.left.setStyle('z-index', zIndex);
8737             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8738             this.maskEl.left.setLeft(0);
8739             this.maskEl.left.setTop(box.y - this.padding);
8740             this.maskEl.left.show();
8741
8742             this.maskEl.bottom.setStyle('position', 'absolute');
8743             this.maskEl.bottom.setStyle('z-index', zIndex);
8744             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8745             this.maskEl.bottom.setLeft(0);
8746             this.maskEl.bottom.setTop(box.bottom + this.padding);
8747             this.maskEl.bottom.show();
8748
8749             this.maskEl.right.setStyle('position', 'absolute');
8750             this.maskEl.right.setStyle('z-index', zIndex);
8751             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8752             this.maskEl.right.setLeft(box.right + this.padding);
8753             this.maskEl.right.setTop(box.y - this.padding);
8754             this.maskEl.right.show();
8755
8756             this.toolTip.bindEl = this.target.el;
8757
8758             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8759
8760             var tip = this.target.blankText;
8761
8762             if(this.target.getValue() !== '' ) {
8763                 
8764                 if (this.target.invalidText.length) {
8765                     tip = this.target.invalidText;
8766                 } else if (this.target.regexText.length){
8767                     tip = this.target.regexText;
8768                 }
8769             }
8770
8771             this.toolTip.show(tip);
8772
8773             this.intervalID = window.setInterval(function() {
8774                 Roo.bootstrap.Form.popover.unmask();
8775             }, 10000);
8776
8777             window.onwheel = function(){ return false;};
8778             
8779             (function(){ this.isMasked = true; }).defer(500, this);
8780             
8781         },
8782         
8783         unmask : function()
8784         {
8785             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8786                 return;
8787             }
8788             
8789             this.maskEl.top.setStyle('position', 'absolute');
8790             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8791             this.maskEl.top.hide();
8792
8793             this.maskEl.left.setStyle('position', 'absolute');
8794             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8795             this.maskEl.left.hide();
8796
8797             this.maskEl.bottom.setStyle('position', 'absolute');
8798             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8799             this.maskEl.bottom.hide();
8800
8801             this.maskEl.right.setStyle('position', 'absolute');
8802             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8803             this.maskEl.right.hide();
8804             
8805             this.toolTip.hide();
8806             
8807             this.toolTip.el.hide();
8808             
8809             window.onwheel = function(){ return true;};
8810             
8811             if(this.intervalID){
8812                 window.clearInterval(this.intervalID);
8813                 this.intervalID = false;
8814             }
8815             
8816             this.isMasked = false;
8817             
8818         }
8819         
8820     }
8821     
8822 });
8823
8824 /*
8825  * Based on:
8826  * Ext JS Library 1.1.1
8827  * Copyright(c) 2006-2007, Ext JS, LLC.
8828  *
8829  * Originally Released Under LGPL - original licence link has changed is not relivant.
8830  *
8831  * Fork - LGPL
8832  * <script type="text/javascript">
8833  */
8834 /**
8835  * @class Roo.form.VTypes
8836  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8837  * @singleton
8838  */
8839 Roo.form.VTypes = function(){
8840     // closure these in so they are only created once.
8841     var alpha = /^[a-zA-Z_]+$/;
8842     var alphanum = /^[a-zA-Z0-9_]+$/;
8843     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8844     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8845
8846     // All these messages and functions are configurable
8847     return {
8848         /**
8849          * The function used to validate email addresses
8850          * @param {String} value The email address
8851          */
8852         'email' : function(v){
8853             return email.test(v);
8854         },
8855         /**
8856          * The error text to display when the email validation function returns false
8857          * @type String
8858          */
8859         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8860         /**
8861          * The keystroke filter mask to be applied on email input
8862          * @type RegExp
8863          */
8864         'emailMask' : /[a-z0-9_\.\-@]/i,
8865
8866         /**
8867          * The function used to validate URLs
8868          * @param {String} value The URL
8869          */
8870         'url' : function(v){
8871             return url.test(v);
8872         },
8873         /**
8874          * The error text to display when the url validation function returns false
8875          * @type String
8876          */
8877         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8878         
8879         /**
8880          * The function used to validate alpha values
8881          * @param {String} value The value
8882          */
8883         'alpha' : function(v){
8884             return alpha.test(v);
8885         },
8886         /**
8887          * The error text to display when the alpha validation function returns false
8888          * @type String
8889          */
8890         'alphaText' : 'This field should only contain letters and _',
8891         /**
8892          * The keystroke filter mask to be applied on alpha input
8893          * @type RegExp
8894          */
8895         'alphaMask' : /[a-z_]/i,
8896
8897         /**
8898          * The function used to validate alphanumeric values
8899          * @param {String} value The value
8900          */
8901         'alphanum' : function(v){
8902             return alphanum.test(v);
8903         },
8904         /**
8905          * The error text to display when the alphanumeric validation function returns false
8906          * @type String
8907          */
8908         'alphanumText' : 'This field should only contain letters, numbers and _',
8909         /**
8910          * The keystroke filter mask to be applied on alphanumeric input
8911          * @type RegExp
8912          */
8913         'alphanumMask' : /[a-z0-9_]/i
8914     };
8915 }();/*
8916  * - LGPL
8917  *
8918  * Input
8919  * 
8920  */
8921
8922 /**
8923  * @class Roo.bootstrap.Input
8924  * @extends Roo.bootstrap.Component
8925  * Bootstrap Input class
8926  * @cfg {Boolean} disabled is it disabled
8927  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8928  * @cfg {String} name name of the input
8929  * @cfg {string} fieldLabel - the label associated
8930  * @cfg {string} placeholder - placeholder to put in text.
8931  * @cfg {string}  before - input group add on before
8932  * @cfg {string} after - input group add on after
8933  * @cfg {string} size - (lg|sm) or leave empty..
8934  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8935  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8936  * @cfg {Number} md colspan out of 12 for computer-sized screens
8937  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8938  * @cfg {string} value default value of the input
8939  * @cfg {Number} labelWidth set the width of label 
8940  * @cfg {Number} labellg set the width of label (1-12)
8941  * @cfg {Number} labelmd set the width of label (1-12)
8942  * @cfg {Number} labelsm set the width of label (1-12)
8943  * @cfg {Number} labelxs set the width of label (1-12)
8944  * @cfg {String} labelAlign (top|left)
8945  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8946  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8947  * @cfg {String} indicatorpos (left|right) default left
8948  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8949  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8950
8951  * @cfg {String} align (left|center|right) Default left
8952  * @cfg {Boolean} forceFeedback (true|false) Default false
8953  * 
8954  * @constructor
8955  * Create a new Input
8956  * @param {Object} config The config object
8957  */
8958
8959 Roo.bootstrap.Input = function(config){
8960     
8961     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8962     
8963     this.addEvents({
8964         /**
8965          * @event focus
8966          * Fires when this field receives input focus.
8967          * @param {Roo.form.Field} this
8968          */
8969         focus : true,
8970         /**
8971          * @event blur
8972          * Fires when this field loses input focus.
8973          * @param {Roo.form.Field} this
8974          */
8975         blur : true,
8976         /**
8977          * @event specialkey
8978          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8979          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8980          * @param {Roo.form.Field} this
8981          * @param {Roo.EventObject} e The event object
8982          */
8983         specialkey : true,
8984         /**
8985          * @event change
8986          * Fires just before the field blurs if the field value has changed.
8987          * @param {Roo.form.Field} this
8988          * @param {Mixed} newValue The new value
8989          * @param {Mixed} oldValue The original value
8990          */
8991         change : true,
8992         /**
8993          * @event invalid
8994          * Fires after the field has been marked as invalid.
8995          * @param {Roo.form.Field} this
8996          * @param {String} msg The validation message
8997          */
8998         invalid : true,
8999         /**
9000          * @event valid
9001          * Fires after the field has been validated with no errors.
9002          * @param {Roo.form.Field} this
9003          */
9004         valid : true,
9005          /**
9006          * @event keyup
9007          * Fires after the key up
9008          * @param {Roo.form.Field} this
9009          * @param {Roo.EventObject}  e The event Object
9010          */
9011         keyup : true
9012     });
9013 };
9014
9015 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
9016      /**
9017      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9018       automatic validation (defaults to "keyup").
9019      */
9020     validationEvent : "keyup",
9021      /**
9022      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9023      */
9024     validateOnBlur : true,
9025     /**
9026      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9027      */
9028     validationDelay : 250,
9029      /**
9030      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9031      */
9032     focusClass : "x-form-focus",  // not needed???
9033     
9034        
9035     /**
9036      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9037      */
9038     invalidClass : "has-warning",
9039     
9040     /**
9041      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9042      */
9043     validClass : "has-success",
9044     
9045     /**
9046      * @cfg {Boolean} hasFeedback (true|false) default true
9047      */
9048     hasFeedback : true,
9049     
9050     /**
9051      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9052      */
9053     invalidFeedbackClass : "glyphicon-warning-sign",
9054     
9055     /**
9056      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9057      */
9058     validFeedbackClass : "glyphicon-ok",
9059     
9060     /**
9061      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9062      */
9063     selectOnFocus : false,
9064     
9065      /**
9066      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9067      */
9068     maskRe : null,
9069        /**
9070      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9071      */
9072     vtype : null,
9073     
9074       /**
9075      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9076      */
9077     disableKeyFilter : false,
9078     
9079        /**
9080      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9081      */
9082     disabled : false,
9083      /**
9084      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9085      */
9086     allowBlank : true,
9087     /**
9088      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9089      */
9090     blankText : "Please complete this mandatory field",
9091     
9092      /**
9093      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9094      */
9095     minLength : 0,
9096     /**
9097      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9098      */
9099     maxLength : Number.MAX_VALUE,
9100     /**
9101      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9102      */
9103     minLengthText : "The minimum length for this field is {0}",
9104     /**
9105      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9106      */
9107     maxLengthText : "The maximum length for this field is {0}",
9108   
9109     
9110     /**
9111      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9112      * If available, this function will be called only after the basic validators all return true, and will be passed the
9113      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9114      */
9115     validator : null,
9116     /**
9117      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9118      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9119      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9120      */
9121     regex : null,
9122     /**
9123      * @cfg {String} regexText -- Depricated - use Invalid Text
9124      */
9125     regexText : "",
9126     
9127     /**
9128      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9129      */
9130     invalidText : "",
9131     
9132     
9133     
9134     autocomplete: false,
9135     
9136     
9137     fieldLabel : '',
9138     inputType : 'text',
9139     
9140     name : false,
9141     placeholder: false,
9142     before : false,
9143     after : false,
9144     size : false,
9145     hasFocus : false,
9146     preventMark: false,
9147     isFormField : true,
9148     value : '',
9149     labelWidth : 2,
9150     labelAlign : false,
9151     readOnly : false,
9152     align : false,
9153     formatedValue : false,
9154     forceFeedback : false,
9155     
9156     indicatorpos : 'left',
9157     
9158     labellg : 0,
9159     labelmd : 0,
9160     labelsm : 0,
9161     labelxs : 0,
9162     
9163     capture : '',
9164     accept : '',
9165     
9166     parentLabelAlign : function()
9167     {
9168         var parent = this;
9169         while (parent.parent()) {
9170             parent = parent.parent();
9171             if (typeof(parent.labelAlign) !='undefined') {
9172                 return parent.labelAlign;
9173             }
9174         }
9175         return 'left';
9176         
9177     },
9178     
9179     getAutoCreate : function()
9180     {
9181         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9182         
9183         var id = Roo.id();
9184         
9185         var cfg = {};
9186         
9187         if(this.inputType != 'hidden'){
9188             cfg.cls = 'form-group' //input-group
9189         }
9190         
9191         var input =  {
9192             tag: 'input',
9193             id : id,
9194             type : this.inputType,
9195             value : this.value,
9196             cls : 'form-control',
9197             placeholder : this.placeholder || '',
9198             autocomplete : this.autocomplete || 'new-password'
9199         };
9200         
9201         if(this.capture.length){
9202             input.capture = this.capture;
9203         }
9204         
9205         if(this.accept.length){
9206             input.accept = this.accept + "/*";
9207         }
9208         
9209         if(this.align){
9210             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9211         }
9212         
9213         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9214             input.maxLength = this.maxLength;
9215         }
9216         
9217         if (this.disabled) {
9218             input.disabled=true;
9219         }
9220         
9221         if (this.readOnly) {
9222             input.readonly=true;
9223         }
9224         
9225         if (this.name) {
9226             input.name = this.name;
9227         }
9228         
9229         if (this.size) {
9230             input.cls += ' input-' + this.size;
9231         }
9232         
9233         var settings=this;
9234         ['xs','sm','md','lg'].map(function(size){
9235             if (settings[size]) {
9236                 cfg.cls += ' col-' + size + '-' + settings[size];
9237             }
9238         });
9239         
9240         var inputblock = input;
9241         
9242         var feedback = {
9243             tag: 'span',
9244             cls: 'glyphicon form-control-feedback'
9245         };
9246             
9247         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9248             
9249             inputblock = {
9250                 cls : 'has-feedback',
9251                 cn :  [
9252                     input,
9253                     feedback
9254                 ] 
9255             };  
9256         }
9257         
9258         if (this.before || this.after) {
9259             
9260             inputblock = {
9261                 cls : 'input-group',
9262                 cn :  [] 
9263             };
9264             
9265             if (this.before && typeof(this.before) == 'string') {
9266                 
9267                 inputblock.cn.push({
9268                     tag :'span',
9269                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9270                     html : this.before
9271                 });
9272             }
9273             if (this.before && typeof(this.before) == 'object') {
9274                 this.before = Roo.factory(this.before);
9275                 
9276                 inputblock.cn.push({
9277                     tag :'span',
9278                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9279                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9280                 });
9281             }
9282             
9283             inputblock.cn.push(input);
9284             
9285             if (this.after && typeof(this.after) == 'string') {
9286                 inputblock.cn.push({
9287                     tag :'span',
9288                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9289                     html : this.after
9290                 });
9291             }
9292             if (this.after && typeof(this.after) == 'object') {
9293                 this.after = Roo.factory(this.after);
9294                 
9295                 inputblock.cn.push({
9296                     tag :'span',
9297                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9298                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9299                 });
9300             }
9301             
9302             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9303                 inputblock.cls += ' has-feedback';
9304                 inputblock.cn.push(feedback);
9305             }
9306         };
9307         var indicator = {
9308             tag : 'i',
9309             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9310             tooltip : 'This field is required'
9311         };
9312         if (Roo.bootstrap.version == 4) {
9313             indicator = {
9314                 tag : 'i',
9315                 style : 'display-none'
9316             };
9317         }
9318         if (align ==='left' && this.fieldLabel.length) {
9319             
9320             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9321             
9322             cfg.cn = [
9323                 indicator,
9324                 {
9325                     tag: 'label',
9326                     'for' :  id,
9327                     cls : 'control-label col-form-label',
9328                     html : this.fieldLabel
9329
9330                 },
9331                 {
9332                     cls : "", 
9333                     cn: [
9334                         inputblock
9335                     ]
9336                 }
9337             ];
9338             
9339             var labelCfg = cfg.cn[1];
9340             var contentCfg = cfg.cn[2];
9341             
9342             if(this.indicatorpos == 'right'){
9343                 cfg.cn = [
9344                     {
9345                         tag: 'label',
9346                         'for' :  id,
9347                         cls : 'control-label col-form-label',
9348                         cn : [
9349                             {
9350                                 tag : 'span',
9351                                 html : this.fieldLabel
9352                             },
9353                             indicator
9354                         ]
9355                     },
9356                     {
9357                         cls : "",
9358                         cn: [
9359                             inputblock
9360                         ]
9361                     }
9362
9363                 ];
9364                 
9365                 labelCfg = cfg.cn[0];
9366                 contentCfg = cfg.cn[1];
9367             
9368             }
9369             
9370             if(this.labelWidth > 12){
9371                 labelCfg.style = "width: " + this.labelWidth + 'px';
9372             }
9373             
9374             if(this.labelWidth < 13 && this.labelmd == 0){
9375                 this.labelmd = this.labelWidth;
9376             }
9377             
9378             if(this.labellg > 0){
9379                 labelCfg.cls += ' col-lg-' + this.labellg;
9380                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9381             }
9382             
9383             if(this.labelmd > 0){
9384                 labelCfg.cls += ' col-md-' + this.labelmd;
9385                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9386             }
9387             
9388             if(this.labelsm > 0){
9389                 labelCfg.cls += ' col-sm-' + this.labelsm;
9390                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9391             }
9392             
9393             if(this.labelxs > 0){
9394                 labelCfg.cls += ' col-xs-' + this.labelxs;
9395                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9396             }
9397             
9398             
9399         } else if ( this.fieldLabel.length) {
9400                 
9401             cfg.cn = [
9402                 {
9403                     tag : 'i',
9404                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9405                     tooltip : 'This field is required'
9406                 },
9407                 {
9408                     tag: 'label',
9409                    //cls : 'input-group-addon',
9410                     html : this.fieldLabel
9411
9412                 },
9413
9414                inputblock
9415
9416            ];
9417            
9418            if(this.indicatorpos == 'right'){
9419                 
9420                 cfg.cn = [
9421                     {
9422                         tag: 'label',
9423                        //cls : 'input-group-addon',
9424                         html : this.fieldLabel
9425
9426                     },
9427                     {
9428                         tag : 'i',
9429                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9430                         tooltip : 'This field is required'
9431                     },
9432
9433                    inputblock
9434
9435                ];
9436
9437             }
9438
9439         } else {
9440             
9441             cfg.cn = [
9442
9443                     inputblock
9444
9445             ];
9446                 
9447                 
9448         };
9449         
9450         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9451            cfg.cls += ' navbar-form';
9452         }
9453         
9454         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9455             // on BS4 we do this only if not form 
9456             cfg.cls += ' navbar-form';
9457             cfg.tag = 'li';
9458         }
9459         
9460         return cfg;
9461         
9462     },
9463     /**
9464      * return the real input element.
9465      */
9466     inputEl: function ()
9467     {
9468         return this.el.select('input.form-control',true).first();
9469     },
9470     
9471     tooltipEl : function()
9472     {
9473         return this.inputEl();
9474     },
9475     
9476     indicatorEl : function()
9477     {
9478         if (Roo.bootstrap.version == 4) {
9479             return false; // not enabled in v4 yet.
9480         }
9481         
9482         var indicator = this.el.select('i.roo-required-indicator',true).first();
9483         
9484         if(!indicator){
9485             return false;
9486         }
9487         
9488         return indicator;
9489         
9490     },
9491     
9492     setDisabled : function(v)
9493     {
9494         var i  = this.inputEl().dom;
9495         if (!v) {
9496             i.removeAttribute('disabled');
9497             return;
9498             
9499         }
9500         i.setAttribute('disabled','true');
9501     },
9502     initEvents : function()
9503     {
9504           
9505         this.inputEl().on("keydown" , this.fireKey,  this);
9506         this.inputEl().on("focus", this.onFocus,  this);
9507         this.inputEl().on("blur", this.onBlur,  this);
9508         
9509         this.inputEl().relayEvent('keyup', this);
9510         
9511         this.indicator = this.indicatorEl();
9512         
9513         if(this.indicator){
9514             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9515         }
9516  
9517         // reference to original value for reset
9518         this.originalValue = this.getValue();
9519         //Roo.form.TextField.superclass.initEvents.call(this);
9520         if(this.validationEvent == 'keyup'){
9521             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9522             this.inputEl().on('keyup', this.filterValidation, this);
9523         }
9524         else if(this.validationEvent !== false){
9525             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9526         }
9527         
9528         if(this.selectOnFocus){
9529             this.on("focus", this.preFocus, this);
9530             
9531         }
9532         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9533             this.inputEl().on("keypress", this.filterKeys, this);
9534         } else {
9535             this.inputEl().relayEvent('keypress', this);
9536         }
9537        /* if(this.grow){
9538             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9539             this.el.on("click", this.autoSize,  this);
9540         }
9541         */
9542         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9543             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9544         }
9545         
9546         if (typeof(this.before) == 'object') {
9547             this.before.render(this.el.select('.roo-input-before',true).first());
9548         }
9549         if (typeof(this.after) == 'object') {
9550             this.after.render(this.el.select('.roo-input-after',true).first());
9551         }
9552         
9553         this.inputEl().on('change', this.onChange, this);
9554         
9555     },
9556     filterValidation : function(e){
9557         if(!e.isNavKeyPress()){
9558             this.validationTask.delay(this.validationDelay);
9559         }
9560     },
9561      /**
9562      * Validates the field value
9563      * @return {Boolean} True if the value is valid, else false
9564      */
9565     validate : function(){
9566         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9567         if(this.disabled || this.validateValue(this.getRawValue())){
9568             this.markValid();
9569             return true;
9570         }
9571         
9572         this.markInvalid();
9573         return false;
9574     },
9575     
9576     
9577     /**
9578      * Validates a value according to the field's validation rules and marks the field as invalid
9579      * if the validation fails
9580      * @param {Mixed} value The value to validate
9581      * @return {Boolean} True if the value is valid, else false
9582      */
9583     validateValue : function(value)
9584     {
9585         if(this.getVisibilityEl().hasClass('hidden')){
9586             return true;
9587         }
9588         
9589         if(value.length < 1)  { // if it's blank
9590             if(this.allowBlank){
9591                 return true;
9592             }
9593             return false;
9594         }
9595         
9596         if(value.length < this.minLength){
9597             return false;
9598         }
9599         if(value.length > this.maxLength){
9600             return false;
9601         }
9602         if(this.vtype){
9603             var vt = Roo.form.VTypes;
9604             if(!vt[this.vtype](value, this)){
9605                 return false;
9606             }
9607         }
9608         if(typeof this.validator == "function"){
9609             var msg = this.validator(value);
9610             if(msg !== true){
9611                 return false;
9612             }
9613             if (typeof(msg) == 'string') {
9614                 this.invalidText = msg;
9615             }
9616         }
9617         
9618         if(this.regex && !this.regex.test(value)){
9619             return false;
9620         }
9621         
9622         return true;
9623     },
9624     
9625      // private
9626     fireKey : function(e){
9627         //Roo.log('field ' + e.getKey());
9628         if(e.isNavKeyPress()){
9629             this.fireEvent("specialkey", this, e);
9630         }
9631     },
9632     focus : function (selectText){
9633         if(this.rendered){
9634             this.inputEl().focus();
9635             if(selectText === true){
9636                 this.inputEl().dom.select();
9637             }
9638         }
9639         return this;
9640     } ,
9641     
9642     onFocus : function(){
9643         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9644            // this.el.addClass(this.focusClass);
9645         }
9646         if(!this.hasFocus){
9647             this.hasFocus = true;
9648             this.startValue = this.getValue();
9649             this.fireEvent("focus", this);
9650         }
9651     },
9652     
9653     beforeBlur : Roo.emptyFn,
9654
9655     
9656     // private
9657     onBlur : function(){
9658         this.beforeBlur();
9659         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9660             //this.el.removeClass(this.focusClass);
9661         }
9662         this.hasFocus = false;
9663         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9664             this.validate();
9665         }
9666         var v = this.getValue();
9667         if(String(v) !== String(this.startValue)){
9668             this.fireEvent('change', this, v, this.startValue);
9669         }
9670         this.fireEvent("blur", this);
9671     },
9672     
9673     onChange : function(e)
9674     {
9675         var v = this.getValue();
9676         if(String(v) !== String(this.startValue)){
9677             this.fireEvent('change', this, v, this.startValue);
9678         }
9679         
9680     },
9681     
9682     /**
9683      * Resets the current field value to the originally loaded value and clears any validation messages
9684      */
9685     reset : function(){
9686         this.setValue(this.originalValue);
9687         this.validate();
9688     },
9689      /**
9690      * Returns the name of the field
9691      * @return {Mixed} name The name field
9692      */
9693     getName: function(){
9694         return this.name;
9695     },
9696      /**
9697      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9698      * @return {Mixed} value The field value
9699      */
9700     getValue : function(){
9701         
9702         var v = this.inputEl().getValue();
9703         
9704         return v;
9705     },
9706     /**
9707      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9708      * @return {Mixed} value The field value
9709      */
9710     getRawValue : function(){
9711         var v = this.inputEl().getValue();
9712         
9713         return v;
9714     },
9715     
9716     /**
9717      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9718      * @param {Mixed} value The value to set
9719      */
9720     setRawValue : function(v){
9721         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9722     },
9723     
9724     selectText : function(start, end){
9725         var v = this.getRawValue();
9726         if(v.length > 0){
9727             start = start === undefined ? 0 : start;
9728             end = end === undefined ? v.length : end;
9729             var d = this.inputEl().dom;
9730             if(d.setSelectionRange){
9731                 d.setSelectionRange(start, end);
9732             }else if(d.createTextRange){
9733                 var range = d.createTextRange();
9734                 range.moveStart("character", start);
9735                 range.moveEnd("character", v.length-end);
9736                 range.select();
9737             }
9738         }
9739     },
9740     
9741     /**
9742      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9743      * @param {Mixed} value The value to set
9744      */
9745     setValue : function(v){
9746         this.value = v;
9747         if(this.rendered){
9748             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9749             this.validate();
9750         }
9751     },
9752     
9753     /*
9754     processValue : function(value){
9755         if(this.stripCharsRe){
9756             var newValue = value.replace(this.stripCharsRe, '');
9757             if(newValue !== value){
9758                 this.setRawValue(newValue);
9759                 return newValue;
9760             }
9761         }
9762         return value;
9763     },
9764   */
9765     preFocus : function(){
9766         
9767         if(this.selectOnFocus){
9768             this.inputEl().dom.select();
9769         }
9770     },
9771     filterKeys : function(e){
9772         var k = e.getKey();
9773         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9774             return;
9775         }
9776         var c = e.getCharCode(), cc = String.fromCharCode(c);
9777         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9778             return;
9779         }
9780         if(!this.maskRe.test(cc)){
9781             e.stopEvent();
9782         }
9783     },
9784      /**
9785      * Clear any invalid styles/messages for this field
9786      */
9787     clearInvalid : function(){
9788         
9789         if(!this.el || this.preventMark){ // not rendered
9790             return;
9791         }
9792         
9793         
9794         this.el.removeClass([this.invalidClass, 'is-invalid']);
9795         
9796         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9797             
9798             var feedback = this.el.select('.form-control-feedback', true).first();
9799             
9800             if(feedback){
9801                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9802             }
9803             
9804         }
9805         
9806         if(this.indicator){
9807             this.indicator.removeClass('visible');
9808             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9809         }
9810         
9811         this.fireEvent('valid', this);
9812     },
9813     
9814      /**
9815      * Mark this field as valid
9816      */
9817     markValid : function()
9818     {
9819         if(!this.el  || this.preventMark){ // not rendered...
9820             return;
9821         }
9822         
9823         this.el.removeClass([this.invalidClass, this.validClass]);
9824         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9825
9826         var feedback = this.el.select('.form-control-feedback', true).first();
9827             
9828         if(feedback){
9829             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9830         }
9831         
9832         if(this.indicator){
9833             this.indicator.removeClass('visible');
9834             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9835         }
9836         
9837         if(this.disabled){
9838             return;
9839         }
9840         
9841         if(this.allowBlank && !this.getRawValue().length){
9842             return;
9843         }
9844         if (Roo.bootstrap.version == 3) {
9845             this.el.addClass(this.validClass);
9846         } else {
9847             this.inputEl().addClass('is-valid');
9848         }
9849
9850         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9851             
9852             var feedback = this.el.select('.form-control-feedback', true).first();
9853             
9854             if(feedback){
9855                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9856                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9857             }
9858             
9859         }
9860         
9861         this.fireEvent('valid', this);
9862     },
9863     
9864      /**
9865      * Mark this field as invalid
9866      * @param {String} msg The validation message
9867      */
9868     markInvalid : function(msg)
9869     {
9870         if(!this.el  || this.preventMark){ // not rendered
9871             return;
9872         }
9873         
9874         this.el.removeClass([this.invalidClass, this.validClass]);
9875         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9876         
9877         var feedback = this.el.select('.form-control-feedback', true).first();
9878             
9879         if(feedback){
9880             this.el.select('.form-control-feedback', true).first().removeClass(
9881                     [this.invalidFeedbackClass, this.validFeedbackClass]);
9882         }
9883
9884         if(this.disabled){
9885             return;
9886         }
9887         
9888         if(this.allowBlank && !this.getRawValue().length){
9889             return;
9890         }
9891         
9892         if(this.indicator){
9893             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9894             this.indicator.addClass('visible');
9895         }
9896         if (Roo.bootstrap.version == 3) {
9897             this.el.addClass(this.invalidClass);
9898         } else {
9899             this.inputEl().addClass('is-invalid');
9900         }
9901         
9902         
9903         
9904         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9905             
9906             var feedback = this.el.select('.form-control-feedback', true).first();
9907             
9908             if(feedback){
9909                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9910                 
9911                 if(this.getValue().length || this.forceFeedback){
9912                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9913                 }
9914                 
9915             }
9916             
9917         }
9918         
9919         this.fireEvent('invalid', this, msg);
9920     },
9921     // private
9922     SafariOnKeyDown : function(event)
9923     {
9924         // this is a workaround for a password hang bug on chrome/ webkit.
9925         if (this.inputEl().dom.type != 'password') {
9926             return;
9927         }
9928         
9929         var isSelectAll = false;
9930         
9931         if(this.inputEl().dom.selectionEnd > 0){
9932             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9933         }
9934         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9935             event.preventDefault();
9936             this.setValue('');
9937             return;
9938         }
9939         
9940         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9941             
9942             event.preventDefault();
9943             // this is very hacky as keydown always get's upper case.
9944             //
9945             var cc = String.fromCharCode(event.getCharCode());
9946             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9947             
9948         }
9949     },
9950     adjustWidth : function(tag, w){
9951         tag = tag.toLowerCase();
9952         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9953             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9954                 if(tag == 'input'){
9955                     return w + 2;
9956                 }
9957                 if(tag == 'textarea'){
9958                     return w-2;
9959                 }
9960             }else if(Roo.isOpera){
9961                 if(tag == 'input'){
9962                     return w + 2;
9963                 }
9964                 if(tag == 'textarea'){
9965                     return w-2;
9966                 }
9967             }
9968         }
9969         return w;
9970     },
9971     
9972     setFieldLabel : function(v)
9973     {
9974         if(!this.rendered){
9975             return;
9976         }
9977         
9978         if(this.indicatorEl()){
9979             var ar = this.el.select('label > span',true);
9980             
9981             if (ar.elements.length) {
9982                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9983                 this.fieldLabel = v;
9984                 return;
9985             }
9986             
9987             var br = this.el.select('label',true);
9988             
9989             if(br.elements.length) {
9990                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9991                 this.fieldLabel = v;
9992                 return;
9993             }
9994             
9995             Roo.log('Cannot Found any of label > span || label in input');
9996             return;
9997         }
9998         
9999         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10000         this.fieldLabel = v;
10001         
10002         
10003     }
10004 });
10005
10006  
10007 /*
10008  * - LGPL
10009  *
10010  * Input
10011  * 
10012  */
10013
10014 /**
10015  * @class Roo.bootstrap.TextArea
10016  * @extends Roo.bootstrap.Input
10017  * Bootstrap TextArea class
10018  * @cfg {Number} cols Specifies the visible width of a text area
10019  * @cfg {Number} rows Specifies the visible number of lines in a text area
10020  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10021  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10022  * @cfg {string} html text
10023  * 
10024  * @constructor
10025  * Create a new TextArea
10026  * @param {Object} config The config object
10027  */
10028
10029 Roo.bootstrap.TextArea = function(config){
10030     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10031    
10032 };
10033
10034 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
10035      
10036     cols : false,
10037     rows : 5,
10038     readOnly : false,
10039     warp : 'soft',
10040     resize : false,
10041     value: false,
10042     html: false,
10043     
10044     getAutoCreate : function(){
10045         
10046         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10047         
10048         var id = Roo.id();
10049         
10050         var cfg = {};
10051         
10052         if(this.inputType != 'hidden'){
10053             cfg.cls = 'form-group' //input-group
10054         }
10055         
10056         var input =  {
10057             tag: 'textarea',
10058             id : id,
10059             warp : this.warp,
10060             rows : this.rows,
10061             value : this.value || '',
10062             html: this.html || '',
10063             cls : 'form-control',
10064             placeholder : this.placeholder || '' 
10065             
10066         };
10067         
10068         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10069             input.maxLength = this.maxLength;
10070         }
10071         
10072         if(this.resize){
10073             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10074         }
10075         
10076         if(this.cols){
10077             input.cols = this.cols;
10078         }
10079         
10080         if (this.readOnly) {
10081             input.readonly = true;
10082         }
10083         
10084         if (this.name) {
10085             input.name = this.name;
10086         }
10087         
10088         if (this.size) {
10089             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10090         }
10091         
10092         var settings=this;
10093         ['xs','sm','md','lg'].map(function(size){
10094             if (settings[size]) {
10095                 cfg.cls += ' col-' + size + '-' + settings[size];
10096             }
10097         });
10098         
10099         var inputblock = input;
10100         
10101         if(this.hasFeedback && !this.allowBlank){
10102             
10103             var feedback = {
10104                 tag: 'span',
10105                 cls: 'glyphicon form-control-feedback'
10106             };
10107
10108             inputblock = {
10109                 cls : 'has-feedback',
10110                 cn :  [
10111                     input,
10112                     feedback
10113                 ] 
10114             };  
10115         }
10116         
10117         
10118         if (this.before || this.after) {
10119             
10120             inputblock = {
10121                 cls : 'input-group',
10122                 cn :  [] 
10123             };
10124             if (this.before) {
10125                 inputblock.cn.push({
10126                     tag :'span',
10127                     cls : 'input-group-addon',
10128                     html : this.before
10129                 });
10130             }
10131             
10132             inputblock.cn.push(input);
10133             
10134             if(this.hasFeedback && !this.allowBlank){
10135                 inputblock.cls += ' has-feedback';
10136                 inputblock.cn.push(feedback);
10137             }
10138             
10139             if (this.after) {
10140                 inputblock.cn.push({
10141                     tag :'span',
10142                     cls : 'input-group-addon',
10143                     html : this.after
10144                 });
10145             }
10146             
10147         }
10148         
10149         if (align ==='left' && this.fieldLabel.length) {
10150             cfg.cn = [
10151                 {
10152                     tag: 'label',
10153                     'for' :  id,
10154                     cls : 'control-label',
10155                     html : this.fieldLabel
10156                 },
10157                 {
10158                     cls : "",
10159                     cn: [
10160                         inputblock
10161                     ]
10162                 }
10163
10164             ];
10165             
10166             if(this.labelWidth > 12){
10167                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10168             }
10169
10170             if(this.labelWidth < 13 && this.labelmd == 0){
10171                 this.labelmd = this.labelWidth;
10172             }
10173
10174             if(this.labellg > 0){
10175                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10176                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10177             }
10178
10179             if(this.labelmd > 0){
10180                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10181                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10182             }
10183
10184             if(this.labelsm > 0){
10185                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10186                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10187             }
10188
10189             if(this.labelxs > 0){
10190                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10191                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10192             }
10193             
10194         } else if ( this.fieldLabel.length) {
10195             cfg.cn = [
10196
10197                {
10198                    tag: 'label',
10199                    //cls : 'input-group-addon',
10200                    html : this.fieldLabel
10201
10202                },
10203
10204                inputblock
10205
10206            ];
10207
10208         } else {
10209
10210             cfg.cn = [
10211
10212                 inputblock
10213
10214             ];
10215                 
10216         }
10217         
10218         if (this.disabled) {
10219             input.disabled=true;
10220         }
10221         
10222         return cfg;
10223         
10224     },
10225     /**
10226      * return the real textarea element.
10227      */
10228     inputEl: function ()
10229     {
10230         return this.el.select('textarea.form-control',true).first();
10231     },
10232     
10233     /**
10234      * Clear any invalid styles/messages for this field
10235      */
10236     clearInvalid : function()
10237     {
10238         
10239         if(!this.el || this.preventMark){ // not rendered
10240             return;
10241         }
10242         
10243         var label = this.el.select('label', true).first();
10244         var icon = this.el.select('i.fa-star', true).first();
10245         
10246         if(label && icon){
10247             icon.remove();
10248         }
10249         this.el.removeClass( this.validClass);
10250         this.inputEl().removeClass('is-invalid');
10251          
10252         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10253             
10254             var feedback = this.el.select('.form-control-feedback', true).first();
10255             
10256             if(feedback){
10257                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10258             }
10259             
10260         }
10261         
10262         this.fireEvent('valid', this);
10263     },
10264     
10265      /**
10266      * Mark this field as valid
10267      */
10268     markValid : function()
10269     {
10270         if(!this.el  || this.preventMark){ // not rendered
10271             return;
10272         }
10273         
10274         this.el.removeClass([this.invalidClass, this.validClass]);
10275         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10276         
10277         var feedback = this.el.select('.form-control-feedback', true).first();
10278             
10279         if(feedback){
10280             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10281         }
10282
10283         if(this.disabled || this.allowBlank){
10284             return;
10285         }
10286         
10287         var label = this.el.select('label', true).first();
10288         var icon = this.el.select('i.fa-star', true).first();
10289         
10290         if(label && icon){
10291             icon.remove();
10292         }
10293         if (Roo.bootstrap.version == 3) {
10294             this.el.addClass(this.validClass);
10295         } else {
10296             this.inputEl().addClass('is-valid');
10297         }
10298         
10299         
10300         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10301             
10302             var feedback = this.el.select('.form-control-feedback', true).first();
10303             
10304             if(feedback){
10305                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10306                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10307             }
10308             
10309         }
10310         
10311         this.fireEvent('valid', this);
10312     },
10313     
10314      /**
10315      * Mark this field as invalid
10316      * @param {String} msg The validation message
10317      */
10318     markInvalid : function(msg)
10319     {
10320         if(!this.el  || this.preventMark){ // not rendered
10321             return;
10322         }
10323         
10324         this.el.removeClass([this.invalidClass, this.validClass]);
10325         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10326         
10327         var feedback = this.el.select('.form-control-feedback', true).first();
10328             
10329         if(feedback){
10330             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10331         }
10332
10333         if(this.disabled || this.allowBlank){
10334             return;
10335         }
10336         
10337         var label = this.el.select('label', true).first();
10338         var icon = this.el.select('i.fa-star', true).first();
10339         
10340         if(!this.getValue().length && label && !icon){
10341             this.el.createChild({
10342                 tag : 'i',
10343                 cls : 'text-danger fa fa-lg fa-star',
10344                 tooltip : 'This field is required',
10345                 style : 'margin-right:5px;'
10346             }, label, true);
10347         }
10348         
10349         if (Roo.bootstrap.version == 3) {
10350             this.el.addClass(this.invalidClass);
10351         } else {
10352             this.inputEl().addClass('is-invalid');
10353         }
10354         
10355         // fixme ... this may be depricated need to test..
10356         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10357             
10358             var feedback = this.el.select('.form-control-feedback', true).first();
10359             
10360             if(feedback){
10361                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10362                 
10363                 if(this.getValue().length || this.forceFeedback){
10364                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10365                 }
10366                 
10367             }
10368             
10369         }
10370         
10371         this.fireEvent('invalid', this, msg);
10372     }
10373 });
10374
10375  
10376 /*
10377  * - LGPL
10378  *
10379  * trigger field - base class for combo..
10380  * 
10381  */
10382  
10383 /**
10384  * @class Roo.bootstrap.TriggerField
10385  * @extends Roo.bootstrap.Input
10386  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10387  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10388  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10389  * for which you can provide a custom implementation.  For example:
10390  * <pre><code>
10391 var trigger = new Roo.bootstrap.TriggerField();
10392 trigger.onTriggerClick = myTriggerFn;
10393 trigger.applyTo('my-field');
10394 </code></pre>
10395  *
10396  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10397  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10398  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10399  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10400  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10401
10402  * @constructor
10403  * Create a new TriggerField.
10404  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10405  * to the base TextField)
10406  */
10407 Roo.bootstrap.TriggerField = function(config){
10408     this.mimicing = false;
10409     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10410 };
10411
10412 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10413     /**
10414      * @cfg {String} triggerClass A CSS class to apply to the trigger
10415      */
10416      /**
10417      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10418      */
10419     hideTrigger:false,
10420
10421     /**
10422      * @cfg {Boolean} removable (true|false) special filter default false
10423      */
10424     removable : false,
10425     
10426     /** @cfg {Boolean} grow @hide */
10427     /** @cfg {Number} growMin @hide */
10428     /** @cfg {Number} growMax @hide */
10429
10430     /**
10431      * @hide 
10432      * @method
10433      */
10434     autoSize: Roo.emptyFn,
10435     // private
10436     monitorTab : true,
10437     // private
10438     deferHeight : true,
10439
10440     
10441     actionMode : 'wrap',
10442     
10443     caret : false,
10444     
10445     
10446     getAutoCreate : function(){
10447        
10448         var align = this.labelAlign || this.parentLabelAlign();
10449         
10450         var id = Roo.id();
10451         
10452         var cfg = {
10453             cls: 'form-group' //input-group
10454         };
10455         
10456         
10457         var input =  {
10458             tag: 'input',
10459             id : id,
10460             type : this.inputType,
10461             cls : 'form-control',
10462             autocomplete: 'new-password',
10463             placeholder : this.placeholder || '' 
10464             
10465         };
10466         if (this.name) {
10467             input.name = this.name;
10468         }
10469         if (this.size) {
10470             input.cls += ' input-' + this.size;
10471         }
10472         
10473         if (this.disabled) {
10474             input.disabled=true;
10475         }
10476         
10477         var inputblock = input;
10478         
10479         if(this.hasFeedback && !this.allowBlank){
10480             
10481             var feedback = {
10482                 tag: 'span',
10483                 cls: 'glyphicon form-control-feedback'
10484             };
10485             
10486             if(this.removable && !this.editable && !this.tickable){
10487                 inputblock = {
10488                     cls : 'has-feedback',
10489                     cn :  [
10490                         inputblock,
10491                         {
10492                             tag: 'button',
10493                             html : 'x',
10494                             cls : 'roo-combo-removable-btn close'
10495                         },
10496                         feedback
10497                     ] 
10498                 };
10499             } else {
10500                 inputblock = {
10501                     cls : 'has-feedback',
10502                     cn :  [
10503                         inputblock,
10504                         feedback
10505                     ] 
10506                 };
10507             }
10508
10509         } else {
10510             if(this.removable && !this.editable && !this.tickable){
10511                 inputblock = {
10512                     cls : 'roo-removable',
10513                     cn :  [
10514                         inputblock,
10515                         {
10516                             tag: 'button',
10517                             html : 'x',
10518                             cls : 'roo-combo-removable-btn close'
10519                         }
10520                     ] 
10521                 };
10522             }
10523         }
10524         
10525         if (this.before || this.after) {
10526             
10527             inputblock = {
10528                 cls : 'input-group',
10529                 cn :  [] 
10530             };
10531             if (this.before) {
10532                 inputblock.cn.push({
10533                     tag :'span',
10534                     cls : 'input-group-addon input-group-prepend input-group-text',
10535                     html : this.before
10536                 });
10537             }
10538             
10539             inputblock.cn.push(input);
10540             
10541             if(this.hasFeedback && !this.allowBlank){
10542                 inputblock.cls += ' has-feedback';
10543                 inputblock.cn.push(feedback);
10544             }
10545             
10546             if (this.after) {
10547                 inputblock.cn.push({
10548                     tag :'span',
10549                     cls : 'input-group-addon input-group-append input-group-text',
10550                     html : this.after
10551                 });
10552             }
10553             
10554         };
10555         
10556       
10557         
10558         var ibwrap = inputblock;
10559         
10560         if(this.multiple){
10561             ibwrap = {
10562                 tag: 'ul',
10563                 cls: 'roo-select2-choices',
10564                 cn:[
10565                     {
10566                         tag: 'li',
10567                         cls: 'roo-select2-search-field',
10568                         cn: [
10569
10570                             inputblock
10571                         ]
10572                     }
10573                 ]
10574             };
10575                 
10576         }
10577         
10578         var combobox = {
10579             cls: 'roo-select2-container input-group',
10580             cn: [
10581                  {
10582                     tag: 'input',
10583                     type : 'hidden',
10584                     cls: 'form-hidden-field'
10585                 },
10586                 ibwrap
10587             ]
10588         };
10589         
10590         if(!this.multiple && this.showToggleBtn){
10591             
10592             var caret = {
10593                         tag: 'span',
10594                         cls: 'caret'
10595              };
10596             if (this.caret != false) {
10597                 caret = {
10598                      tag: 'i',
10599                      cls: 'fa fa-' + this.caret
10600                 };
10601                 
10602             }
10603             
10604             combobox.cn.push({
10605                 tag :'span',
10606                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10607                 cn : [
10608                     caret,
10609                     {
10610                         tag: 'span',
10611                         cls: 'combobox-clear',
10612                         cn  : [
10613                             {
10614                                 tag : 'i',
10615                                 cls: 'icon-remove'
10616                             }
10617                         ]
10618                     }
10619                 ]
10620
10621             })
10622         }
10623         
10624         if(this.multiple){
10625             combobox.cls += ' roo-select2-container-multi';
10626         }
10627          var indicator = {
10628             tag : 'i',
10629             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10630             tooltip : 'This field is required'
10631         };
10632         if (Roo.bootstrap.version == 4) {
10633             indicator = {
10634                 tag : 'i',
10635                 style : 'display:none'
10636             };
10637         }
10638         
10639         
10640         if (align ==='left' && this.fieldLabel.length) {
10641             
10642             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10643
10644             cfg.cn = [
10645                 indicator,
10646                 {
10647                     tag: 'label',
10648                     'for' :  id,
10649                     cls : 'control-label',
10650                     html : this.fieldLabel
10651
10652                 },
10653                 {
10654                     cls : "", 
10655                     cn: [
10656                         combobox
10657                     ]
10658                 }
10659
10660             ];
10661             
10662             var labelCfg = cfg.cn[1];
10663             var contentCfg = cfg.cn[2];
10664             
10665             if(this.indicatorpos == 'right'){
10666                 cfg.cn = [
10667                     {
10668                         tag: 'label',
10669                         'for' :  id,
10670                         cls : 'control-label',
10671                         cn : [
10672                             {
10673                                 tag : 'span',
10674                                 html : this.fieldLabel
10675                             },
10676                             indicator
10677                         ]
10678                     },
10679                     {
10680                         cls : "", 
10681                         cn: [
10682                             combobox
10683                         ]
10684                     }
10685
10686                 ];
10687                 
10688                 labelCfg = cfg.cn[0];
10689                 contentCfg = cfg.cn[1];
10690             }
10691             
10692             if(this.labelWidth > 12){
10693                 labelCfg.style = "width: " + this.labelWidth + 'px';
10694             }
10695             
10696             if(this.labelWidth < 13 && this.labelmd == 0){
10697                 this.labelmd = this.labelWidth;
10698             }
10699             
10700             if(this.labellg > 0){
10701                 labelCfg.cls += ' col-lg-' + this.labellg;
10702                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10703             }
10704             
10705             if(this.labelmd > 0){
10706                 labelCfg.cls += ' col-md-' + this.labelmd;
10707                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10708             }
10709             
10710             if(this.labelsm > 0){
10711                 labelCfg.cls += ' col-sm-' + this.labelsm;
10712                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10713             }
10714             
10715             if(this.labelxs > 0){
10716                 labelCfg.cls += ' col-xs-' + this.labelxs;
10717                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10718             }
10719             
10720         } else if ( this.fieldLabel.length) {
10721 //                Roo.log(" label");
10722             cfg.cn = [
10723                 indicator,
10724                {
10725                    tag: 'label',
10726                    //cls : 'input-group-addon',
10727                    html : this.fieldLabel
10728
10729                },
10730
10731                combobox
10732
10733             ];
10734             
10735             if(this.indicatorpos == 'right'){
10736                 
10737                 cfg.cn = [
10738                     {
10739                        tag: 'label',
10740                        cn : [
10741                            {
10742                                tag : 'span',
10743                                html : this.fieldLabel
10744                            },
10745                            indicator
10746                        ]
10747
10748                     },
10749                     combobox
10750
10751                 ];
10752
10753             }
10754
10755         } else {
10756             
10757 //                Roo.log(" no label && no align");
10758                 cfg = combobox
10759                      
10760                 
10761         }
10762         
10763         var settings=this;
10764         ['xs','sm','md','lg'].map(function(size){
10765             if (settings[size]) {
10766                 cfg.cls += ' col-' + size + '-' + settings[size];
10767             }
10768         });
10769         
10770         return cfg;
10771         
10772     },
10773     
10774     
10775     
10776     // private
10777     onResize : function(w, h){
10778 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10779 //        if(typeof w == 'number'){
10780 //            var x = w - this.trigger.getWidth();
10781 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10782 //            this.trigger.setStyle('left', x+'px');
10783 //        }
10784     },
10785
10786     // private
10787     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10788
10789     // private
10790     getResizeEl : function(){
10791         return this.inputEl();
10792     },
10793
10794     // private
10795     getPositionEl : function(){
10796         return this.inputEl();
10797     },
10798
10799     // private
10800     alignErrorIcon : function(){
10801         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10802     },
10803
10804     // private
10805     initEvents : function(){
10806         
10807         this.createList();
10808         
10809         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10810         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10811         if(!this.multiple && this.showToggleBtn){
10812             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10813             if(this.hideTrigger){
10814                 this.trigger.setDisplayed(false);
10815             }
10816             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10817         }
10818         
10819         if(this.multiple){
10820             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10821         }
10822         
10823         if(this.removable && !this.editable && !this.tickable){
10824             var close = this.closeTriggerEl();
10825             
10826             if(close){
10827                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10828                 close.on('click', this.removeBtnClick, this, close);
10829             }
10830         }
10831         
10832         //this.trigger.addClassOnOver('x-form-trigger-over');
10833         //this.trigger.addClassOnClick('x-form-trigger-click');
10834         
10835         //if(!this.width){
10836         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10837         //}
10838     },
10839     
10840     closeTriggerEl : function()
10841     {
10842         var close = this.el.select('.roo-combo-removable-btn', true).first();
10843         return close ? close : false;
10844     },
10845     
10846     removeBtnClick : function(e, h, el)
10847     {
10848         e.preventDefault();
10849         
10850         if(this.fireEvent("remove", this) !== false){
10851             this.reset();
10852             this.fireEvent("afterremove", this)
10853         }
10854     },
10855     
10856     createList : function()
10857     {
10858         this.list = Roo.get(document.body).createChild({
10859             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10860             cls: 'typeahead typeahead-long dropdown-menu',
10861             style: 'display:none'
10862         });
10863         
10864         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10865         
10866     },
10867
10868     // private
10869     initTrigger : function(){
10870        
10871     },
10872
10873     // private
10874     onDestroy : function(){
10875         if(this.trigger){
10876             this.trigger.removeAllListeners();
10877           //  this.trigger.remove();
10878         }
10879         //if(this.wrap){
10880         //    this.wrap.remove();
10881         //}
10882         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10883     },
10884
10885     // private
10886     onFocus : function(){
10887         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10888         /*
10889         if(!this.mimicing){
10890             this.wrap.addClass('x-trigger-wrap-focus');
10891             this.mimicing = true;
10892             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10893             if(this.monitorTab){
10894                 this.el.on("keydown", this.checkTab, this);
10895             }
10896         }
10897         */
10898     },
10899
10900     // private
10901     checkTab : function(e){
10902         if(e.getKey() == e.TAB){
10903             this.triggerBlur();
10904         }
10905     },
10906
10907     // private
10908     onBlur : function(){
10909         // do nothing
10910     },
10911
10912     // private
10913     mimicBlur : function(e, t){
10914         /*
10915         if(!this.wrap.contains(t) && this.validateBlur()){
10916             this.triggerBlur();
10917         }
10918         */
10919     },
10920
10921     // private
10922     triggerBlur : function(){
10923         this.mimicing = false;
10924         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10925         if(this.monitorTab){
10926             this.el.un("keydown", this.checkTab, this);
10927         }
10928         //this.wrap.removeClass('x-trigger-wrap-focus');
10929         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10930     },
10931
10932     // private
10933     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10934     validateBlur : function(e, t){
10935         return true;
10936     },
10937
10938     // private
10939     onDisable : function(){
10940         this.inputEl().dom.disabled = true;
10941         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10942         //if(this.wrap){
10943         //    this.wrap.addClass('x-item-disabled');
10944         //}
10945     },
10946
10947     // private
10948     onEnable : function(){
10949         this.inputEl().dom.disabled = false;
10950         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10951         //if(this.wrap){
10952         //    this.el.removeClass('x-item-disabled');
10953         //}
10954     },
10955
10956     // private
10957     onShow : function(){
10958         var ae = this.getActionEl();
10959         
10960         if(ae){
10961             ae.dom.style.display = '';
10962             ae.dom.style.visibility = 'visible';
10963         }
10964     },
10965
10966     // private
10967     
10968     onHide : function(){
10969         var ae = this.getActionEl();
10970         ae.dom.style.display = 'none';
10971     },
10972
10973     /**
10974      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10975      * by an implementing function.
10976      * @method
10977      * @param {EventObject} e
10978      */
10979     onTriggerClick : Roo.emptyFn
10980 });
10981  /*
10982  * Based on:
10983  * Ext JS Library 1.1.1
10984  * Copyright(c) 2006-2007, Ext JS, LLC.
10985  *
10986  * Originally Released Under LGPL - original licence link has changed is not relivant.
10987  *
10988  * Fork - LGPL
10989  * <script type="text/javascript">
10990  */
10991
10992
10993 /**
10994  * @class Roo.data.SortTypes
10995  * @singleton
10996  * Defines the default sorting (casting?) comparison functions used when sorting data.
10997  */
10998 Roo.data.SortTypes = {
10999     /**
11000      * Default sort that does nothing
11001      * @param {Mixed} s The value being converted
11002      * @return {Mixed} The comparison value
11003      */
11004     none : function(s){
11005         return s;
11006     },
11007     
11008     /**
11009      * The regular expression used to strip tags
11010      * @type {RegExp}
11011      * @property
11012      */
11013     stripTagsRE : /<\/?[^>]+>/gi,
11014     
11015     /**
11016      * Strips all HTML tags to sort on text only
11017      * @param {Mixed} s The value being converted
11018      * @return {String} The comparison value
11019      */
11020     asText : function(s){
11021         return String(s).replace(this.stripTagsRE, "");
11022     },
11023     
11024     /**
11025      * Strips all HTML tags to sort on text only - Case insensitive
11026      * @param {Mixed} s The value being converted
11027      * @return {String} The comparison value
11028      */
11029     asUCText : function(s){
11030         return String(s).toUpperCase().replace(this.stripTagsRE, "");
11031     },
11032     
11033     /**
11034      * Case insensitive string
11035      * @param {Mixed} s The value being converted
11036      * @return {String} The comparison value
11037      */
11038     asUCString : function(s) {
11039         return String(s).toUpperCase();
11040     },
11041     
11042     /**
11043      * Date sorting
11044      * @param {Mixed} s The value being converted
11045      * @return {Number} The comparison value
11046      */
11047     asDate : function(s) {
11048         if(!s){
11049             return 0;
11050         }
11051         if(s instanceof Date){
11052             return s.getTime();
11053         }
11054         return Date.parse(String(s));
11055     },
11056     
11057     /**
11058      * Float sorting
11059      * @param {Mixed} s The value being converted
11060      * @return {Float} The comparison value
11061      */
11062     asFloat : function(s) {
11063         var val = parseFloat(String(s).replace(/,/g, ""));
11064         if(isNaN(val)) {
11065             val = 0;
11066         }
11067         return val;
11068     },
11069     
11070     /**
11071      * Integer sorting
11072      * @param {Mixed} s The value being converted
11073      * @return {Number} The comparison value
11074      */
11075     asInt : function(s) {
11076         var val = parseInt(String(s).replace(/,/g, ""));
11077         if(isNaN(val)) {
11078             val = 0;
11079         }
11080         return val;
11081     }
11082 };/*
11083  * Based on:
11084  * Ext JS Library 1.1.1
11085  * Copyright(c) 2006-2007, Ext JS, LLC.
11086  *
11087  * Originally Released Under LGPL - original licence link has changed is not relivant.
11088  *
11089  * Fork - LGPL
11090  * <script type="text/javascript">
11091  */
11092
11093 /**
11094 * @class Roo.data.Record
11095  * Instances of this class encapsulate both record <em>definition</em> information, and record
11096  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11097  * to access Records cached in an {@link Roo.data.Store} object.<br>
11098  * <p>
11099  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11100  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11101  * objects.<br>
11102  * <p>
11103  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11104  * @constructor
11105  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11106  * {@link #create}. The parameters are the same.
11107  * @param {Array} data An associative Array of data values keyed by the field name.
11108  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11109  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11110  * not specified an integer id is generated.
11111  */
11112 Roo.data.Record = function(data, id){
11113     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11114     this.data = data;
11115 };
11116
11117 /**
11118  * Generate a constructor for a specific record layout.
11119  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11120  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11121  * Each field definition object may contain the following properties: <ul>
11122  * <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,
11123  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11124  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11125  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11126  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11127  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11128  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11129  * this may be omitted.</p></li>
11130  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11131  * <ul><li>auto (Default, implies no conversion)</li>
11132  * <li>string</li>
11133  * <li>int</li>
11134  * <li>float</li>
11135  * <li>boolean</li>
11136  * <li>date</li></ul></p></li>
11137  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11138  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11139  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11140  * by the Reader into an object that will be stored in the Record. It is passed the
11141  * following parameters:<ul>
11142  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11143  * </ul></p></li>
11144  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11145  * </ul>
11146  * <br>usage:<br><pre><code>
11147 var TopicRecord = Roo.data.Record.create(
11148     {name: 'title', mapping: 'topic_title'},
11149     {name: 'author', mapping: 'username'},
11150     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11151     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11152     {name: 'lastPoster', mapping: 'user2'},
11153     {name: 'excerpt', mapping: 'post_text'}
11154 );
11155
11156 var myNewRecord = new TopicRecord({
11157     title: 'Do my job please',
11158     author: 'noobie',
11159     totalPosts: 1,
11160     lastPost: new Date(),
11161     lastPoster: 'Animal',
11162     excerpt: 'No way dude!'
11163 });
11164 myStore.add(myNewRecord);
11165 </code></pre>
11166  * @method create
11167  * @static
11168  */
11169 Roo.data.Record.create = function(o){
11170     var f = function(){
11171         f.superclass.constructor.apply(this, arguments);
11172     };
11173     Roo.extend(f, Roo.data.Record);
11174     var p = f.prototype;
11175     p.fields = new Roo.util.MixedCollection(false, function(field){
11176         return field.name;
11177     });
11178     for(var i = 0, len = o.length; i < len; i++){
11179         p.fields.add(new Roo.data.Field(o[i]));
11180     }
11181     f.getField = function(name){
11182         return p.fields.get(name);  
11183     };
11184     return f;
11185 };
11186
11187 Roo.data.Record.AUTO_ID = 1000;
11188 Roo.data.Record.EDIT = 'edit';
11189 Roo.data.Record.REJECT = 'reject';
11190 Roo.data.Record.COMMIT = 'commit';
11191
11192 Roo.data.Record.prototype = {
11193     /**
11194      * Readonly flag - true if this record has been modified.
11195      * @type Boolean
11196      */
11197     dirty : false,
11198     editing : false,
11199     error: null,
11200     modified: null,
11201
11202     // private
11203     join : function(store){
11204         this.store = store;
11205     },
11206
11207     /**
11208      * Set the named field to the specified value.
11209      * @param {String} name The name of the field to set.
11210      * @param {Object} value The value to set the field to.
11211      */
11212     set : function(name, value){
11213         if(this.data[name] == value){
11214             return;
11215         }
11216         this.dirty = true;
11217         if(!this.modified){
11218             this.modified = {};
11219         }
11220         if(typeof this.modified[name] == 'undefined'){
11221             this.modified[name] = this.data[name];
11222         }
11223         this.data[name] = value;
11224         if(!this.editing && this.store){
11225             this.store.afterEdit(this);
11226         }       
11227     },
11228
11229     /**
11230      * Get the value of the named field.
11231      * @param {String} name The name of the field to get the value of.
11232      * @return {Object} The value of the field.
11233      */
11234     get : function(name){
11235         return this.data[name]; 
11236     },
11237
11238     // private
11239     beginEdit : function(){
11240         this.editing = true;
11241         this.modified = {}; 
11242     },
11243
11244     // private
11245     cancelEdit : function(){
11246         this.editing = false;
11247         delete this.modified;
11248     },
11249
11250     // private
11251     endEdit : function(){
11252         this.editing = false;
11253         if(this.dirty && this.store){
11254             this.store.afterEdit(this);
11255         }
11256     },
11257
11258     /**
11259      * Usually called by the {@link Roo.data.Store} which owns the Record.
11260      * Rejects all changes made to the Record since either creation, or the last commit operation.
11261      * Modified fields are reverted to their original values.
11262      * <p>
11263      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11264      * of reject operations.
11265      */
11266     reject : function(){
11267         var m = this.modified;
11268         for(var n in m){
11269             if(typeof m[n] != "function"){
11270                 this.data[n] = m[n];
11271             }
11272         }
11273         this.dirty = false;
11274         delete this.modified;
11275         this.editing = false;
11276         if(this.store){
11277             this.store.afterReject(this);
11278         }
11279     },
11280
11281     /**
11282      * Usually called by the {@link Roo.data.Store} which owns the Record.
11283      * Commits all changes made to the Record since either creation, or the last commit operation.
11284      * <p>
11285      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11286      * of commit operations.
11287      */
11288     commit : function(){
11289         this.dirty = false;
11290         delete this.modified;
11291         this.editing = false;
11292         if(this.store){
11293             this.store.afterCommit(this);
11294         }
11295     },
11296
11297     // private
11298     hasError : function(){
11299         return this.error != null;
11300     },
11301
11302     // private
11303     clearError : function(){
11304         this.error = null;
11305     },
11306
11307     /**
11308      * Creates a copy of this record.
11309      * @param {String} id (optional) A new record id if you don't want to use this record's id
11310      * @return {Record}
11311      */
11312     copy : function(newId) {
11313         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11314     }
11315 };/*
11316  * Based on:
11317  * Ext JS Library 1.1.1
11318  * Copyright(c) 2006-2007, Ext JS, LLC.
11319  *
11320  * Originally Released Under LGPL - original licence link has changed is not relivant.
11321  *
11322  * Fork - LGPL
11323  * <script type="text/javascript">
11324  */
11325
11326
11327
11328 /**
11329  * @class Roo.data.Store
11330  * @extends Roo.util.Observable
11331  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11332  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11333  * <p>
11334  * 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
11335  * has no knowledge of the format of the data returned by the Proxy.<br>
11336  * <p>
11337  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11338  * instances from the data object. These records are cached and made available through accessor functions.
11339  * @constructor
11340  * Creates a new Store.
11341  * @param {Object} config A config object containing the objects needed for the Store to access data,
11342  * and read the data into Records.
11343  */
11344 Roo.data.Store = function(config){
11345     this.data = new Roo.util.MixedCollection(false);
11346     this.data.getKey = function(o){
11347         return o.id;
11348     };
11349     this.baseParams = {};
11350     // private
11351     this.paramNames = {
11352         "start" : "start",
11353         "limit" : "limit",
11354         "sort" : "sort",
11355         "dir" : "dir",
11356         "multisort" : "_multisort"
11357     };
11358
11359     if(config && config.data){
11360         this.inlineData = config.data;
11361         delete config.data;
11362     }
11363
11364     Roo.apply(this, config);
11365     
11366     if(this.reader){ // reader passed
11367         this.reader = Roo.factory(this.reader, Roo.data);
11368         this.reader.xmodule = this.xmodule || false;
11369         if(!this.recordType){
11370             this.recordType = this.reader.recordType;
11371         }
11372         if(this.reader.onMetaChange){
11373             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11374         }
11375     }
11376
11377     if(this.recordType){
11378         this.fields = this.recordType.prototype.fields;
11379     }
11380     this.modified = [];
11381
11382     this.addEvents({
11383         /**
11384          * @event datachanged
11385          * Fires when the data cache has changed, and a widget which is using this Store
11386          * as a Record cache should refresh its view.
11387          * @param {Store} this
11388          */
11389         datachanged : true,
11390         /**
11391          * @event metachange
11392          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11393          * @param {Store} this
11394          * @param {Object} meta The JSON metadata
11395          */
11396         metachange : true,
11397         /**
11398          * @event add
11399          * Fires when Records have been added to the Store
11400          * @param {Store} this
11401          * @param {Roo.data.Record[]} records The array of Records added
11402          * @param {Number} index The index at which the record(s) were added
11403          */
11404         add : true,
11405         /**
11406          * @event remove
11407          * Fires when a Record has been removed from the Store
11408          * @param {Store} this
11409          * @param {Roo.data.Record} record The Record that was removed
11410          * @param {Number} index The index at which the record was removed
11411          */
11412         remove : true,
11413         /**
11414          * @event update
11415          * Fires when a Record has been updated
11416          * @param {Store} this
11417          * @param {Roo.data.Record} record The Record that was updated
11418          * @param {String} operation The update operation being performed.  Value may be one of:
11419          * <pre><code>
11420  Roo.data.Record.EDIT
11421  Roo.data.Record.REJECT
11422  Roo.data.Record.COMMIT
11423          * </code></pre>
11424          */
11425         update : true,
11426         /**
11427          * @event clear
11428          * Fires when the data cache has been cleared.
11429          * @param {Store} this
11430          */
11431         clear : true,
11432         /**
11433          * @event beforeload
11434          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11435          * the load action will be canceled.
11436          * @param {Store} this
11437          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11438          */
11439         beforeload : true,
11440         /**
11441          * @event beforeloadadd
11442          * Fires after a new set of Records has been loaded.
11443          * @param {Store} this
11444          * @param {Roo.data.Record[]} records The Records that were loaded
11445          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11446          */
11447         beforeloadadd : true,
11448         /**
11449          * @event load
11450          * Fires after a new set of Records has been loaded, before they are added to the store.
11451          * @param {Store} this
11452          * @param {Roo.data.Record[]} records The Records that were loaded
11453          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11454          * @params {Object} return from reader
11455          */
11456         load : true,
11457         /**
11458          * @event loadexception
11459          * Fires if an exception occurs in the Proxy during loading.
11460          * Called with the signature of the Proxy's "loadexception" event.
11461          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11462          * 
11463          * @param {Proxy} 
11464          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11465          * @param {Object} load options 
11466          * @param {Object} jsonData from your request (normally this contains the Exception)
11467          */
11468         loadexception : true
11469     });
11470     
11471     if(this.proxy){
11472         this.proxy = Roo.factory(this.proxy, Roo.data);
11473         this.proxy.xmodule = this.xmodule || false;
11474         this.relayEvents(this.proxy,  ["loadexception"]);
11475     }
11476     this.sortToggle = {};
11477     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11478
11479     Roo.data.Store.superclass.constructor.call(this);
11480
11481     if(this.inlineData){
11482         this.loadData(this.inlineData);
11483         delete this.inlineData;
11484     }
11485 };
11486
11487 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11488      /**
11489     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11490     * without a remote query - used by combo/forms at present.
11491     */
11492     
11493     /**
11494     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11495     */
11496     /**
11497     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11498     */
11499     /**
11500     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11501     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11502     */
11503     /**
11504     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11505     * on any HTTP request
11506     */
11507     /**
11508     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11509     */
11510     /**
11511     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11512     */
11513     multiSort: false,
11514     /**
11515     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11516     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11517     */
11518     remoteSort : false,
11519
11520     /**
11521     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11522      * loaded or when a record is removed. (defaults to false).
11523     */
11524     pruneModifiedRecords : false,
11525
11526     // private
11527     lastOptions : null,
11528
11529     /**
11530      * Add Records to the Store and fires the add event.
11531      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11532      */
11533     add : function(records){
11534         records = [].concat(records);
11535         for(var i = 0, len = records.length; i < len; i++){
11536             records[i].join(this);
11537         }
11538         var index = this.data.length;
11539         this.data.addAll(records);
11540         this.fireEvent("add", this, records, index);
11541     },
11542
11543     /**
11544      * Remove a Record from the Store and fires the remove event.
11545      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11546      */
11547     remove : function(record){
11548         var index = this.data.indexOf(record);
11549         this.data.removeAt(index);
11550  
11551         if(this.pruneModifiedRecords){
11552             this.modified.remove(record);
11553         }
11554         this.fireEvent("remove", this, record, index);
11555     },
11556
11557     /**
11558      * Remove all Records from the Store and fires the clear event.
11559      */
11560     removeAll : function(){
11561         this.data.clear();
11562         if(this.pruneModifiedRecords){
11563             this.modified = [];
11564         }
11565         this.fireEvent("clear", this);
11566     },
11567
11568     /**
11569      * Inserts Records to the Store at the given index and fires the add event.
11570      * @param {Number} index The start index at which to insert the passed Records.
11571      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11572      */
11573     insert : function(index, records){
11574         records = [].concat(records);
11575         for(var i = 0, len = records.length; i < len; i++){
11576             this.data.insert(index, records[i]);
11577             records[i].join(this);
11578         }
11579         this.fireEvent("add", this, records, index);
11580     },
11581
11582     /**
11583      * Get the index within the cache of the passed Record.
11584      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11585      * @return {Number} The index of the passed Record. Returns -1 if not found.
11586      */
11587     indexOf : function(record){
11588         return this.data.indexOf(record);
11589     },
11590
11591     /**
11592      * Get the index within the cache of the Record with the passed id.
11593      * @param {String} id The id of the Record to find.
11594      * @return {Number} The index of the Record. Returns -1 if not found.
11595      */
11596     indexOfId : function(id){
11597         return this.data.indexOfKey(id);
11598     },
11599
11600     /**
11601      * Get the Record with the specified id.
11602      * @param {String} id The id of the Record to find.
11603      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11604      */
11605     getById : function(id){
11606         return this.data.key(id);
11607     },
11608
11609     /**
11610      * Get the Record at the specified index.
11611      * @param {Number} index The index of the Record to find.
11612      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11613      */
11614     getAt : function(index){
11615         return this.data.itemAt(index);
11616     },
11617
11618     /**
11619      * Returns a range of Records between specified indices.
11620      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11621      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11622      * @return {Roo.data.Record[]} An array of Records
11623      */
11624     getRange : function(start, end){
11625         return this.data.getRange(start, end);
11626     },
11627
11628     // private
11629     storeOptions : function(o){
11630         o = Roo.apply({}, o);
11631         delete o.callback;
11632         delete o.scope;
11633         this.lastOptions = o;
11634     },
11635
11636     /**
11637      * Loads the Record cache from the configured Proxy using the configured Reader.
11638      * <p>
11639      * If using remote paging, then the first load call must specify the <em>start</em>
11640      * and <em>limit</em> properties in the options.params property to establish the initial
11641      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11642      * <p>
11643      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11644      * and this call will return before the new data has been loaded. Perform any post-processing
11645      * in a callback function, or in a "load" event handler.</strong>
11646      * <p>
11647      * @param {Object} options An object containing properties which control loading options:<ul>
11648      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11649      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11650      * passed the following arguments:<ul>
11651      * <li>r : Roo.data.Record[]</li>
11652      * <li>options: Options object from the load call</li>
11653      * <li>success: Boolean success indicator</li></ul></li>
11654      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11655      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11656      * </ul>
11657      */
11658     load : function(options){
11659         options = options || {};
11660         if(this.fireEvent("beforeload", this, options) !== false){
11661             this.storeOptions(options);
11662             var p = Roo.apply(options.params || {}, this.baseParams);
11663             // if meta was not loaded from remote source.. try requesting it.
11664             if (!this.reader.metaFromRemote) {
11665                 p._requestMeta = 1;
11666             }
11667             if(this.sortInfo && this.remoteSort){
11668                 var pn = this.paramNames;
11669                 p[pn["sort"]] = this.sortInfo.field;
11670                 p[pn["dir"]] = this.sortInfo.direction;
11671             }
11672             if (this.multiSort) {
11673                 var pn = this.paramNames;
11674                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11675             }
11676             
11677             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11678         }
11679     },
11680
11681     /**
11682      * Reloads the Record cache from the configured Proxy using the configured Reader and
11683      * the options from the last load operation performed.
11684      * @param {Object} options (optional) An object containing properties which may override the options
11685      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11686      * the most recently used options are reused).
11687      */
11688     reload : function(options){
11689         this.load(Roo.applyIf(options||{}, this.lastOptions));
11690     },
11691
11692     // private
11693     // Called as a callback by the Reader during a load operation.
11694     loadRecords : function(o, options, success){
11695         if(!o || success === false){
11696             if(success !== false){
11697                 this.fireEvent("load", this, [], options, o);
11698             }
11699             if(options.callback){
11700                 options.callback.call(options.scope || this, [], options, false);
11701             }
11702             return;
11703         }
11704         // if data returned failure - throw an exception.
11705         if (o.success === false) {
11706             // show a message if no listener is registered.
11707             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11708                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11709             }
11710             // loadmask wil be hooked into this..
11711             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11712             return;
11713         }
11714         var r = o.records, t = o.totalRecords || r.length;
11715         
11716         this.fireEvent("beforeloadadd", this, r, options, o);
11717         
11718         if(!options || options.add !== true){
11719             if(this.pruneModifiedRecords){
11720                 this.modified = [];
11721             }
11722             for(var i = 0, len = r.length; i < len; i++){
11723                 r[i].join(this);
11724             }
11725             if(this.snapshot){
11726                 this.data = this.snapshot;
11727                 delete this.snapshot;
11728             }
11729             this.data.clear();
11730             this.data.addAll(r);
11731             this.totalLength = t;
11732             this.applySort();
11733             this.fireEvent("datachanged", this);
11734         }else{
11735             this.totalLength = Math.max(t, this.data.length+r.length);
11736             this.add(r);
11737         }
11738         
11739         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11740                 
11741             var e = new Roo.data.Record({});
11742
11743             e.set(this.parent.displayField, this.parent.emptyTitle);
11744             e.set(this.parent.valueField, '');
11745
11746             this.insert(0, e);
11747         }
11748             
11749         this.fireEvent("load", this, r, options, o);
11750         if(options.callback){
11751             options.callback.call(options.scope || this, r, options, true);
11752         }
11753     },
11754
11755
11756     /**
11757      * Loads data from a passed data block. A Reader which understands the format of the data
11758      * must have been configured in the constructor.
11759      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11760      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11761      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11762      */
11763     loadData : function(o, append){
11764         var r = this.reader.readRecords(o);
11765         this.loadRecords(r, {add: append}, true);
11766     },
11767
11768     /**
11769      * Gets the number of cached records.
11770      * <p>
11771      * <em>If using paging, this may not be the total size of the dataset. If the data object
11772      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11773      * the data set size</em>
11774      */
11775     getCount : function(){
11776         return this.data.length || 0;
11777     },
11778
11779     /**
11780      * Gets the total number of records in the dataset as returned by the server.
11781      * <p>
11782      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11783      * the dataset size</em>
11784      */
11785     getTotalCount : function(){
11786         return this.totalLength || 0;
11787     },
11788
11789     /**
11790      * Returns the sort state of the Store as an object with two properties:
11791      * <pre><code>
11792  field {String} The name of the field by which the Records are sorted
11793  direction {String} The sort order, "ASC" or "DESC"
11794      * </code></pre>
11795      */
11796     getSortState : function(){
11797         return this.sortInfo;
11798     },
11799
11800     // private
11801     applySort : function(){
11802         if(this.sortInfo && !this.remoteSort){
11803             var s = this.sortInfo, f = s.field;
11804             var st = this.fields.get(f).sortType;
11805             var fn = function(r1, r2){
11806                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11807                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11808             };
11809             this.data.sort(s.direction, fn);
11810             if(this.snapshot && this.snapshot != this.data){
11811                 this.snapshot.sort(s.direction, fn);
11812             }
11813         }
11814     },
11815
11816     /**
11817      * Sets the default sort column and order to be used by the next load operation.
11818      * @param {String} fieldName The name of the field to sort by.
11819      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11820      */
11821     setDefaultSort : function(field, dir){
11822         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11823     },
11824
11825     /**
11826      * Sort the Records.
11827      * If remote sorting is used, the sort is performed on the server, and the cache is
11828      * reloaded. If local sorting is used, the cache is sorted internally.
11829      * @param {String} fieldName The name of the field to sort by.
11830      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11831      */
11832     sort : function(fieldName, dir){
11833         var f = this.fields.get(fieldName);
11834         if(!dir){
11835             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11836             
11837             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11838                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11839             }else{
11840                 dir = f.sortDir;
11841             }
11842         }
11843         this.sortToggle[f.name] = dir;
11844         this.sortInfo = {field: f.name, direction: dir};
11845         if(!this.remoteSort){
11846             this.applySort();
11847             this.fireEvent("datachanged", this);
11848         }else{
11849             this.load(this.lastOptions);
11850         }
11851     },
11852
11853     /**
11854      * Calls the specified function for each of the Records in the cache.
11855      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11856      * Returning <em>false</em> aborts and exits the iteration.
11857      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11858      */
11859     each : function(fn, scope){
11860         this.data.each(fn, scope);
11861     },
11862
11863     /**
11864      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11865      * (e.g., during paging).
11866      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11867      */
11868     getModifiedRecords : function(){
11869         return this.modified;
11870     },
11871
11872     // private
11873     createFilterFn : function(property, value, anyMatch){
11874         if(!value.exec){ // not a regex
11875             value = String(value);
11876             if(value.length == 0){
11877                 return false;
11878             }
11879             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11880         }
11881         return function(r){
11882             return value.test(r.data[property]);
11883         };
11884     },
11885
11886     /**
11887      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11888      * @param {String} property A field on your records
11889      * @param {Number} start The record index to start at (defaults to 0)
11890      * @param {Number} end The last record index to include (defaults to length - 1)
11891      * @return {Number} The sum
11892      */
11893     sum : function(property, start, end){
11894         var rs = this.data.items, v = 0;
11895         start = start || 0;
11896         end = (end || end === 0) ? end : rs.length-1;
11897
11898         for(var i = start; i <= end; i++){
11899             v += (rs[i].data[property] || 0);
11900         }
11901         return v;
11902     },
11903
11904     /**
11905      * Filter the records by a specified property.
11906      * @param {String} field A field on your records
11907      * @param {String/RegExp} value Either a string that the field
11908      * should start with or a RegExp to test against the field
11909      * @param {Boolean} anyMatch True to match any part not just the beginning
11910      */
11911     filter : function(property, value, anyMatch){
11912         var fn = this.createFilterFn(property, value, anyMatch);
11913         return fn ? this.filterBy(fn) : this.clearFilter();
11914     },
11915
11916     /**
11917      * Filter by a function. The specified function will be called with each
11918      * record in this data source. If the function returns true the record is included,
11919      * otherwise it is filtered.
11920      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11921      * @param {Object} scope (optional) The scope of the function (defaults to this)
11922      */
11923     filterBy : function(fn, scope){
11924         this.snapshot = this.snapshot || this.data;
11925         this.data = this.queryBy(fn, scope||this);
11926         this.fireEvent("datachanged", this);
11927     },
11928
11929     /**
11930      * Query the records by a specified property.
11931      * @param {String} field A field on your records
11932      * @param {String/RegExp} value Either a string that the field
11933      * should start with or a RegExp to test against the field
11934      * @param {Boolean} anyMatch True to match any part not just the beginning
11935      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11936      */
11937     query : function(property, value, anyMatch){
11938         var fn = this.createFilterFn(property, value, anyMatch);
11939         return fn ? this.queryBy(fn) : this.data.clone();
11940     },
11941
11942     /**
11943      * Query by a function. The specified function will be called with each
11944      * record in this data source. If the function returns true the record is included
11945      * in the results.
11946      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11947      * @param {Object} scope (optional) The scope of the function (defaults to this)
11948       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11949      **/
11950     queryBy : function(fn, scope){
11951         var data = this.snapshot || this.data;
11952         return data.filterBy(fn, scope||this);
11953     },
11954
11955     /**
11956      * Collects unique values for a particular dataIndex from this store.
11957      * @param {String} dataIndex The property to collect
11958      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11959      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11960      * @return {Array} An array of the unique values
11961      **/
11962     collect : function(dataIndex, allowNull, bypassFilter){
11963         var d = (bypassFilter === true && this.snapshot) ?
11964                 this.snapshot.items : this.data.items;
11965         var v, sv, r = [], l = {};
11966         for(var i = 0, len = d.length; i < len; i++){
11967             v = d[i].data[dataIndex];
11968             sv = String(v);
11969             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11970                 l[sv] = true;
11971                 r[r.length] = v;
11972             }
11973         }
11974         return r;
11975     },
11976
11977     /**
11978      * Revert to a view of the Record cache with no filtering applied.
11979      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11980      */
11981     clearFilter : function(suppressEvent){
11982         if(this.snapshot && this.snapshot != this.data){
11983             this.data = this.snapshot;
11984             delete this.snapshot;
11985             if(suppressEvent !== true){
11986                 this.fireEvent("datachanged", this);
11987             }
11988         }
11989     },
11990
11991     // private
11992     afterEdit : function(record){
11993         if(this.modified.indexOf(record) == -1){
11994             this.modified.push(record);
11995         }
11996         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11997     },
11998     
11999     // private
12000     afterReject : function(record){
12001         this.modified.remove(record);
12002         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12003     },
12004
12005     // private
12006     afterCommit : function(record){
12007         this.modified.remove(record);
12008         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12009     },
12010
12011     /**
12012      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12013      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12014      */
12015     commitChanges : function(){
12016         var m = this.modified.slice(0);
12017         this.modified = [];
12018         for(var i = 0, len = m.length; i < len; i++){
12019             m[i].commit();
12020         }
12021     },
12022
12023     /**
12024      * Cancel outstanding changes on all changed records.
12025      */
12026     rejectChanges : function(){
12027         var m = this.modified.slice(0);
12028         this.modified = [];
12029         for(var i = 0, len = m.length; i < len; i++){
12030             m[i].reject();
12031         }
12032     },
12033
12034     onMetaChange : function(meta, rtype, o){
12035         this.recordType = rtype;
12036         this.fields = rtype.prototype.fields;
12037         delete this.snapshot;
12038         this.sortInfo = meta.sortInfo || this.sortInfo;
12039         this.modified = [];
12040         this.fireEvent('metachange', this, this.reader.meta);
12041     },
12042     
12043     moveIndex : function(data, type)
12044     {
12045         var index = this.indexOf(data);
12046         
12047         var newIndex = index + type;
12048         
12049         this.remove(data);
12050         
12051         this.insert(newIndex, data);
12052         
12053     }
12054 });/*
12055  * Based on:
12056  * Ext JS Library 1.1.1
12057  * Copyright(c) 2006-2007, Ext JS, LLC.
12058  *
12059  * Originally Released Under LGPL - original licence link has changed is not relivant.
12060  *
12061  * Fork - LGPL
12062  * <script type="text/javascript">
12063  */
12064
12065 /**
12066  * @class Roo.data.SimpleStore
12067  * @extends Roo.data.Store
12068  * Small helper class to make creating Stores from Array data easier.
12069  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12070  * @cfg {Array} fields An array of field definition objects, or field name strings.
12071  * @cfg {Array} data The multi-dimensional array of data
12072  * @constructor
12073  * @param {Object} config
12074  */
12075 Roo.data.SimpleStore = function(config){
12076     Roo.data.SimpleStore.superclass.constructor.call(this, {
12077         isLocal : true,
12078         reader: new Roo.data.ArrayReader({
12079                 id: config.id
12080             },
12081             Roo.data.Record.create(config.fields)
12082         ),
12083         proxy : new Roo.data.MemoryProxy(config.data)
12084     });
12085     this.load();
12086 };
12087 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12088  * Based on:
12089  * Ext JS Library 1.1.1
12090  * Copyright(c) 2006-2007, Ext JS, LLC.
12091  *
12092  * Originally Released Under LGPL - original licence link has changed is not relivant.
12093  *
12094  * Fork - LGPL
12095  * <script type="text/javascript">
12096  */
12097
12098 /**
12099 /**
12100  * @extends Roo.data.Store
12101  * @class Roo.data.JsonStore
12102  * Small helper class to make creating Stores for JSON data easier. <br/>
12103 <pre><code>
12104 var store = new Roo.data.JsonStore({
12105     url: 'get-images.php',
12106     root: 'images',
12107     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12108 });
12109 </code></pre>
12110  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12111  * JsonReader and HttpProxy (unless inline data is provided).</b>
12112  * @cfg {Array} fields An array of field definition objects, or field name strings.
12113  * @constructor
12114  * @param {Object} config
12115  */
12116 Roo.data.JsonStore = function(c){
12117     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12118         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12119         reader: new Roo.data.JsonReader(c, c.fields)
12120     }));
12121 };
12122 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12123  * Based on:
12124  * Ext JS Library 1.1.1
12125  * Copyright(c) 2006-2007, Ext JS, LLC.
12126  *
12127  * Originally Released Under LGPL - original licence link has changed is not relivant.
12128  *
12129  * Fork - LGPL
12130  * <script type="text/javascript">
12131  */
12132
12133  
12134 Roo.data.Field = function(config){
12135     if(typeof config == "string"){
12136         config = {name: config};
12137     }
12138     Roo.apply(this, config);
12139     
12140     if(!this.type){
12141         this.type = "auto";
12142     }
12143     
12144     var st = Roo.data.SortTypes;
12145     // named sortTypes are supported, here we look them up
12146     if(typeof this.sortType == "string"){
12147         this.sortType = st[this.sortType];
12148     }
12149     
12150     // set default sortType for strings and dates
12151     if(!this.sortType){
12152         switch(this.type){
12153             case "string":
12154                 this.sortType = st.asUCString;
12155                 break;
12156             case "date":
12157                 this.sortType = st.asDate;
12158                 break;
12159             default:
12160                 this.sortType = st.none;
12161         }
12162     }
12163
12164     // define once
12165     var stripRe = /[\$,%]/g;
12166
12167     // prebuilt conversion function for this field, instead of
12168     // switching every time we're reading a value
12169     if(!this.convert){
12170         var cv, dateFormat = this.dateFormat;
12171         switch(this.type){
12172             case "":
12173             case "auto":
12174             case undefined:
12175                 cv = function(v){ return v; };
12176                 break;
12177             case "string":
12178                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12179                 break;
12180             case "int":
12181                 cv = function(v){
12182                     return v !== undefined && v !== null && v !== '' ?
12183                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12184                     };
12185                 break;
12186             case "float":
12187                 cv = function(v){
12188                     return v !== undefined && v !== null && v !== '' ?
12189                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12190                     };
12191                 break;
12192             case "bool":
12193             case "boolean":
12194                 cv = function(v){ return v === true || v === "true" || v == 1; };
12195                 break;
12196             case "date":
12197                 cv = function(v){
12198                     if(!v){
12199                         return '';
12200                     }
12201                     if(v instanceof Date){
12202                         return v;
12203                     }
12204                     if(dateFormat){
12205                         if(dateFormat == "timestamp"){
12206                             return new Date(v*1000);
12207                         }
12208                         return Date.parseDate(v, dateFormat);
12209                     }
12210                     var parsed = Date.parse(v);
12211                     return parsed ? new Date(parsed) : null;
12212                 };
12213              break;
12214             
12215         }
12216         this.convert = cv;
12217     }
12218 };
12219
12220 Roo.data.Field.prototype = {
12221     dateFormat: null,
12222     defaultValue: "",
12223     mapping: null,
12224     sortType : null,
12225     sortDir : "ASC"
12226 };/*
12227  * Based on:
12228  * Ext JS Library 1.1.1
12229  * Copyright(c) 2006-2007, Ext JS, LLC.
12230  *
12231  * Originally Released Under LGPL - original licence link has changed is not relivant.
12232  *
12233  * Fork - LGPL
12234  * <script type="text/javascript">
12235  */
12236  
12237 // Base class for reading structured data from a data source.  This class is intended to be
12238 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12239
12240 /**
12241  * @class Roo.data.DataReader
12242  * Base class for reading structured data from a data source.  This class is intended to be
12243  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12244  */
12245
12246 Roo.data.DataReader = function(meta, recordType){
12247     
12248     this.meta = meta;
12249     
12250     this.recordType = recordType instanceof Array ? 
12251         Roo.data.Record.create(recordType) : recordType;
12252 };
12253
12254 Roo.data.DataReader.prototype = {
12255      /**
12256      * Create an empty record
12257      * @param {Object} data (optional) - overlay some values
12258      * @return {Roo.data.Record} record created.
12259      */
12260     newRow :  function(d) {
12261         var da =  {};
12262         this.recordType.prototype.fields.each(function(c) {
12263             switch( c.type) {
12264                 case 'int' : da[c.name] = 0; break;
12265                 case 'date' : da[c.name] = new Date(); break;
12266                 case 'float' : da[c.name] = 0.0; break;
12267                 case 'boolean' : da[c.name] = false; break;
12268                 default : da[c.name] = ""; break;
12269             }
12270             
12271         });
12272         return new this.recordType(Roo.apply(da, d));
12273     }
12274     
12275 };/*
12276  * Based on:
12277  * Ext JS Library 1.1.1
12278  * Copyright(c) 2006-2007, Ext JS, LLC.
12279  *
12280  * Originally Released Under LGPL - original licence link has changed is not relivant.
12281  *
12282  * Fork - LGPL
12283  * <script type="text/javascript">
12284  */
12285
12286 /**
12287  * @class Roo.data.DataProxy
12288  * @extends Roo.data.Observable
12289  * This class is an abstract base class for implementations which provide retrieval of
12290  * unformatted data objects.<br>
12291  * <p>
12292  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12293  * (of the appropriate type which knows how to parse the data object) to provide a block of
12294  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12295  * <p>
12296  * Custom implementations must implement the load method as described in
12297  * {@link Roo.data.HttpProxy#load}.
12298  */
12299 Roo.data.DataProxy = function(){
12300     this.addEvents({
12301         /**
12302          * @event beforeload
12303          * Fires before a network request is made to retrieve a data object.
12304          * @param {Object} This DataProxy object.
12305          * @param {Object} params The params parameter to the load function.
12306          */
12307         beforeload : true,
12308         /**
12309          * @event load
12310          * Fires before the load method's callback is called.
12311          * @param {Object} This DataProxy object.
12312          * @param {Object} o The data object.
12313          * @param {Object} arg The callback argument object passed to the load function.
12314          */
12315         load : true,
12316         /**
12317          * @event loadexception
12318          * Fires if an Exception occurs during data retrieval.
12319          * @param {Object} This DataProxy object.
12320          * @param {Object} o The data object.
12321          * @param {Object} arg The callback argument object passed to the load function.
12322          * @param {Object} e The Exception.
12323          */
12324         loadexception : true
12325     });
12326     Roo.data.DataProxy.superclass.constructor.call(this);
12327 };
12328
12329 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12330
12331     /**
12332      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12333      */
12334 /*
12335  * Based on:
12336  * Ext JS Library 1.1.1
12337  * Copyright(c) 2006-2007, Ext JS, LLC.
12338  *
12339  * Originally Released Under LGPL - original licence link has changed is not relivant.
12340  *
12341  * Fork - LGPL
12342  * <script type="text/javascript">
12343  */
12344 /**
12345  * @class Roo.data.MemoryProxy
12346  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12347  * to the Reader when its load method is called.
12348  * @constructor
12349  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12350  */
12351 Roo.data.MemoryProxy = function(data){
12352     if (data.data) {
12353         data = data.data;
12354     }
12355     Roo.data.MemoryProxy.superclass.constructor.call(this);
12356     this.data = data;
12357 };
12358
12359 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12360     
12361     /**
12362      * Load data from the requested source (in this case an in-memory
12363      * data object passed to the constructor), read the data object into
12364      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12365      * process that block using the passed callback.
12366      * @param {Object} params This parameter is not used by the MemoryProxy class.
12367      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12368      * object into a block of Roo.data.Records.
12369      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12370      * The function must be passed <ul>
12371      * <li>The Record block object</li>
12372      * <li>The "arg" argument from the load function</li>
12373      * <li>A boolean success indicator</li>
12374      * </ul>
12375      * @param {Object} scope The scope in which to call the callback
12376      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12377      */
12378     load : function(params, reader, callback, scope, arg){
12379         params = params || {};
12380         var result;
12381         try {
12382             result = reader.readRecords(this.data);
12383         }catch(e){
12384             this.fireEvent("loadexception", this, arg, null, e);
12385             callback.call(scope, null, arg, false);
12386             return;
12387         }
12388         callback.call(scope, result, arg, true);
12389     },
12390     
12391     // private
12392     update : function(params, records){
12393         
12394     }
12395 });/*
12396  * Based on:
12397  * Ext JS Library 1.1.1
12398  * Copyright(c) 2006-2007, Ext JS, LLC.
12399  *
12400  * Originally Released Under LGPL - original licence link has changed is not relivant.
12401  *
12402  * Fork - LGPL
12403  * <script type="text/javascript">
12404  */
12405 /**
12406  * @class Roo.data.HttpProxy
12407  * @extends Roo.data.DataProxy
12408  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12409  * configured to reference a certain URL.<br><br>
12410  * <p>
12411  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12412  * from which the running page was served.<br><br>
12413  * <p>
12414  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12415  * <p>
12416  * Be aware that to enable the browser to parse an XML document, the server must set
12417  * the Content-Type header in the HTTP response to "text/xml".
12418  * @constructor
12419  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12420  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12421  * will be used to make the request.
12422  */
12423 Roo.data.HttpProxy = function(conn){
12424     Roo.data.HttpProxy.superclass.constructor.call(this);
12425     // is conn a conn config or a real conn?
12426     this.conn = conn;
12427     this.useAjax = !conn || !conn.events;
12428   
12429 };
12430
12431 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12432     // thse are take from connection...
12433     
12434     /**
12435      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12436      */
12437     /**
12438      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12439      * extra parameters to each request made by this object. (defaults to undefined)
12440      */
12441     /**
12442      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12443      *  to each request made by this object. (defaults to undefined)
12444      */
12445     /**
12446      * @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)
12447      */
12448     /**
12449      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12450      */
12451      /**
12452      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12453      * @type Boolean
12454      */
12455   
12456
12457     /**
12458      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12459      * @type Boolean
12460      */
12461     /**
12462      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12463      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12464      * a finer-grained basis than the DataProxy events.
12465      */
12466     getConnection : function(){
12467         return this.useAjax ? Roo.Ajax : this.conn;
12468     },
12469
12470     /**
12471      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12472      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12473      * process that block using the passed callback.
12474      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12475      * for the request to the remote server.
12476      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12477      * object into a block of Roo.data.Records.
12478      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12479      * The function must be passed <ul>
12480      * <li>The Record block object</li>
12481      * <li>The "arg" argument from the load function</li>
12482      * <li>A boolean success indicator</li>
12483      * </ul>
12484      * @param {Object} scope The scope in which to call the callback
12485      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12486      */
12487     load : function(params, reader, callback, scope, arg){
12488         if(this.fireEvent("beforeload", this, params) !== false){
12489             var  o = {
12490                 params : params || {},
12491                 request: {
12492                     callback : callback,
12493                     scope : scope,
12494                     arg : arg
12495                 },
12496                 reader: reader,
12497                 callback : this.loadResponse,
12498                 scope: this
12499             };
12500             if(this.useAjax){
12501                 Roo.applyIf(o, this.conn);
12502                 if(this.activeRequest){
12503                     Roo.Ajax.abort(this.activeRequest);
12504                 }
12505                 this.activeRequest = Roo.Ajax.request(o);
12506             }else{
12507                 this.conn.request(o);
12508             }
12509         }else{
12510             callback.call(scope||this, null, arg, false);
12511         }
12512     },
12513
12514     // private
12515     loadResponse : function(o, success, response){
12516         delete this.activeRequest;
12517         if(!success){
12518             this.fireEvent("loadexception", this, o, response);
12519             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12520             return;
12521         }
12522         var result;
12523         try {
12524             result = o.reader.read(response);
12525         }catch(e){
12526             this.fireEvent("loadexception", this, o, response, e);
12527             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12528             return;
12529         }
12530         
12531         this.fireEvent("load", this, o, o.request.arg);
12532         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12533     },
12534
12535     // private
12536     update : function(dataSet){
12537
12538     },
12539
12540     // private
12541     updateResponse : function(dataSet){
12542
12543     }
12544 });/*
12545  * Based on:
12546  * Ext JS Library 1.1.1
12547  * Copyright(c) 2006-2007, Ext JS, LLC.
12548  *
12549  * Originally Released Under LGPL - original licence link has changed is not relivant.
12550  *
12551  * Fork - LGPL
12552  * <script type="text/javascript">
12553  */
12554
12555 /**
12556  * @class Roo.data.ScriptTagProxy
12557  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12558  * other than the originating domain of the running page.<br><br>
12559  * <p>
12560  * <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
12561  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12562  * <p>
12563  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12564  * source code that is used as the source inside a &lt;script> tag.<br><br>
12565  * <p>
12566  * In order for the browser to process the returned data, the server must wrap the data object
12567  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12568  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12569  * depending on whether the callback name was passed:
12570  * <p>
12571  * <pre><code>
12572 boolean scriptTag = false;
12573 String cb = request.getParameter("callback");
12574 if (cb != null) {
12575     scriptTag = true;
12576     response.setContentType("text/javascript");
12577 } else {
12578     response.setContentType("application/x-json");
12579 }
12580 Writer out = response.getWriter();
12581 if (scriptTag) {
12582     out.write(cb + "(");
12583 }
12584 out.print(dataBlock.toJsonString());
12585 if (scriptTag) {
12586     out.write(");");
12587 }
12588 </pre></code>
12589  *
12590  * @constructor
12591  * @param {Object} config A configuration object.
12592  */
12593 Roo.data.ScriptTagProxy = function(config){
12594     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12595     Roo.apply(this, config);
12596     this.head = document.getElementsByTagName("head")[0];
12597 };
12598
12599 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12600
12601 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12602     /**
12603      * @cfg {String} url The URL from which to request the data object.
12604      */
12605     /**
12606      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12607      */
12608     timeout : 30000,
12609     /**
12610      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12611      * the server the name of the callback function set up by the load call to process the returned data object.
12612      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12613      * javascript output which calls this named function passing the data object as its only parameter.
12614      */
12615     callbackParam : "callback",
12616     /**
12617      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12618      * name to the request.
12619      */
12620     nocache : true,
12621
12622     /**
12623      * Load data from the configured URL, read the data object into
12624      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12625      * process that block using the passed callback.
12626      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12627      * for the request to the remote server.
12628      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12629      * object into a block of Roo.data.Records.
12630      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12631      * The function must be passed <ul>
12632      * <li>The Record block object</li>
12633      * <li>The "arg" argument from the load function</li>
12634      * <li>A boolean success indicator</li>
12635      * </ul>
12636      * @param {Object} scope The scope in which to call the callback
12637      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12638      */
12639     load : function(params, reader, callback, scope, arg){
12640         if(this.fireEvent("beforeload", this, params) !== false){
12641
12642             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12643
12644             var url = this.url;
12645             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12646             if(this.nocache){
12647                 url += "&_dc=" + (new Date().getTime());
12648             }
12649             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12650             var trans = {
12651                 id : transId,
12652                 cb : "stcCallback"+transId,
12653                 scriptId : "stcScript"+transId,
12654                 params : params,
12655                 arg : arg,
12656                 url : url,
12657                 callback : callback,
12658                 scope : scope,
12659                 reader : reader
12660             };
12661             var conn = this;
12662
12663             window[trans.cb] = function(o){
12664                 conn.handleResponse(o, trans);
12665             };
12666
12667             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12668
12669             if(this.autoAbort !== false){
12670                 this.abort();
12671             }
12672
12673             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12674
12675             var script = document.createElement("script");
12676             script.setAttribute("src", url);
12677             script.setAttribute("type", "text/javascript");
12678             script.setAttribute("id", trans.scriptId);
12679             this.head.appendChild(script);
12680
12681             this.trans = trans;
12682         }else{
12683             callback.call(scope||this, null, arg, false);
12684         }
12685     },
12686
12687     // private
12688     isLoading : function(){
12689         return this.trans ? true : false;
12690     },
12691
12692     /**
12693      * Abort the current server request.
12694      */
12695     abort : function(){
12696         if(this.isLoading()){
12697             this.destroyTrans(this.trans);
12698         }
12699     },
12700
12701     // private
12702     destroyTrans : function(trans, isLoaded){
12703         this.head.removeChild(document.getElementById(trans.scriptId));
12704         clearTimeout(trans.timeoutId);
12705         if(isLoaded){
12706             window[trans.cb] = undefined;
12707             try{
12708                 delete window[trans.cb];
12709             }catch(e){}
12710         }else{
12711             // if hasn't been loaded, wait for load to remove it to prevent script error
12712             window[trans.cb] = function(){
12713                 window[trans.cb] = undefined;
12714                 try{
12715                     delete window[trans.cb];
12716                 }catch(e){}
12717             };
12718         }
12719     },
12720
12721     // private
12722     handleResponse : function(o, trans){
12723         this.trans = false;
12724         this.destroyTrans(trans, true);
12725         var result;
12726         try {
12727             result = trans.reader.readRecords(o);
12728         }catch(e){
12729             this.fireEvent("loadexception", this, o, trans.arg, e);
12730             trans.callback.call(trans.scope||window, null, trans.arg, false);
12731             return;
12732         }
12733         this.fireEvent("load", this, o, trans.arg);
12734         trans.callback.call(trans.scope||window, result, trans.arg, true);
12735     },
12736
12737     // private
12738     handleFailure : function(trans){
12739         this.trans = false;
12740         this.destroyTrans(trans, false);
12741         this.fireEvent("loadexception", this, null, trans.arg);
12742         trans.callback.call(trans.scope||window, null, trans.arg, false);
12743     }
12744 });/*
12745  * Based on:
12746  * Ext JS Library 1.1.1
12747  * Copyright(c) 2006-2007, Ext JS, LLC.
12748  *
12749  * Originally Released Under LGPL - original licence link has changed is not relivant.
12750  *
12751  * Fork - LGPL
12752  * <script type="text/javascript">
12753  */
12754
12755 /**
12756  * @class Roo.data.JsonReader
12757  * @extends Roo.data.DataReader
12758  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12759  * based on mappings in a provided Roo.data.Record constructor.
12760  * 
12761  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12762  * in the reply previously. 
12763  * 
12764  * <p>
12765  * Example code:
12766  * <pre><code>
12767 var RecordDef = Roo.data.Record.create([
12768     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12769     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12770 ]);
12771 var myReader = new Roo.data.JsonReader({
12772     totalProperty: "results",    // The property which contains the total dataset size (optional)
12773     root: "rows",                // The property which contains an Array of row objects
12774     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12775 }, RecordDef);
12776 </code></pre>
12777  * <p>
12778  * This would consume a JSON file like this:
12779  * <pre><code>
12780 { 'results': 2, 'rows': [
12781     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12782     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12783 }
12784 </code></pre>
12785  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12786  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12787  * paged from the remote server.
12788  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12789  * @cfg {String} root name of the property which contains the Array of row objects.
12790  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12791  * @cfg {Array} fields Array of field definition objects
12792  * @constructor
12793  * Create a new JsonReader
12794  * @param {Object} meta Metadata configuration options
12795  * @param {Object} recordType Either an Array of field definition objects,
12796  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12797  */
12798 Roo.data.JsonReader = function(meta, recordType){
12799     
12800     meta = meta || {};
12801     // set some defaults:
12802     Roo.applyIf(meta, {
12803         totalProperty: 'total',
12804         successProperty : 'success',
12805         root : 'data',
12806         id : 'id'
12807     });
12808     
12809     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12810 };
12811 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12812     
12813     /**
12814      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12815      * Used by Store query builder to append _requestMeta to params.
12816      * 
12817      */
12818     metaFromRemote : false,
12819     /**
12820      * This method is only used by a DataProxy which has retrieved data from a remote server.
12821      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12822      * @return {Object} data A data block which is used by an Roo.data.Store object as
12823      * a cache of Roo.data.Records.
12824      */
12825     read : function(response){
12826         var json = response.responseText;
12827        
12828         var o = /* eval:var:o */ eval("("+json+")");
12829         if(!o) {
12830             throw {message: "JsonReader.read: Json object not found"};
12831         }
12832         
12833         if(o.metaData){
12834             
12835             delete this.ef;
12836             this.metaFromRemote = true;
12837             this.meta = o.metaData;
12838             this.recordType = Roo.data.Record.create(o.metaData.fields);
12839             this.onMetaChange(this.meta, this.recordType, o);
12840         }
12841         return this.readRecords(o);
12842     },
12843
12844     // private function a store will implement
12845     onMetaChange : function(meta, recordType, o){
12846
12847     },
12848
12849     /**
12850          * @ignore
12851          */
12852     simpleAccess: function(obj, subsc) {
12853         return obj[subsc];
12854     },
12855
12856         /**
12857          * @ignore
12858          */
12859     getJsonAccessor: function(){
12860         var re = /[\[\.]/;
12861         return function(expr) {
12862             try {
12863                 return(re.test(expr))
12864                     ? new Function("obj", "return obj." + expr)
12865                     : function(obj){
12866                         return obj[expr];
12867                     };
12868             } catch(e){}
12869             return Roo.emptyFn;
12870         };
12871     }(),
12872
12873     /**
12874      * Create a data block containing Roo.data.Records from an XML document.
12875      * @param {Object} o An object which contains an Array of row objects in the property specified
12876      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12877      * which contains the total size of the dataset.
12878      * @return {Object} data A data block which is used by an Roo.data.Store object as
12879      * a cache of Roo.data.Records.
12880      */
12881     readRecords : function(o){
12882         /**
12883          * After any data loads, the raw JSON data is available for further custom processing.
12884          * @type Object
12885          */
12886         this.o = o;
12887         var s = this.meta, Record = this.recordType,
12888             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12889
12890 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12891         if (!this.ef) {
12892             if(s.totalProperty) {
12893                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12894                 }
12895                 if(s.successProperty) {
12896                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12897                 }
12898                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12899                 if (s.id) {
12900                         var g = this.getJsonAccessor(s.id);
12901                         this.getId = function(rec) {
12902                                 var r = g(rec);  
12903                                 return (r === undefined || r === "") ? null : r;
12904                         };
12905                 } else {
12906                         this.getId = function(){return null;};
12907                 }
12908             this.ef = [];
12909             for(var jj = 0; jj < fl; jj++){
12910                 f = fi[jj];
12911                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12912                 this.ef[jj] = this.getJsonAccessor(map);
12913             }
12914         }
12915
12916         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12917         if(s.totalProperty){
12918             var vt = parseInt(this.getTotal(o), 10);
12919             if(!isNaN(vt)){
12920                 totalRecords = vt;
12921             }
12922         }
12923         if(s.successProperty){
12924             var vs = this.getSuccess(o);
12925             if(vs === false || vs === 'false'){
12926                 success = false;
12927             }
12928         }
12929         var records = [];
12930         for(var i = 0; i < c; i++){
12931                 var n = root[i];
12932             var values = {};
12933             var id = this.getId(n);
12934             for(var j = 0; j < fl; j++){
12935                 f = fi[j];
12936             var v = this.ef[j](n);
12937             if (!f.convert) {
12938                 Roo.log('missing convert for ' + f.name);
12939                 Roo.log(f);
12940                 continue;
12941             }
12942             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12943             }
12944             var record = new Record(values, id);
12945             record.json = n;
12946             records[i] = record;
12947         }
12948         return {
12949             raw : o,
12950             success : success,
12951             records : records,
12952             totalRecords : totalRecords
12953         };
12954     }
12955 });/*
12956  * Based on:
12957  * Ext JS Library 1.1.1
12958  * Copyright(c) 2006-2007, Ext JS, LLC.
12959  *
12960  * Originally Released Under LGPL - original licence link has changed is not relivant.
12961  *
12962  * Fork - LGPL
12963  * <script type="text/javascript">
12964  */
12965
12966 /**
12967  * @class Roo.data.ArrayReader
12968  * @extends Roo.data.DataReader
12969  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12970  * Each element of that Array represents a row of data fields. The
12971  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12972  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12973  * <p>
12974  * Example code:.
12975  * <pre><code>
12976 var RecordDef = Roo.data.Record.create([
12977     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12978     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12979 ]);
12980 var myReader = new Roo.data.ArrayReader({
12981     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12982 }, RecordDef);
12983 </code></pre>
12984  * <p>
12985  * This would consume an Array like this:
12986  * <pre><code>
12987 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12988   </code></pre>
12989  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12990  * @constructor
12991  * Create a new JsonReader
12992  * @param {Object} meta Metadata configuration options.
12993  * @param {Object} recordType Either an Array of field definition objects
12994  * as specified to {@link Roo.data.Record#create},
12995  * or an {@link Roo.data.Record} object
12996  * created using {@link Roo.data.Record#create}.
12997  */
12998 Roo.data.ArrayReader = function(meta, recordType){
12999     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
13000 };
13001
13002 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13003     /**
13004      * Create a data block containing Roo.data.Records from an XML document.
13005      * @param {Object} o An Array of row objects which represents the dataset.
13006      * @return {Object} data A data block which is used by an Roo.data.Store object as
13007      * a cache of Roo.data.Records.
13008      */
13009     readRecords : function(o){
13010         var sid = this.meta ? this.meta.id : null;
13011         var recordType = this.recordType, fields = recordType.prototype.fields;
13012         var records = [];
13013         var root = o;
13014             for(var i = 0; i < root.length; i++){
13015                     var n = root[i];
13016                 var values = {};
13017                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13018                 for(var j = 0, jlen = fields.length; j < jlen; j++){
13019                 var f = fields.items[j];
13020                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13021                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13022                 v = f.convert(v);
13023                 values[f.name] = v;
13024             }
13025                 var record = new recordType(values, id);
13026                 record.json = n;
13027                 records[records.length] = record;
13028             }
13029             return {
13030                 records : records,
13031                 totalRecords : records.length
13032             };
13033     }
13034 });/*
13035  * - LGPL
13036  * * 
13037  */
13038
13039 /**
13040  * @class Roo.bootstrap.ComboBox
13041  * @extends Roo.bootstrap.TriggerField
13042  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13043  * @cfg {Boolean} append (true|false) default false
13044  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13045  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13046  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13047  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13048  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13049  * @cfg {Boolean} animate default true
13050  * @cfg {Boolean} emptyResultText only for touch device
13051  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13052  * @cfg {String} emptyTitle default ''
13053  * @constructor
13054  * Create a new ComboBox.
13055  * @param {Object} config Configuration options
13056  */
13057 Roo.bootstrap.ComboBox = function(config){
13058     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13059     this.addEvents({
13060         /**
13061          * @event expand
13062          * Fires when the dropdown list is expanded
13063         * @param {Roo.bootstrap.ComboBox} combo This combo box
13064         */
13065         'expand' : true,
13066         /**
13067          * @event collapse
13068          * Fires when the dropdown list is collapsed
13069         * @param {Roo.bootstrap.ComboBox} combo This combo box
13070         */
13071         'collapse' : true,
13072         /**
13073          * @event beforeselect
13074          * Fires before a list item is selected. Return false to cancel the selection.
13075         * @param {Roo.bootstrap.ComboBox} combo This combo box
13076         * @param {Roo.data.Record} record The data record returned from the underlying store
13077         * @param {Number} index The index of the selected item in the dropdown list
13078         */
13079         'beforeselect' : true,
13080         /**
13081          * @event select
13082          * Fires when a list item is selected
13083         * @param {Roo.bootstrap.ComboBox} combo This combo box
13084         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13085         * @param {Number} index The index of the selected item in the dropdown list
13086         */
13087         'select' : true,
13088         /**
13089          * @event beforequery
13090          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13091          * The event object passed has these properties:
13092         * @param {Roo.bootstrap.ComboBox} combo This combo box
13093         * @param {String} query The query
13094         * @param {Boolean} forceAll true to force "all" query
13095         * @param {Boolean} cancel true to cancel the query
13096         * @param {Object} e The query event object
13097         */
13098         'beforequery': true,
13099          /**
13100          * @event add
13101          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13102         * @param {Roo.bootstrap.ComboBox} combo This combo box
13103         */
13104         'add' : true,
13105         /**
13106          * @event edit
13107          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13108         * @param {Roo.bootstrap.ComboBox} combo This combo box
13109         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13110         */
13111         'edit' : true,
13112         /**
13113          * @event remove
13114          * Fires when the remove value from the combobox array
13115         * @param {Roo.bootstrap.ComboBox} combo This combo box
13116         */
13117         'remove' : true,
13118         /**
13119          * @event afterremove
13120          * Fires when the remove value from the combobox array
13121         * @param {Roo.bootstrap.ComboBox} combo This combo box
13122         */
13123         'afterremove' : true,
13124         /**
13125          * @event specialfilter
13126          * Fires when specialfilter
13127             * @param {Roo.bootstrap.ComboBox} combo This combo box
13128             */
13129         'specialfilter' : true,
13130         /**
13131          * @event tick
13132          * Fires when tick the element
13133             * @param {Roo.bootstrap.ComboBox} combo This combo box
13134             */
13135         'tick' : true,
13136         /**
13137          * @event touchviewdisplay
13138          * Fires when touch view require special display (default is using displayField)
13139             * @param {Roo.bootstrap.ComboBox} combo This combo box
13140             * @param {Object} cfg set html .
13141             */
13142         'touchviewdisplay' : true
13143         
13144     });
13145     
13146     this.item = [];
13147     this.tickItems = [];
13148     
13149     this.selectedIndex = -1;
13150     if(this.mode == 'local'){
13151         if(config.queryDelay === undefined){
13152             this.queryDelay = 10;
13153         }
13154         if(config.minChars === undefined){
13155             this.minChars = 0;
13156         }
13157     }
13158 };
13159
13160 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13161      
13162     /**
13163      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13164      * rendering into an Roo.Editor, defaults to false)
13165      */
13166     /**
13167      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13168      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13169      */
13170     /**
13171      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13172      */
13173     /**
13174      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13175      * the dropdown list (defaults to undefined, with no header element)
13176      */
13177
13178      /**
13179      * @cfg {String/Roo.Template} tpl The template to use to render the output
13180      */
13181      
13182      /**
13183      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13184      */
13185     listWidth: undefined,
13186     /**
13187      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13188      * mode = 'remote' or 'text' if mode = 'local')
13189      */
13190     displayField: undefined,
13191     
13192     /**
13193      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13194      * mode = 'remote' or 'value' if mode = 'local'). 
13195      * Note: use of a valueField requires the user make a selection
13196      * in order for a value to be mapped.
13197      */
13198     valueField: undefined,
13199     /**
13200      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13201      */
13202     modalTitle : '',
13203     
13204     /**
13205      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13206      * field's data value (defaults to the underlying DOM element's name)
13207      */
13208     hiddenName: undefined,
13209     /**
13210      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13211      */
13212     listClass: '',
13213     /**
13214      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13215      */
13216     selectedClass: 'active',
13217     
13218     /**
13219      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13220      */
13221     shadow:'sides',
13222     /**
13223      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13224      * anchor positions (defaults to 'tl-bl')
13225      */
13226     listAlign: 'tl-bl?',
13227     /**
13228      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13229      */
13230     maxHeight: 300,
13231     /**
13232      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13233      * query specified by the allQuery config option (defaults to 'query')
13234      */
13235     triggerAction: 'query',
13236     /**
13237      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13238      * (defaults to 4, does not apply if editable = false)
13239      */
13240     minChars : 4,
13241     /**
13242      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13243      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13244      */
13245     typeAhead: false,
13246     /**
13247      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13248      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13249      */
13250     queryDelay: 500,
13251     /**
13252      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13253      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13254      */
13255     pageSize: 0,
13256     /**
13257      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13258      * when editable = true (defaults to false)
13259      */
13260     selectOnFocus:false,
13261     /**
13262      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13263      */
13264     queryParam: 'query',
13265     /**
13266      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13267      * when mode = 'remote' (defaults to 'Loading...')
13268      */
13269     loadingText: 'Loading...',
13270     /**
13271      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13272      */
13273     resizable: false,
13274     /**
13275      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13276      */
13277     handleHeight : 8,
13278     /**
13279      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13280      * traditional select (defaults to true)
13281      */
13282     editable: true,
13283     /**
13284      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13285      */
13286     allQuery: '',
13287     /**
13288      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13289      */
13290     mode: 'remote',
13291     /**
13292      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13293      * listWidth has a higher value)
13294      */
13295     minListWidth : 70,
13296     /**
13297      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13298      * allow the user to set arbitrary text into the field (defaults to false)
13299      */
13300     forceSelection:false,
13301     /**
13302      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13303      * if typeAhead = true (defaults to 250)
13304      */
13305     typeAheadDelay : 250,
13306     /**
13307      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13308      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13309      */
13310     valueNotFoundText : undefined,
13311     /**
13312      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13313      */
13314     blockFocus : false,
13315     
13316     /**
13317      * @cfg {Boolean} disableClear Disable showing of clear button.
13318      */
13319     disableClear : false,
13320     /**
13321      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13322      */
13323     alwaysQuery : false,
13324     
13325     /**
13326      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13327      */
13328     multiple : false,
13329     
13330     /**
13331      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13332      */
13333     invalidClass : "has-warning",
13334     
13335     /**
13336      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13337      */
13338     validClass : "has-success",
13339     
13340     /**
13341      * @cfg {Boolean} specialFilter (true|false) special filter default false
13342      */
13343     specialFilter : false,
13344     
13345     /**
13346      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13347      */
13348     mobileTouchView : true,
13349     
13350     /**
13351      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13352      */
13353     useNativeIOS : false,
13354     
13355     /**
13356      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13357      */
13358     mobile_restrict_height : false,
13359     
13360     ios_options : false,
13361     
13362     //private
13363     addicon : false,
13364     editicon: false,
13365     
13366     page: 0,
13367     hasQuery: false,
13368     append: false,
13369     loadNext: false,
13370     autoFocus : true,
13371     tickable : false,
13372     btnPosition : 'right',
13373     triggerList : true,
13374     showToggleBtn : true,
13375     animate : true,
13376     emptyResultText: 'Empty',
13377     triggerText : 'Select',
13378     emptyTitle : '',
13379     
13380     // element that contains real text value.. (when hidden is used..)
13381     
13382     getAutoCreate : function()
13383     {   
13384         var cfg = false;
13385         //render
13386         /*
13387          * Render classic select for iso
13388          */
13389         
13390         if(Roo.isIOS && this.useNativeIOS){
13391             cfg = this.getAutoCreateNativeIOS();
13392             return cfg;
13393         }
13394         
13395         /*
13396          * Touch Devices
13397          */
13398         
13399         if(Roo.isTouch && this.mobileTouchView){
13400             cfg = this.getAutoCreateTouchView();
13401             return cfg;;
13402         }
13403         
13404         /*
13405          *  Normal ComboBox
13406          */
13407         if(!this.tickable){
13408             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13409             return cfg;
13410         }
13411         
13412         /*
13413          *  ComboBox with tickable selections
13414          */
13415              
13416         var align = this.labelAlign || this.parentLabelAlign();
13417         
13418         cfg = {
13419             cls : 'form-group roo-combobox-tickable' //input-group
13420         };
13421         
13422         var btn_text_select = '';
13423         var btn_text_done = '';
13424         var btn_text_cancel = '';
13425         
13426         if (this.btn_text_show) {
13427             btn_text_select = 'Select';
13428             btn_text_done = 'Done';
13429             btn_text_cancel = 'Cancel'; 
13430         }
13431         
13432         var buttons = {
13433             tag : 'div',
13434             cls : 'tickable-buttons',
13435             cn : [
13436                 {
13437                     tag : 'button',
13438                     type : 'button',
13439                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13440                     //html : this.triggerText
13441                     html: btn_text_select
13442                 },
13443                 {
13444                     tag : 'button',
13445                     type : 'button',
13446                     name : 'ok',
13447                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13448                     //html : 'Done'
13449                     html: btn_text_done
13450                 },
13451                 {
13452                     tag : 'button',
13453                     type : 'button',
13454                     name : 'cancel',
13455                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13456                     //html : 'Cancel'
13457                     html: btn_text_cancel
13458                 }
13459             ]
13460         };
13461         
13462         if(this.editable){
13463             buttons.cn.unshift({
13464                 tag: 'input',
13465                 cls: 'roo-select2-search-field-input'
13466             });
13467         }
13468         
13469         var _this = this;
13470         
13471         Roo.each(buttons.cn, function(c){
13472             if (_this.size) {
13473                 c.cls += ' btn-' + _this.size;
13474             }
13475
13476             if (_this.disabled) {
13477                 c.disabled = true;
13478             }
13479         });
13480         
13481         var box = {
13482             tag: 'div',
13483             style : 'display: contents',
13484             cn: [
13485                 {
13486                     tag: 'input',
13487                     type : 'hidden',
13488                     cls: 'form-hidden-field'
13489                 },
13490                 {
13491                     tag: 'ul',
13492                     cls: 'roo-select2-choices',
13493                     cn:[
13494                         {
13495                             tag: 'li',
13496                             cls: 'roo-select2-search-field',
13497                             cn: [
13498                                 buttons
13499                             ]
13500                         }
13501                     ]
13502                 }
13503             ]
13504         };
13505         
13506         var combobox = {
13507             cls: 'roo-select2-container input-group roo-select2-container-multi',
13508             cn: [
13509                 
13510                 box
13511 //                {
13512 //                    tag: 'ul',
13513 //                    cls: 'typeahead typeahead-long dropdown-menu',
13514 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13515 //                }
13516             ]
13517         };
13518         
13519         if(this.hasFeedback && !this.allowBlank){
13520             
13521             var feedback = {
13522                 tag: 'span',
13523                 cls: 'glyphicon form-control-feedback'
13524             };
13525
13526             combobox.cn.push(feedback);
13527         }
13528         
13529         var indicator = {
13530             tag : 'i',
13531             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13532             tooltip : 'This field is required'
13533         };
13534         if (Roo.bootstrap.version == 4) {
13535             indicator = {
13536                 tag : 'i',
13537                 style : 'display:none'
13538             };
13539         }
13540         if (align ==='left' && this.fieldLabel.length) {
13541             
13542             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13543             
13544             cfg.cn = [
13545                 indicator,
13546                 {
13547                     tag: 'label',
13548                     'for' :  id,
13549                     cls : 'control-label col-form-label',
13550                     html : this.fieldLabel
13551
13552                 },
13553                 {
13554                     cls : "", 
13555                     cn: [
13556                         combobox
13557                     ]
13558                 }
13559
13560             ];
13561             
13562             var labelCfg = cfg.cn[1];
13563             var contentCfg = cfg.cn[2];
13564             
13565
13566             if(this.indicatorpos == 'right'){
13567                 
13568                 cfg.cn = [
13569                     {
13570                         tag: 'label',
13571                         'for' :  id,
13572                         cls : 'control-label col-form-label',
13573                         cn : [
13574                             {
13575                                 tag : 'span',
13576                                 html : this.fieldLabel
13577                             },
13578                             indicator
13579                         ]
13580                     },
13581                     {
13582                         cls : "",
13583                         cn: [
13584                             combobox
13585                         ]
13586                     }
13587
13588                 ];
13589                 
13590                 
13591                 
13592                 labelCfg = cfg.cn[0];
13593                 contentCfg = cfg.cn[1];
13594             
13595             }
13596             
13597             if(this.labelWidth > 12){
13598                 labelCfg.style = "width: " + this.labelWidth + 'px';
13599             }
13600             
13601             if(this.labelWidth < 13 && this.labelmd == 0){
13602                 this.labelmd = this.labelWidth;
13603             }
13604             
13605             if(this.labellg > 0){
13606                 labelCfg.cls += ' col-lg-' + this.labellg;
13607                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13608             }
13609             
13610             if(this.labelmd > 0){
13611                 labelCfg.cls += ' col-md-' + this.labelmd;
13612                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13613             }
13614             
13615             if(this.labelsm > 0){
13616                 labelCfg.cls += ' col-sm-' + this.labelsm;
13617                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13618             }
13619             
13620             if(this.labelxs > 0){
13621                 labelCfg.cls += ' col-xs-' + this.labelxs;
13622                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13623             }
13624                 
13625                 
13626         } else if ( this.fieldLabel.length) {
13627 //                Roo.log(" label");
13628                  cfg.cn = [
13629                    indicator,
13630                     {
13631                         tag: 'label',
13632                         //cls : 'input-group-addon',
13633                         html : this.fieldLabel
13634                     },
13635                     combobox
13636                 ];
13637                 
13638                 if(this.indicatorpos == 'right'){
13639                     cfg.cn = [
13640                         {
13641                             tag: 'label',
13642                             //cls : 'input-group-addon',
13643                             html : this.fieldLabel
13644                         },
13645                         indicator,
13646                         combobox
13647                     ];
13648                     
13649                 }
13650
13651         } else {
13652             
13653 //                Roo.log(" no label && no align");
13654                 cfg = combobox
13655                      
13656                 
13657         }
13658          
13659         var settings=this;
13660         ['xs','sm','md','lg'].map(function(size){
13661             if (settings[size]) {
13662                 cfg.cls += ' col-' + size + '-' + settings[size];
13663             }
13664         });
13665         
13666         return cfg;
13667         
13668     },
13669     
13670     _initEventsCalled : false,
13671     
13672     // private
13673     initEvents: function()
13674     {   
13675         if (this._initEventsCalled) { // as we call render... prevent looping...
13676             return;
13677         }
13678         this._initEventsCalled = true;
13679         
13680         if (!this.store) {
13681             throw "can not find store for combo";
13682         }
13683         
13684         this.indicator = this.indicatorEl();
13685         
13686         this.store = Roo.factory(this.store, Roo.data);
13687         this.store.parent = this;
13688         
13689         // if we are building from html. then this element is so complex, that we can not really
13690         // use the rendered HTML.
13691         // so we have to trash and replace the previous code.
13692         if (Roo.XComponent.build_from_html) {
13693             // remove this element....
13694             var e = this.el.dom, k=0;
13695             while (e ) { e = e.previousSibling;  ++k;}
13696
13697             this.el.remove();
13698             
13699             this.el=false;
13700             this.rendered = false;
13701             
13702             this.render(this.parent().getChildContainer(true), k);
13703         }
13704         
13705         if(Roo.isIOS && this.useNativeIOS){
13706             this.initIOSView();
13707             return;
13708         }
13709         
13710         /*
13711          * Touch Devices
13712          */
13713         
13714         if(Roo.isTouch && this.mobileTouchView){
13715             this.initTouchView();
13716             return;
13717         }
13718         
13719         if(this.tickable){
13720             this.initTickableEvents();
13721             return;
13722         }
13723         
13724         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13725         
13726         if(this.hiddenName){
13727             
13728             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13729             
13730             this.hiddenField.dom.value =
13731                 this.hiddenValue !== undefined ? this.hiddenValue :
13732                 this.value !== undefined ? this.value : '';
13733
13734             // prevent input submission
13735             this.el.dom.removeAttribute('name');
13736             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13737              
13738              
13739         }
13740         //if(Roo.isGecko){
13741         //    this.el.dom.setAttribute('autocomplete', 'off');
13742         //}
13743         
13744         var cls = 'x-combo-list';
13745         
13746         //this.list = new Roo.Layer({
13747         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13748         //});
13749         
13750         var _this = this;
13751         
13752         (function(){
13753             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13754             _this.list.setWidth(lw);
13755         }).defer(100);
13756         
13757         this.list.on('mouseover', this.onViewOver, this);
13758         this.list.on('mousemove', this.onViewMove, this);
13759         this.list.on('scroll', this.onViewScroll, this);
13760         
13761         /*
13762         this.list.swallowEvent('mousewheel');
13763         this.assetHeight = 0;
13764
13765         if(this.title){
13766             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13767             this.assetHeight += this.header.getHeight();
13768         }
13769
13770         this.innerList = this.list.createChild({cls:cls+'-inner'});
13771         this.innerList.on('mouseover', this.onViewOver, this);
13772         this.innerList.on('mousemove', this.onViewMove, this);
13773         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13774         
13775         if(this.allowBlank && !this.pageSize && !this.disableClear){
13776             this.footer = this.list.createChild({cls:cls+'-ft'});
13777             this.pageTb = new Roo.Toolbar(this.footer);
13778            
13779         }
13780         if(this.pageSize){
13781             this.footer = this.list.createChild({cls:cls+'-ft'});
13782             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13783                     {pageSize: this.pageSize});
13784             
13785         }
13786         
13787         if (this.pageTb && this.allowBlank && !this.disableClear) {
13788             var _this = this;
13789             this.pageTb.add(new Roo.Toolbar.Fill(), {
13790                 cls: 'x-btn-icon x-btn-clear',
13791                 text: '&#160;',
13792                 handler: function()
13793                 {
13794                     _this.collapse();
13795                     _this.clearValue();
13796                     _this.onSelect(false, -1);
13797                 }
13798             });
13799         }
13800         if (this.footer) {
13801             this.assetHeight += this.footer.getHeight();
13802         }
13803         */
13804             
13805         if(!this.tpl){
13806             this.tpl = Roo.bootstrap.version == 4 ?
13807                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13808                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13809         }
13810
13811         this.view = new Roo.View(this.list, this.tpl, {
13812             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13813         });
13814         //this.view.wrapEl.setDisplayed(false);
13815         this.view.on('click', this.onViewClick, this);
13816         
13817         
13818         this.store.on('beforeload', this.onBeforeLoad, this);
13819         this.store.on('load', this.onLoad, this);
13820         this.store.on('loadexception', this.onLoadException, this);
13821         /*
13822         if(this.resizable){
13823             this.resizer = new Roo.Resizable(this.list,  {
13824                pinned:true, handles:'se'
13825             });
13826             this.resizer.on('resize', function(r, w, h){
13827                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13828                 this.listWidth = w;
13829                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13830                 this.restrictHeight();
13831             }, this);
13832             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13833         }
13834         */
13835         if(!this.editable){
13836             this.editable = true;
13837             this.setEditable(false);
13838         }
13839         
13840         /*
13841         
13842         if (typeof(this.events.add.listeners) != 'undefined') {
13843             
13844             this.addicon = this.wrap.createChild(
13845                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13846        
13847             this.addicon.on('click', function(e) {
13848                 this.fireEvent('add', this);
13849             }, this);
13850         }
13851         if (typeof(this.events.edit.listeners) != 'undefined') {
13852             
13853             this.editicon = this.wrap.createChild(
13854                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13855             if (this.addicon) {
13856                 this.editicon.setStyle('margin-left', '40px');
13857             }
13858             this.editicon.on('click', function(e) {
13859                 
13860                 // we fire even  if inothing is selected..
13861                 this.fireEvent('edit', this, this.lastData );
13862                 
13863             }, this);
13864         }
13865         */
13866         
13867         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13868             "up" : function(e){
13869                 this.inKeyMode = true;
13870                 this.selectPrev();
13871             },
13872
13873             "down" : function(e){
13874                 if(!this.isExpanded()){
13875                     this.onTriggerClick();
13876                 }else{
13877                     this.inKeyMode = true;
13878                     this.selectNext();
13879                 }
13880             },
13881
13882             "enter" : function(e){
13883 //                this.onViewClick();
13884                 //return true;
13885                 this.collapse();
13886                 
13887                 if(this.fireEvent("specialkey", this, e)){
13888                     this.onViewClick(false);
13889                 }
13890                 
13891                 return true;
13892             },
13893
13894             "esc" : function(e){
13895                 this.collapse();
13896             },
13897
13898             "tab" : function(e){
13899                 this.collapse();
13900                 
13901                 if(this.fireEvent("specialkey", this, e)){
13902                     this.onViewClick(false);
13903                 }
13904                 
13905                 return true;
13906             },
13907
13908             scope : this,
13909
13910             doRelay : function(foo, bar, hname){
13911                 if(hname == 'down' || this.scope.isExpanded()){
13912                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13913                 }
13914                 return true;
13915             },
13916
13917             forceKeyDown: true
13918         });
13919         
13920         
13921         this.queryDelay = Math.max(this.queryDelay || 10,
13922                 this.mode == 'local' ? 10 : 250);
13923         
13924         
13925         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13926         
13927         if(this.typeAhead){
13928             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13929         }
13930         if(this.editable !== false){
13931             this.inputEl().on("keyup", this.onKeyUp, this);
13932         }
13933         if(this.forceSelection){
13934             this.inputEl().on('blur', this.doForce, this);
13935         }
13936         
13937         if(this.multiple){
13938             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13939             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13940         }
13941     },
13942     
13943     initTickableEvents: function()
13944     {   
13945         this.createList();
13946         
13947         if(this.hiddenName){
13948             
13949             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13950             
13951             this.hiddenField.dom.value =
13952                 this.hiddenValue !== undefined ? this.hiddenValue :
13953                 this.value !== undefined ? this.value : '';
13954
13955             // prevent input submission
13956             this.el.dom.removeAttribute('name');
13957             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13958              
13959              
13960         }
13961         
13962 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13963         
13964         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13965         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13966         if(this.triggerList){
13967             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13968         }
13969          
13970         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13971         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13972         
13973         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13974         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13975         
13976         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13977         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13978         
13979         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13980         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13981         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13982         
13983         this.okBtn.hide();
13984         this.cancelBtn.hide();
13985         
13986         var _this = this;
13987         
13988         (function(){
13989             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13990             _this.list.setWidth(lw);
13991         }).defer(100);
13992         
13993         this.list.on('mouseover', this.onViewOver, this);
13994         this.list.on('mousemove', this.onViewMove, this);
13995         
13996         this.list.on('scroll', this.onViewScroll, this);
13997         
13998         if(!this.tpl){
13999             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
14000                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14001         }
14002
14003         this.view = new Roo.View(this.list, this.tpl, {
14004             singleSelect:true,
14005             tickable:true,
14006             parent:this,
14007             store: this.store,
14008             selectedClass: this.selectedClass
14009         });
14010         
14011         //this.view.wrapEl.setDisplayed(false);
14012         this.view.on('click', this.onViewClick, this);
14013         
14014         
14015         
14016         this.store.on('beforeload', this.onBeforeLoad, this);
14017         this.store.on('load', this.onLoad, this);
14018         this.store.on('loadexception', this.onLoadException, this);
14019         
14020         if(this.editable){
14021             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14022                 "up" : function(e){
14023                     this.inKeyMode = true;
14024                     this.selectPrev();
14025                 },
14026
14027                 "down" : function(e){
14028                     this.inKeyMode = true;
14029                     this.selectNext();
14030                 },
14031
14032                 "enter" : function(e){
14033                     if(this.fireEvent("specialkey", this, e)){
14034                         this.onViewClick(false);
14035                     }
14036                     
14037                     return true;
14038                 },
14039
14040                 "esc" : function(e){
14041                     this.onTickableFooterButtonClick(e, false, false);
14042                 },
14043
14044                 "tab" : function(e){
14045                     this.fireEvent("specialkey", this, e);
14046                     
14047                     this.onTickableFooterButtonClick(e, false, false);
14048                     
14049                     return true;
14050                 },
14051
14052                 scope : this,
14053
14054                 doRelay : function(e, fn, key){
14055                     if(this.scope.isExpanded()){
14056                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14057                     }
14058                     return true;
14059                 },
14060
14061                 forceKeyDown: true
14062             });
14063         }
14064         
14065         this.queryDelay = Math.max(this.queryDelay || 10,
14066                 this.mode == 'local' ? 10 : 250);
14067         
14068         
14069         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14070         
14071         if(this.typeAhead){
14072             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14073         }
14074         
14075         if(this.editable !== false){
14076             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14077         }
14078         
14079         this.indicator = this.indicatorEl();
14080         
14081         if(this.indicator){
14082             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14083             this.indicator.hide();
14084         }
14085         
14086     },
14087
14088     onDestroy : function(){
14089         if(this.view){
14090             this.view.setStore(null);
14091             this.view.el.removeAllListeners();
14092             this.view.el.remove();
14093             this.view.purgeListeners();
14094         }
14095         if(this.list){
14096             this.list.dom.innerHTML  = '';
14097         }
14098         
14099         if(this.store){
14100             this.store.un('beforeload', this.onBeforeLoad, this);
14101             this.store.un('load', this.onLoad, this);
14102             this.store.un('loadexception', this.onLoadException, this);
14103         }
14104         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14105     },
14106
14107     // private
14108     fireKey : function(e){
14109         if(e.isNavKeyPress() && !this.list.isVisible()){
14110             this.fireEvent("specialkey", this, e);
14111         }
14112     },
14113
14114     // private
14115     onResize: function(w, h){
14116 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14117 //        
14118 //        if(typeof w != 'number'){
14119 //            // we do not handle it!?!?
14120 //            return;
14121 //        }
14122 //        var tw = this.trigger.getWidth();
14123 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14124 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14125 //        var x = w - tw;
14126 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14127 //            
14128 //        //this.trigger.setStyle('left', x+'px');
14129 //        
14130 //        if(this.list && this.listWidth === undefined){
14131 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14132 //            this.list.setWidth(lw);
14133 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14134 //        }
14135         
14136     
14137         
14138     },
14139
14140     /**
14141      * Allow or prevent the user from directly editing the field text.  If false is passed,
14142      * the user will only be able to select from the items defined in the dropdown list.  This method
14143      * is the runtime equivalent of setting the 'editable' config option at config time.
14144      * @param {Boolean} value True to allow the user to directly edit the field text
14145      */
14146     setEditable : function(value){
14147         if(value == this.editable){
14148             return;
14149         }
14150         this.editable = value;
14151         if(!value){
14152             this.inputEl().dom.setAttribute('readOnly', true);
14153             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14154             this.inputEl().addClass('x-combo-noedit');
14155         }else{
14156             this.inputEl().dom.setAttribute('readOnly', false);
14157             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14158             this.inputEl().removeClass('x-combo-noedit');
14159         }
14160     },
14161
14162     // private
14163     
14164     onBeforeLoad : function(combo,opts){
14165         if(!this.hasFocus){
14166             return;
14167         }
14168          if (!opts.add) {
14169             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14170          }
14171         this.restrictHeight();
14172         this.selectedIndex = -1;
14173     },
14174
14175     // private
14176     onLoad : function(){
14177         
14178         this.hasQuery = false;
14179         
14180         if(!this.hasFocus){
14181             return;
14182         }
14183         
14184         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14185             this.loading.hide();
14186         }
14187         
14188         if(this.store.getCount() > 0){
14189             
14190             this.expand();
14191             this.restrictHeight();
14192             if(this.lastQuery == this.allQuery){
14193                 if(this.editable && !this.tickable){
14194                     this.inputEl().dom.select();
14195                 }
14196                 
14197                 if(
14198                     !this.selectByValue(this.value, true) &&
14199                     this.autoFocus && 
14200                     (
14201                         !this.store.lastOptions ||
14202                         typeof(this.store.lastOptions.add) == 'undefined' || 
14203                         this.store.lastOptions.add != true
14204                     )
14205                 ){
14206                     this.select(0, true);
14207                 }
14208             }else{
14209                 if(this.autoFocus){
14210                     this.selectNext();
14211                 }
14212                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14213                     this.taTask.delay(this.typeAheadDelay);
14214                 }
14215             }
14216         }else{
14217             this.onEmptyResults();
14218         }
14219         
14220         //this.el.focus();
14221     },
14222     // private
14223     onLoadException : function()
14224     {
14225         this.hasQuery = false;
14226         
14227         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14228             this.loading.hide();
14229         }
14230         
14231         if(this.tickable && this.editable){
14232             return;
14233         }
14234         
14235         this.collapse();
14236         // only causes errors at present
14237         //Roo.log(this.store.reader.jsonData);
14238         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14239             // fixme
14240             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14241         //}
14242         
14243         
14244     },
14245     // private
14246     onTypeAhead : function(){
14247         if(this.store.getCount() > 0){
14248             var r = this.store.getAt(0);
14249             var newValue = r.data[this.displayField];
14250             var len = newValue.length;
14251             var selStart = this.getRawValue().length;
14252             
14253             if(selStart != len){
14254                 this.setRawValue(newValue);
14255                 this.selectText(selStart, newValue.length);
14256             }
14257         }
14258     },
14259
14260     // private
14261     onSelect : function(record, index){
14262         
14263         if(this.fireEvent('beforeselect', this, record, index) !== false){
14264         
14265             this.setFromData(index > -1 ? record.data : false);
14266             
14267             this.collapse();
14268             this.fireEvent('select', this, record, index);
14269         }
14270     },
14271
14272     /**
14273      * Returns the currently selected field value or empty string if no value is set.
14274      * @return {String} value The selected value
14275      */
14276     getValue : function()
14277     {
14278         if(Roo.isIOS && this.useNativeIOS){
14279             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14280         }
14281         
14282         if(this.multiple){
14283             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14284         }
14285         
14286         if(this.valueField){
14287             return typeof this.value != 'undefined' ? this.value : '';
14288         }else{
14289             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14290         }
14291     },
14292     
14293     getRawValue : function()
14294     {
14295         if(Roo.isIOS && this.useNativeIOS){
14296             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14297         }
14298         
14299         var v = this.inputEl().getValue();
14300         
14301         return v;
14302     },
14303
14304     /**
14305      * Clears any text/value currently set in the field
14306      */
14307     clearValue : function(){
14308         
14309         if(this.hiddenField){
14310             this.hiddenField.dom.value = '';
14311         }
14312         this.value = '';
14313         this.setRawValue('');
14314         this.lastSelectionText = '';
14315         this.lastData = false;
14316         
14317         var close = this.closeTriggerEl();
14318         
14319         if(close){
14320             close.hide();
14321         }
14322         
14323         this.validate();
14324         
14325     },
14326
14327     /**
14328      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14329      * will be displayed in the field.  If the value does not match the data value of an existing item,
14330      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14331      * Otherwise the field will be blank (although the value will still be set).
14332      * @param {String} value The value to match
14333      */
14334     setValue : function(v)
14335     {
14336         if(Roo.isIOS && this.useNativeIOS){
14337             this.setIOSValue(v);
14338             return;
14339         }
14340         
14341         if(this.multiple){
14342             this.syncValue();
14343             return;
14344         }
14345         
14346         var text = v;
14347         if(this.valueField){
14348             var r = this.findRecord(this.valueField, v);
14349             if(r){
14350                 text = r.data[this.displayField];
14351             }else if(this.valueNotFoundText !== undefined){
14352                 text = this.valueNotFoundText;
14353             }
14354         }
14355         this.lastSelectionText = text;
14356         if(this.hiddenField){
14357             this.hiddenField.dom.value = v;
14358         }
14359         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14360         this.value = v;
14361         
14362         var close = this.closeTriggerEl();
14363         
14364         if(close){
14365             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14366         }
14367         
14368         this.validate();
14369     },
14370     /**
14371      * @property {Object} the last set data for the element
14372      */
14373     
14374     lastData : false,
14375     /**
14376      * Sets the value of the field based on a object which is related to the record format for the store.
14377      * @param {Object} value the value to set as. or false on reset?
14378      */
14379     setFromData : function(o){
14380         
14381         if(this.multiple){
14382             this.addItem(o);
14383             return;
14384         }
14385             
14386         var dv = ''; // display value
14387         var vv = ''; // value value..
14388         this.lastData = o;
14389         if (this.displayField) {
14390             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14391         } else {
14392             // this is an error condition!!!
14393             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14394         }
14395         
14396         if(this.valueField){
14397             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14398         }
14399         
14400         var close = this.closeTriggerEl();
14401         
14402         if(close){
14403             if(dv.length || vv * 1 > 0){
14404                 close.show() ;
14405                 this.blockFocus=true;
14406             } else {
14407                 close.hide();
14408             }             
14409         }
14410         
14411         if(this.hiddenField){
14412             this.hiddenField.dom.value = vv;
14413             
14414             this.lastSelectionText = dv;
14415             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14416             this.value = vv;
14417             return;
14418         }
14419         // no hidden field.. - we store the value in 'value', but still display
14420         // display field!!!!
14421         this.lastSelectionText = dv;
14422         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14423         this.value = vv;
14424         
14425         
14426         
14427     },
14428     // private
14429     reset : function(){
14430         // overridden so that last data is reset..
14431         
14432         if(this.multiple){
14433             this.clearItem();
14434             return;
14435         }
14436         
14437         this.setValue(this.originalValue);
14438         //this.clearInvalid();
14439         this.lastData = false;
14440         if (this.view) {
14441             this.view.clearSelections();
14442         }
14443         
14444         this.validate();
14445     },
14446     // private
14447     findRecord : function(prop, value){
14448         var record;
14449         if(this.store.getCount() > 0){
14450             this.store.each(function(r){
14451                 if(r.data[prop] == value){
14452                     record = r;
14453                     return false;
14454                 }
14455                 return true;
14456             });
14457         }
14458         return record;
14459     },
14460     
14461     getName: function()
14462     {
14463         // returns hidden if it's set..
14464         if (!this.rendered) {return ''};
14465         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14466         
14467     },
14468     // private
14469     onViewMove : function(e, t){
14470         this.inKeyMode = false;
14471     },
14472
14473     // private
14474     onViewOver : function(e, t){
14475         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14476             return;
14477         }
14478         var item = this.view.findItemFromChild(t);
14479         
14480         if(item){
14481             var index = this.view.indexOf(item);
14482             this.select(index, false);
14483         }
14484     },
14485
14486     // private
14487     onViewClick : function(view, doFocus, el, e)
14488     {
14489         var index = this.view.getSelectedIndexes()[0];
14490         
14491         var r = this.store.getAt(index);
14492         
14493         if(this.tickable){
14494             
14495             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14496                 return;
14497             }
14498             
14499             var rm = false;
14500             var _this = this;
14501             
14502             Roo.each(this.tickItems, function(v,k){
14503                 
14504                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14505                     Roo.log(v);
14506                     _this.tickItems.splice(k, 1);
14507                     
14508                     if(typeof(e) == 'undefined' && view == false){
14509                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14510                     }
14511                     
14512                     rm = true;
14513                     return;
14514                 }
14515             });
14516             
14517             if(rm){
14518                 return;
14519             }
14520             
14521             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14522                 this.tickItems.push(r.data);
14523             }
14524             
14525             if(typeof(e) == 'undefined' && view == false){
14526                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14527             }
14528                     
14529             return;
14530         }
14531         
14532         if(r){
14533             this.onSelect(r, index);
14534         }
14535         if(doFocus !== false && !this.blockFocus){
14536             this.inputEl().focus();
14537         }
14538     },
14539
14540     // private
14541     restrictHeight : function(){
14542         //this.innerList.dom.style.height = '';
14543         //var inner = this.innerList.dom;
14544         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14545         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14546         //this.list.beginUpdate();
14547         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14548         this.list.alignTo(this.inputEl(), this.listAlign);
14549         this.list.alignTo(this.inputEl(), this.listAlign);
14550         //this.list.endUpdate();
14551     },
14552
14553     // private
14554     onEmptyResults : function(){
14555         
14556         if(this.tickable && this.editable){
14557             this.hasFocus = false;
14558             this.restrictHeight();
14559             return;
14560         }
14561         
14562         this.collapse();
14563     },
14564
14565     /**
14566      * Returns true if the dropdown list is expanded, else false.
14567      */
14568     isExpanded : function(){
14569         return this.list.isVisible();
14570     },
14571
14572     /**
14573      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14574      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14575      * @param {String} value The data value of the item to select
14576      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14577      * selected item if it is not currently in view (defaults to true)
14578      * @return {Boolean} True if the value matched an item in the list, else false
14579      */
14580     selectByValue : function(v, scrollIntoView){
14581         if(v !== undefined && v !== null){
14582             var r = this.findRecord(this.valueField || this.displayField, v);
14583             if(r){
14584                 this.select(this.store.indexOf(r), scrollIntoView);
14585                 return true;
14586             }
14587         }
14588         return false;
14589     },
14590
14591     /**
14592      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14593      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14594      * @param {Number} index The zero-based index of the list item to select
14595      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14596      * selected item if it is not currently in view (defaults to true)
14597      */
14598     select : function(index, scrollIntoView){
14599         this.selectedIndex = index;
14600         this.view.select(index);
14601         if(scrollIntoView !== false){
14602             var el = this.view.getNode(index);
14603             /*
14604              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14605              */
14606             if(el){
14607                 this.list.scrollChildIntoView(el, false);
14608             }
14609         }
14610     },
14611
14612     // private
14613     selectNext : function(){
14614         var ct = this.store.getCount();
14615         if(ct > 0){
14616             if(this.selectedIndex == -1){
14617                 this.select(0);
14618             }else if(this.selectedIndex < ct-1){
14619                 this.select(this.selectedIndex+1);
14620             }
14621         }
14622     },
14623
14624     // private
14625     selectPrev : function(){
14626         var ct = this.store.getCount();
14627         if(ct > 0){
14628             if(this.selectedIndex == -1){
14629                 this.select(0);
14630             }else if(this.selectedIndex != 0){
14631                 this.select(this.selectedIndex-1);
14632             }
14633         }
14634     },
14635
14636     // private
14637     onKeyUp : function(e){
14638         if(this.editable !== false && !e.isSpecialKey()){
14639             this.lastKey = e.getKey();
14640             this.dqTask.delay(this.queryDelay);
14641         }
14642     },
14643
14644     // private
14645     validateBlur : function(){
14646         return !this.list || !this.list.isVisible();   
14647     },
14648
14649     // private
14650     initQuery : function(){
14651         
14652         var v = this.getRawValue();
14653         
14654         if(this.tickable && this.editable){
14655             v = this.tickableInputEl().getValue();
14656         }
14657         
14658         this.doQuery(v);
14659     },
14660
14661     // private
14662     doForce : function(){
14663         if(this.inputEl().dom.value.length > 0){
14664             this.inputEl().dom.value =
14665                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14666              
14667         }
14668     },
14669
14670     /**
14671      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14672      * query allowing the query action to be canceled if needed.
14673      * @param {String} query The SQL query to execute
14674      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14675      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14676      * saved in the current store (defaults to false)
14677      */
14678     doQuery : function(q, forceAll){
14679         
14680         if(q === undefined || q === null){
14681             q = '';
14682         }
14683         var qe = {
14684             query: q,
14685             forceAll: forceAll,
14686             combo: this,
14687             cancel:false
14688         };
14689         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14690             return false;
14691         }
14692         q = qe.query;
14693         
14694         forceAll = qe.forceAll;
14695         if(forceAll === true || (q.length >= this.minChars)){
14696             
14697             this.hasQuery = true;
14698             
14699             if(this.lastQuery != q || this.alwaysQuery){
14700                 this.lastQuery = q;
14701                 if(this.mode == 'local'){
14702                     this.selectedIndex = -1;
14703                     if(forceAll){
14704                         this.store.clearFilter();
14705                     }else{
14706                         
14707                         if(this.specialFilter){
14708                             this.fireEvent('specialfilter', this);
14709                             this.onLoad();
14710                             return;
14711                         }
14712                         
14713                         this.store.filter(this.displayField, q);
14714                     }
14715                     
14716                     this.store.fireEvent("datachanged", this.store);
14717                     
14718                     this.onLoad();
14719                     
14720                     
14721                 }else{
14722                     
14723                     this.store.baseParams[this.queryParam] = q;
14724                     
14725                     var options = {params : this.getParams(q)};
14726                     
14727                     if(this.loadNext){
14728                         options.add = true;
14729                         options.params.start = this.page * this.pageSize;
14730                     }
14731                     
14732                     this.store.load(options);
14733                     
14734                     /*
14735                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14736                      *  we should expand the list on onLoad
14737                      *  so command out it
14738                      */
14739 //                    this.expand();
14740                 }
14741             }else{
14742                 this.selectedIndex = -1;
14743                 this.onLoad();   
14744             }
14745         }
14746         
14747         this.loadNext = false;
14748     },
14749     
14750     // private
14751     getParams : function(q){
14752         var p = {};
14753         //p[this.queryParam] = q;
14754         
14755         if(this.pageSize){
14756             p.start = 0;
14757             p.limit = this.pageSize;
14758         }
14759         return p;
14760     },
14761
14762     /**
14763      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14764      */
14765     collapse : function(){
14766         if(!this.isExpanded()){
14767             return;
14768         }
14769         
14770         this.list.hide();
14771         
14772         this.hasFocus = false;
14773         
14774         if(this.tickable){
14775             this.okBtn.hide();
14776             this.cancelBtn.hide();
14777             this.trigger.show();
14778             
14779             if(this.editable){
14780                 this.tickableInputEl().dom.value = '';
14781                 this.tickableInputEl().blur();
14782             }
14783             
14784         }
14785         
14786         Roo.get(document).un('mousedown', this.collapseIf, this);
14787         Roo.get(document).un('mousewheel', this.collapseIf, this);
14788         if (!this.editable) {
14789             Roo.get(document).un('keydown', this.listKeyPress, this);
14790         }
14791         this.fireEvent('collapse', this);
14792         
14793         this.validate();
14794     },
14795
14796     // private
14797     collapseIf : function(e){
14798         var in_combo  = e.within(this.el);
14799         var in_list =  e.within(this.list);
14800         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14801         
14802         if (in_combo || in_list || is_list) {
14803             //e.stopPropagation();
14804             return;
14805         }
14806         
14807         if(this.tickable){
14808             this.onTickableFooterButtonClick(e, false, false);
14809         }
14810
14811         this.collapse();
14812         
14813     },
14814
14815     /**
14816      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14817      */
14818     expand : function(){
14819        
14820         if(this.isExpanded() || !this.hasFocus){
14821             return;
14822         }
14823         
14824         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14825         this.list.setWidth(lw);
14826         
14827         Roo.log('expand');
14828         
14829         this.list.show();
14830         
14831         this.restrictHeight();
14832         
14833         if(this.tickable){
14834             
14835             this.tickItems = Roo.apply([], this.item);
14836             
14837             this.okBtn.show();
14838             this.cancelBtn.show();
14839             this.trigger.hide();
14840             
14841             if(this.editable){
14842                 this.tickableInputEl().focus();
14843             }
14844             
14845         }
14846         
14847         Roo.get(document).on('mousedown', this.collapseIf, this);
14848         Roo.get(document).on('mousewheel', this.collapseIf, this);
14849         if (!this.editable) {
14850             Roo.get(document).on('keydown', this.listKeyPress, this);
14851         }
14852         
14853         this.fireEvent('expand', this);
14854     },
14855
14856     // private
14857     // Implements the default empty TriggerField.onTriggerClick function
14858     onTriggerClick : function(e)
14859     {
14860         Roo.log('trigger click');
14861         
14862         if(this.disabled || !this.triggerList){
14863             return;
14864         }
14865         
14866         this.page = 0;
14867         this.loadNext = false;
14868         
14869         if(this.isExpanded()){
14870             this.collapse();
14871             if (!this.blockFocus) {
14872                 this.inputEl().focus();
14873             }
14874             
14875         }else {
14876             this.hasFocus = true;
14877             if(this.triggerAction == 'all') {
14878                 this.doQuery(this.allQuery, true);
14879             } else {
14880                 this.doQuery(this.getRawValue());
14881             }
14882             if (!this.blockFocus) {
14883                 this.inputEl().focus();
14884             }
14885         }
14886     },
14887     
14888     onTickableTriggerClick : function(e)
14889     {
14890         if(this.disabled){
14891             return;
14892         }
14893         
14894         this.page = 0;
14895         this.loadNext = false;
14896         this.hasFocus = true;
14897         
14898         if(this.triggerAction == 'all') {
14899             this.doQuery(this.allQuery, true);
14900         } else {
14901             this.doQuery(this.getRawValue());
14902         }
14903     },
14904     
14905     onSearchFieldClick : function(e)
14906     {
14907         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14908             this.onTickableFooterButtonClick(e, false, false);
14909             return;
14910         }
14911         
14912         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14913             return;
14914         }
14915         
14916         this.page = 0;
14917         this.loadNext = false;
14918         this.hasFocus = true;
14919         
14920         if(this.triggerAction == 'all') {
14921             this.doQuery(this.allQuery, true);
14922         } else {
14923             this.doQuery(this.getRawValue());
14924         }
14925     },
14926     
14927     listKeyPress : function(e)
14928     {
14929         //Roo.log('listkeypress');
14930         // scroll to first matching element based on key pres..
14931         if (e.isSpecialKey()) {
14932             return false;
14933         }
14934         var k = String.fromCharCode(e.getKey()).toUpperCase();
14935         //Roo.log(k);
14936         var match  = false;
14937         var csel = this.view.getSelectedNodes();
14938         var cselitem = false;
14939         if (csel.length) {
14940             var ix = this.view.indexOf(csel[0]);
14941             cselitem  = this.store.getAt(ix);
14942             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14943                 cselitem = false;
14944             }
14945             
14946         }
14947         
14948         this.store.each(function(v) { 
14949             if (cselitem) {
14950                 // start at existing selection.
14951                 if (cselitem.id == v.id) {
14952                     cselitem = false;
14953                 }
14954                 return true;
14955             }
14956                 
14957             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14958                 match = this.store.indexOf(v);
14959                 return false;
14960             }
14961             return true;
14962         }, this);
14963         
14964         if (match === false) {
14965             return true; // no more action?
14966         }
14967         // scroll to?
14968         this.view.select(match);
14969         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14970         sn.scrollIntoView(sn.dom.parentNode, false);
14971     },
14972     
14973     onViewScroll : function(e, t){
14974         
14975         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){
14976             return;
14977         }
14978         
14979         this.hasQuery = true;
14980         
14981         this.loading = this.list.select('.loading', true).first();
14982         
14983         if(this.loading === null){
14984             this.list.createChild({
14985                 tag: 'div',
14986                 cls: 'loading roo-select2-more-results roo-select2-active',
14987                 html: 'Loading more results...'
14988             });
14989             
14990             this.loading = this.list.select('.loading', true).first();
14991             
14992             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14993             
14994             this.loading.hide();
14995         }
14996         
14997         this.loading.show();
14998         
14999         var _combo = this;
15000         
15001         this.page++;
15002         this.loadNext = true;
15003         
15004         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15005         
15006         return;
15007     },
15008     
15009     addItem : function(o)
15010     {   
15011         var dv = ''; // display value
15012         
15013         if (this.displayField) {
15014             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15015         } else {
15016             // this is an error condition!!!
15017             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15018         }
15019         
15020         if(!dv.length){
15021             return;
15022         }
15023         
15024         var choice = this.choices.createChild({
15025             tag: 'li',
15026             cls: 'roo-select2-search-choice',
15027             cn: [
15028                 {
15029                     tag: 'div',
15030                     html: dv
15031                 },
15032                 {
15033                     tag: 'a',
15034                     href: '#',
15035                     cls: 'roo-select2-search-choice-close fa fa-times',
15036                     tabindex: '-1'
15037                 }
15038             ]
15039             
15040         }, this.searchField);
15041         
15042         var close = choice.select('a.roo-select2-search-choice-close', true).first();
15043         
15044         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15045         
15046         this.item.push(o);
15047         
15048         this.lastData = o;
15049         
15050         this.syncValue();
15051         
15052         this.inputEl().dom.value = '';
15053         
15054         this.validate();
15055     },
15056     
15057     onRemoveItem : function(e, _self, o)
15058     {
15059         e.preventDefault();
15060         
15061         this.lastItem = Roo.apply([], this.item);
15062         
15063         var index = this.item.indexOf(o.data) * 1;
15064         
15065         if( index < 0){
15066             Roo.log('not this item?!');
15067             return;
15068         }
15069         
15070         this.item.splice(index, 1);
15071         o.item.remove();
15072         
15073         this.syncValue();
15074         
15075         this.fireEvent('remove', this, e);
15076         
15077         this.validate();
15078         
15079     },
15080     
15081     syncValue : function()
15082     {
15083         if(!this.item.length){
15084             this.clearValue();
15085             return;
15086         }
15087             
15088         var value = [];
15089         var _this = this;
15090         Roo.each(this.item, function(i){
15091             if(_this.valueField){
15092                 value.push(i[_this.valueField]);
15093                 return;
15094             }
15095
15096             value.push(i);
15097         });
15098
15099         this.value = value.join(',');
15100
15101         if(this.hiddenField){
15102             this.hiddenField.dom.value = this.value;
15103         }
15104         
15105         this.store.fireEvent("datachanged", this.store);
15106         
15107         this.validate();
15108     },
15109     
15110     clearItem : function()
15111     {
15112         if(!this.multiple){
15113             return;
15114         }
15115         
15116         this.item = [];
15117         
15118         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15119            c.remove();
15120         });
15121         
15122         this.syncValue();
15123         
15124         this.validate();
15125         
15126         if(this.tickable && !Roo.isTouch){
15127             this.view.refresh();
15128         }
15129     },
15130     
15131     inputEl: function ()
15132     {
15133         if(Roo.isIOS && this.useNativeIOS){
15134             return this.el.select('select.roo-ios-select', true).first();
15135         }
15136         
15137         if(Roo.isTouch && this.mobileTouchView){
15138             return this.el.select('input.form-control',true).first();
15139         }
15140         
15141         if(this.tickable){
15142             return this.searchField;
15143         }
15144         
15145         return this.el.select('input.form-control',true).first();
15146     },
15147     
15148     onTickableFooterButtonClick : function(e, btn, el)
15149     {
15150         e.preventDefault();
15151         
15152         this.lastItem = Roo.apply([], this.item);
15153         
15154         if(btn && btn.name == 'cancel'){
15155             this.tickItems = Roo.apply([], this.item);
15156             this.collapse();
15157             return;
15158         }
15159         
15160         this.clearItem();
15161         
15162         var _this = this;
15163         
15164         Roo.each(this.tickItems, function(o){
15165             _this.addItem(o);
15166         });
15167         
15168         this.collapse();
15169         
15170     },
15171     
15172     validate : function()
15173     {
15174         if(this.getVisibilityEl().hasClass('hidden')){
15175             return true;
15176         }
15177         
15178         var v = this.getRawValue();
15179         
15180         if(this.multiple){
15181             v = this.getValue();
15182         }
15183         
15184         if(this.disabled || this.allowBlank || v.length){
15185             this.markValid();
15186             return true;
15187         }
15188         
15189         this.markInvalid();
15190         return false;
15191     },
15192     
15193     tickableInputEl : function()
15194     {
15195         if(!this.tickable || !this.editable){
15196             return this.inputEl();
15197         }
15198         
15199         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15200     },
15201     
15202     
15203     getAutoCreateTouchView : function()
15204     {
15205         var id = Roo.id();
15206         
15207         var cfg = {
15208             cls: 'form-group' //input-group
15209         };
15210         
15211         var input =  {
15212             tag: 'input',
15213             id : id,
15214             type : this.inputType,
15215             cls : 'form-control x-combo-noedit',
15216             autocomplete: 'new-password',
15217             placeholder : this.placeholder || '',
15218             readonly : true
15219         };
15220         
15221         if (this.name) {
15222             input.name = this.name;
15223         }
15224         
15225         if (this.size) {
15226             input.cls += ' input-' + this.size;
15227         }
15228         
15229         if (this.disabled) {
15230             input.disabled = true;
15231         }
15232         
15233         var inputblock = {
15234             cls : '',
15235             cn : [
15236                 input
15237             ]
15238         };
15239         
15240         if(this.before){
15241             inputblock.cls += ' input-group';
15242             
15243             inputblock.cn.unshift({
15244                 tag :'span',
15245                 cls : 'input-group-addon input-group-prepend input-group-text',
15246                 html : this.before
15247             });
15248         }
15249         
15250         if(this.removable && !this.multiple){
15251             inputblock.cls += ' roo-removable';
15252             
15253             inputblock.cn.push({
15254                 tag: 'button',
15255                 html : 'x',
15256                 cls : 'roo-combo-removable-btn close'
15257             });
15258         }
15259
15260         if(this.hasFeedback && !this.allowBlank){
15261             
15262             inputblock.cls += ' has-feedback';
15263             
15264             inputblock.cn.push({
15265                 tag: 'span',
15266                 cls: 'glyphicon form-control-feedback'
15267             });
15268             
15269         }
15270         
15271         if (this.after) {
15272             
15273             inputblock.cls += (this.before) ? '' : ' input-group';
15274             
15275             inputblock.cn.push({
15276                 tag :'span',
15277                 cls : 'input-group-addon input-group-append input-group-text',
15278                 html : this.after
15279             });
15280         }
15281
15282         
15283         var ibwrap = inputblock;
15284         
15285         if(this.multiple){
15286             ibwrap = {
15287                 tag: 'ul',
15288                 cls: 'roo-select2-choices',
15289                 cn:[
15290                     {
15291                         tag: 'li',
15292                         cls: 'roo-select2-search-field',
15293                         cn: [
15294
15295                             inputblock
15296                         ]
15297                     }
15298                 ]
15299             };
15300         
15301             
15302         }
15303         
15304         var combobox = {
15305             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15306             cn: [
15307                 {
15308                     tag: 'input',
15309                     type : 'hidden',
15310                     cls: 'form-hidden-field'
15311                 },
15312                 ibwrap
15313             ]
15314         };
15315         
15316         if(!this.multiple && this.showToggleBtn){
15317             
15318             var caret = {
15319                         tag: 'span',
15320                         cls: 'caret'
15321             };
15322             
15323             if (this.caret != false) {
15324                 caret = {
15325                      tag: 'i',
15326                      cls: 'fa fa-' + this.caret
15327                 };
15328                 
15329             }
15330             
15331             combobox.cn.push({
15332                 tag :'span',
15333                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15334                 cn : [
15335                     caret,
15336                     {
15337                         tag: 'span',
15338                         cls: 'combobox-clear',
15339                         cn  : [
15340                             {
15341                                 tag : 'i',
15342                                 cls: 'icon-remove'
15343                             }
15344                         ]
15345                     }
15346                 ]
15347
15348             })
15349         }
15350         
15351         if(this.multiple){
15352             combobox.cls += ' roo-select2-container-multi';
15353         }
15354         
15355         var align = this.labelAlign || this.parentLabelAlign();
15356         
15357         if (align ==='left' && this.fieldLabel.length) {
15358
15359             cfg.cn = [
15360                 {
15361                    tag : 'i',
15362                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15363                    tooltip : 'This field is required'
15364                 },
15365                 {
15366                     tag: 'label',
15367                     cls : 'control-label col-form-label',
15368                     html : this.fieldLabel
15369
15370                 },
15371                 {
15372                     cls : '', 
15373                     cn: [
15374                         combobox
15375                     ]
15376                 }
15377             ];
15378             
15379             var labelCfg = cfg.cn[1];
15380             var contentCfg = cfg.cn[2];
15381             
15382
15383             if(this.indicatorpos == 'right'){
15384                 cfg.cn = [
15385                     {
15386                         tag: 'label',
15387                         'for' :  id,
15388                         cls : 'control-label col-form-label',
15389                         cn : [
15390                             {
15391                                 tag : 'span',
15392                                 html : this.fieldLabel
15393                             },
15394                             {
15395                                 tag : 'i',
15396                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15397                                 tooltip : 'This field is required'
15398                             }
15399                         ]
15400                     },
15401                     {
15402                         cls : "",
15403                         cn: [
15404                             combobox
15405                         ]
15406                     }
15407
15408                 ];
15409                 
15410                 labelCfg = cfg.cn[0];
15411                 contentCfg = cfg.cn[1];
15412             }
15413             
15414            
15415             
15416             if(this.labelWidth > 12){
15417                 labelCfg.style = "width: " + this.labelWidth + 'px';
15418             }
15419             
15420             if(this.labelWidth < 13 && this.labelmd == 0){
15421                 this.labelmd = this.labelWidth;
15422             }
15423             
15424             if(this.labellg > 0){
15425                 labelCfg.cls += ' col-lg-' + this.labellg;
15426                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15427             }
15428             
15429             if(this.labelmd > 0){
15430                 labelCfg.cls += ' col-md-' + this.labelmd;
15431                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15432             }
15433             
15434             if(this.labelsm > 0){
15435                 labelCfg.cls += ' col-sm-' + this.labelsm;
15436                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15437             }
15438             
15439             if(this.labelxs > 0){
15440                 labelCfg.cls += ' col-xs-' + this.labelxs;
15441                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15442             }
15443                 
15444                 
15445         } else if ( this.fieldLabel.length) {
15446             cfg.cn = [
15447                 {
15448                    tag : 'i',
15449                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15450                    tooltip : 'This field is required'
15451                 },
15452                 {
15453                     tag: 'label',
15454                     cls : 'control-label',
15455                     html : this.fieldLabel
15456
15457                 },
15458                 {
15459                     cls : '', 
15460                     cn: [
15461                         combobox
15462                     ]
15463                 }
15464             ];
15465             
15466             if(this.indicatorpos == 'right'){
15467                 cfg.cn = [
15468                     {
15469                         tag: 'label',
15470                         cls : 'control-label',
15471                         html : this.fieldLabel,
15472                         cn : [
15473                             {
15474                                tag : 'i',
15475                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15476                                tooltip : 'This field is required'
15477                             }
15478                         ]
15479                     },
15480                     {
15481                         cls : '', 
15482                         cn: [
15483                             combobox
15484                         ]
15485                     }
15486                 ];
15487             }
15488         } else {
15489             cfg.cn = combobox;    
15490         }
15491         
15492         
15493         var settings = this;
15494         
15495         ['xs','sm','md','lg'].map(function(size){
15496             if (settings[size]) {
15497                 cfg.cls += ' col-' + size + '-' + settings[size];
15498             }
15499         });
15500         
15501         return cfg;
15502     },
15503     
15504     initTouchView : function()
15505     {
15506         this.renderTouchView();
15507         
15508         this.touchViewEl.on('scroll', function(){
15509             this.el.dom.scrollTop = 0;
15510         }, this);
15511         
15512         this.originalValue = this.getValue();
15513         
15514         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15515         
15516         this.inputEl().on("click", this.showTouchView, this);
15517         if (this.triggerEl) {
15518             this.triggerEl.on("click", this.showTouchView, this);
15519         }
15520         
15521         
15522         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15523         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15524         
15525         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15526         
15527         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15528         this.store.on('load', this.onTouchViewLoad, this);
15529         this.store.on('loadexception', this.onTouchViewLoadException, this);
15530         
15531         if(this.hiddenName){
15532             
15533             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15534             
15535             this.hiddenField.dom.value =
15536                 this.hiddenValue !== undefined ? this.hiddenValue :
15537                 this.value !== undefined ? this.value : '';
15538         
15539             this.el.dom.removeAttribute('name');
15540             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15541         }
15542         
15543         if(this.multiple){
15544             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15545             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15546         }
15547         
15548         if(this.removable && !this.multiple){
15549             var close = this.closeTriggerEl();
15550             if(close){
15551                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15552                 close.on('click', this.removeBtnClick, this, close);
15553             }
15554         }
15555         /*
15556          * fix the bug in Safari iOS8
15557          */
15558         this.inputEl().on("focus", function(e){
15559             document.activeElement.blur();
15560         }, this);
15561         
15562         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15563         
15564         return;
15565         
15566         
15567     },
15568     
15569     renderTouchView : function()
15570     {
15571         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15572         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15573         
15574         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15575         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15576         
15577         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15578         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15579         this.touchViewBodyEl.setStyle('overflow', 'auto');
15580         
15581         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15582         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15583         
15584         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15585         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15586         
15587     },
15588     
15589     showTouchView : function()
15590     {
15591         if(this.disabled){
15592             return;
15593         }
15594         
15595         this.touchViewHeaderEl.hide();
15596
15597         if(this.modalTitle.length){
15598             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15599             this.touchViewHeaderEl.show();
15600         }
15601
15602         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15603         this.touchViewEl.show();
15604
15605         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15606         
15607         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15608         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15609
15610         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15611
15612         if(this.modalTitle.length){
15613             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15614         }
15615         
15616         this.touchViewBodyEl.setHeight(bodyHeight);
15617
15618         if(this.animate){
15619             var _this = this;
15620             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15621         }else{
15622             this.touchViewEl.addClass('in');
15623         }
15624         
15625         if(this._touchViewMask){
15626             Roo.get(document.body).addClass("x-body-masked");
15627             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15628             this._touchViewMask.setStyle('z-index', 10000);
15629             this._touchViewMask.addClass('show');
15630         }
15631         
15632         this.doTouchViewQuery();
15633         
15634     },
15635     
15636     hideTouchView : function()
15637     {
15638         this.touchViewEl.removeClass('in');
15639
15640         if(this.animate){
15641             var _this = this;
15642             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15643         }else{
15644             this.touchViewEl.setStyle('display', 'none');
15645         }
15646         
15647         if(this._touchViewMask){
15648             this._touchViewMask.removeClass('show');
15649             Roo.get(document.body).removeClass("x-body-masked");
15650         }
15651     },
15652     
15653     setTouchViewValue : function()
15654     {
15655         if(this.multiple){
15656             this.clearItem();
15657         
15658             var _this = this;
15659
15660             Roo.each(this.tickItems, function(o){
15661                 this.addItem(o);
15662             }, this);
15663         }
15664         
15665         this.hideTouchView();
15666     },
15667     
15668     doTouchViewQuery : function()
15669     {
15670         var qe = {
15671             query: '',
15672             forceAll: true,
15673             combo: this,
15674             cancel:false
15675         };
15676         
15677         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15678             return false;
15679         }
15680         
15681         if(!this.alwaysQuery || this.mode == 'local'){
15682             this.onTouchViewLoad();
15683             return;
15684         }
15685         
15686         this.store.load();
15687     },
15688     
15689     onTouchViewBeforeLoad : function(combo,opts)
15690     {
15691         return;
15692     },
15693
15694     // private
15695     onTouchViewLoad : function()
15696     {
15697         if(this.store.getCount() < 1){
15698             this.onTouchViewEmptyResults();
15699             return;
15700         }
15701         
15702         this.clearTouchView();
15703         
15704         var rawValue = this.getRawValue();
15705         
15706         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15707         
15708         this.tickItems = [];
15709         
15710         this.store.data.each(function(d, rowIndex){
15711             var row = this.touchViewListGroup.createChild(template);
15712             
15713             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15714                 row.addClass(d.data.cls);
15715             }
15716             
15717             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15718                 var cfg = {
15719                     data : d.data,
15720                     html : d.data[this.displayField]
15721                 };
15722                 
15723                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15724                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15725                 }
15726             }
15727             row.removeClass('selected');
15728             if(!this.multiple && this.valueField &&
15729                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15730             {
15731                 // radio buttons..
15732                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15733                 row.addClass('selected');
15734             }
15735             
15736             if(this.multiple && this.valueField &&
15737                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15738             {
15739                 
15740                 // checkboxes...
15741                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15742                 this.tickItems.push(d.data);
15743             }
15744             
15745             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15746             
15747         }, this);
15748         
15749         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15750         
15751         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15752
15753         if(this.modalTitle.length){
15754             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15755         }
15756
15757         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15758         
15759         if(this.mobile_restrict_height && listHeight < bodyHeight){
15760             this.touchViewBodyEl.setHeight(listHeight);
15761         }
15762         
15763         var _this = this;
15764         
15765         if(firstChecked && listHeight > bodyHeight){
15766             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15767         }
15768         
15769     },
15770     
15771     onTouchViewLoadException : function()
15772     {
15773         this.hideTouchView();
15774     },
15775     
15776     onTouchViewEmptyResults : function()
15777     {
15778         this.clearTouchView();
15779         
15780         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15781         
15782         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15783         
15784     },
15785     
15786     clearTouchView : function()
15787     {
15788         this.touchViewListGroup.dom.innerHTML = '';
15789     },
15790     
15791     onTouchViewClick : function(e, el, o)
15792     {
15793         e.preventDefault();
15794         
15795         var row = o.row;
15796         var rowIndex = o.rowIndex;
15797         
15798         var r = this.store.getAt(rowIndex);
15799         
15800         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15801             
15802             if(!this.multiple){
15803                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15804                     c.dom.removeAttribute('checked');
15805                 }, this);
15806
15807                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15808
15809                 this.setFromData(r.data);
15810
15811                 var close = this.closeTriggerEl();
15812
15813                 if(close){
15814                     close.show();
15815                 }
15816
15817                 this.hideTouchView();
15818
15819                 this.fireEvent('select', this, r, rowIndex);
15820
15821                 return;
15822             }
15823
15824             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15825                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15826                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15827                 return;
15828             }
15829
15830             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15831             this.addItem(r.data);
15832             this.tickItems.push(r.data);
15833         }
15834     },
15835     
15836     getAutoCreateNativeIOS : function()
15837     {
15838         var cfg = {
15839             cls: 'form-group' //input-group,
15840         };
15841         
15842         var combobox =  {
15843             tag: 'select',
15844             cls : 'roo-ios-select'
15845         };
15846         
15847         if (this.name) {
15848             combobox.name = this.name;
15849         }
15850         
15851         if (this.disabled) {
15852             combobox.disabled = true;
15853         }
15854         
15855         var settings = this;
15856         
15857         ['xs','sm','md','lg'].map(function(size){
15858             if (settings[size]) {
15859                 cfg.cls += ' col-' + size + '-' + settings[size];
15860             }
15861         });
15862         
15863         cfg.cn = combobox;
15864         
15865         return cfg;
15866         
15867     },
15868     
15869     initIOSView : function()
15870     {
15871         this.store.on('load', this.onIOSViewLoad, this);
15872         
15873         return;
15874     },
15875     
15876     onIOSViewLoad : function()
15877     {
15878         if(this.store.getCount() < 1){
15879             return;
15880         }
15881         
15882         this.clearIOSView();
15883         
15884         if(this.allowBlank) {
15885             
15886             var default_text = '-- SELECT --';
15887             
15888             if(this.placeholder.length){
15889                 default_text = this.placeholder;
15890             }
15891             
15892             if(this.emptyTitle.length){
15893                 default_text += ' - ' + this.emptyTitle + ' -';
15894             }
15895             
15896             var opt = this.inputEl().createChild({
15897                 tag: 'option',
15898                 value : 0,
15899                 html : default_text
15900             });
15901             
15902             var o = {};
15903             o[this.valueField] = 0;
15904             o[this.displayField] = default_text;
15905             
15906             this.ios_options.push({
15907                 data : o,
15908                 el : opt
15909             });
15910             
15911         }
15912         
15913         this.store.data.each(function(d, rowIndex){
15914             
15915             var html = '';
15916             
15917             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15918                 html = d.data[this.displayField];
15919             }
15920             
15921             var value = '';
15922             
15923             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15924                 value = d.data[this.valueField];
15925             }
15926             
15927             var option = {
15928                 tag: 'option',
15929                 value : value,
15930                 html : html
15931             };
15932             
15933             if(this.value == d.data[this.valueField]){
15934                 option['selected'] = true;
15935             }
15936             
15937             var opt = this.inputEl().createChild(option);
15938             
15939             this.ios_options.push({
15940                 data : d.data,
15941                 el : opt
15942             });
15943             
15944         }, this);
15945         
15946         this.inputEl().on('change', function(){
15947            this.fireEvent('select', this);
15948         }, this);
15949         
15950     },
15951     
15952     clearIOSView: function()
15953     {
15954         this.inputEl().dom.innerHTML = '';
15955         
15956         this.ios_options = [];
15957     },
15958     
15959     setIOSValue: function(v)
15960     {
15961         this.value = v;
15962         
15963         if(!this.ios_options){
15964             return;
15965         }
15966         
15967         Roo.each(this.ios_options, function(opts){
15968            
15969            opts.el.dom.removeAttribute('selected');
15970            
15971            if(opts.data[this.valueField] != v){
15972                return;
15973            }
15974            
15975            opts.el.dom.setAttribute('selected', true);
15976            
15977         }, this);
15978     }
15979
15980     /** 
15981     * @cfg {Boolean} grow 
15982     * @hide 
15983     */
15984     /** 
15985     * @cfg {Number} growMin 
15986     * @hide 
15987     */
15988     /** 
15989     * @cfg {Number} growMax 
15990     * @hide 
15991     */
15992     /**
15993      * @hide
15994      * @method autoSize
15995      */
15996 });
15997
15998 Roo.apply(Roo.bootstrap.ComboBox,  {
15999     
16000     header : {
16001         tag: 'div',
16002         cls: 'modal-header',
16003         cn: [
16004             {
16005                 tag: 'h4',
16006                 cls: 'modal-title'
16007             }
16008         ]
16009     },
16010     
16011     body : {
16012         tag: 'div',
16013         cls: 'modal-body',
16014         cn: [
16015             {
16016                 tag: 'ul',
16017                 cls: 'list-group'
16018             }
16019         ]
16020     },
16021     
16022     listItemRadio : {
16023         tag: 'li',
16024         cls: 'list-group-item',
16025         cn: [
16026             {
16027                 tag: 'span',
16028                 cls: 'roo-combobox-list-group-item-value'
16029             },
16030             {
16031                 tag: 'div',
16032                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16033                 cn: [
16034                     {
16035                         tag: 'input',
16036                         type: 'radio'
16037                     },
16038                     {
16039                         tag: 'label'
16040                     }
16041                 ]
16042             }
16043         ]
16044     },
16045     
16046     listItemCheckbox : {
16047         tag: 'li',
16048         cls: 'list-group-item',
16049         cn: [
16050             {
16051                 tag: 'span',
16052                 cls: 'roo-combobox-list-group-item-value'
16053             },
16054             {
16055                 tag: 'div',
16056                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16057                 cn: [
16058                     {
16059                         tag: 'input',
16060                         type: 'checkbox'
16061                     },
16062                     {
16063                         tag: 'label'
16064                     }
16065                 ]
16066             }
16067         ]
16068     },
16069     
16070     emptyResult : {
16071         tag: 'div',
16072         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16073     },
16074     
16075     footer : {
16076         tag: 'div',
16077         cls: 'modal-footer',
16078         cn: [
16079             {
16080                 tag: 'div',
16081                 cls: 'row',
16082                 cn: [
16083                     {
16084                         tag: 'div',
16085                         cls: 'col-xs-6 text-left',
16086                         cn: {
16087                             tag: 'button',
16088                             cls: 'btn btn-danger roo-touch-view-cancel',
16089                             html: 'Cancel'
16090                         }
16091                     },
16092                     {
16093                         tag: 'div',
16094                         cls: 'col-xs-6 text-right',
16095                         cn: {
16096                             tag: 'button',
16097                             cls: 'btn btn-success roo-touch-view-ok',
16098                             html: 'OK'
16099                         }
16100                     }
16101                 ]
16102             }
16103         ]
16104         
16105     }
16106 });
16107
16108 Roo.apply(Roo.bootstrap.ComboBox,  {
16109     
16110     touchViewTemplate : {
16111         tag: 'div',
16112         cls: 'modal fade roo-combobox-touch-view',
16113         cn: [
16114             {
16115                 tag: 'div',
16116                 cls: 'modal-dialog',
16117                 style : 'position:fixed', // we have to fix position....
16118                 cn: [
16119                     {
16120                         tag: 'div',
16121                         cls: 'modal-content',
16122                         cn: [
16123                             Roo.bootstrap.ComboBox.header,
16124                             Roo.bootstrap.ComboBox.body,
16125                             Roo.bootstrap.ComboBox.footer
16126                         ]
16127                     }
16128                 ]
16129             }
16130         ]
16131     }
16132 });/*
16133  * Based on:
16134  * Ext JS Library 1.1.1
16135  * Copyright(c) 2006-2007, Ext JS, LLC.
16136  *
16137  * Originally Released Under LGPL - original licence link has changed is not relivant.
16138  *
16139  * Fork - LGPL
16140  * <script type="text/javascript">
16141  */
16142
16143 /**
16144  * @class Roo.View
16145  * @extends Roo.util.Observable
16146  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16147  * This class also supports single and multi selection modes. <br>
16148  * Create a data model bound view:
16149  <pre><code>
16150  var store = new Roo.data.Store(...);
16151
16152  var view = new Roo.View({
16153     el : "my-element",
16154     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16155  
16156     singleSelect: true,
16157     selectedClass: "ydataview-selected",
16158     store: store
16159  });
16160
16161  // listen for node click?
16162  view.on("click", function(vw, index, node, e){
16163  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16164  });
16165
16166  // load XML data
16167  dataModel.load("foobar.xml");
16168  </code></pre>
16169  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16170  * <br><br>
16171  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16172  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16173  * 
16174  * Note: old style constructor is still suported (container, template, config)
16175  * 
16176  * @constructor
16177  * Create a new View
16178  * @param {Object} config The config object
16179  * 
16180  */
16181 Roo.View = function(config, depreciated_tpl, depreciated_config){
16182     
16183     this.parent = false;
16184     
16185     if (typeof(depreciated_tpl) == 'undefined') {
16186         // new way.. - universal constructor.
16187         Roo.apply(this, config);
16188         this.el  = Roo.get(this.el);
16189     } else {
16190         // old format..
16191         this.el  = Roo.get(config);
16192         this.tpl = depreciated_tpl;
16193         Roo.apply(this, depreciated_config);
16194     }
16195     this.wrapEl  = this.el.wrap().wrap();
16196     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16197     
16198     
16199     if(typeof(this.tpl) == "string"){
16200         this.tpl = new Roo.Template(this.tpl);
16201     } else {
16202         // support xtype ctors..
16203         this.tpl = new Roo.factory(this.tpl, Roo);
16204     }
16205     
16206     
16207     this.tpl.compile();
16208     
16209     /** @private */
16210     this.addEvents({
16211         /**
16212          * @event beforeclick
16213          * Fires before a click is processed. Returns false to cancel the default action.
16214          * @param {Roo.View} this
16215          * @param {Number} index The index of the target node
16216          * @param {HTMLElement} node The target node
16217          * @param {Roo.EventObject} e The raw event object
16218          */
16219             "beforeclick" : true,
16220         /**
16221          * @event click
16222          * Fires when a template node is clicked.
16223          * @param {Roo.View} this
16224          * @param {Number} index The index of the target node
16225          * @param {HTMLElement} node The target node
16226          * @param {Roo.EventObject} e The raw event object
16227          */
16228             "click" : true,
16229         /**
16230          * @event dblclick
16231          * Fires when a template node is double clicked.
16232          * @param {Roo.View} this
16233          * @param {Number} index The index of the target node
16234          * @param {HTMLElement} node The target node
16235          * @param {Roo.EventObject} e The raw event object
16236          */
16237             "dblclick" : true,
16238         /**
16239          * @event contextmenu
16240          * Fires when a template node is right clicked.
16241          * @param {Roo.View} this
16242          * @param {Number} index The index of the target node
16243          * @param {HTMLElement} node The target node
16244          * @param {Roo.EventObject} e The raw event object
16245          */
16246             "contextmenu" : true,
16247         /**
16248          * @event selectionchange
16249          * Fires when the selected nodes change.
16250          * @param {Roo.View} this
16251          * @param {Array} selections Array of the selected nodes
16252          */
16253             "selectionchange" : true,
16254     
16255         /**
16256          * @event beforeselect
16257          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16258          * @param {Roo.View} this
16259          * @param {HTMLElement} node The node to be selected
16260          * @param {Array} selections Array of currently selected nodes
16261          */
16262             "beforeselect" : true,
16263         /**
16264          * @event preparedata
16265          * Fires on every row to render, to allow you to change the data.
16266          * @param {Roo.View} this
16267          * @param {Object} data to be rendered (change this)
16268          */
16269           "preparedata" : true
16270           
16271           
16272         });
16273
16274
16275
16276     this.el.on({
16277         "click": this.onClick,
16278         "dblclick": this.onDblClick,
16279         "contextmenu": this.onContextMenu,
16280         scope:this
16281     });
16282
16283     this.selections = [];
16284     this.nodes = [];
16285     this.cmp = new Roo.CompositeElementLite([]);
16286     if(this.store){
16287         this.store = Roo.factory(this.store, Roo.data);
16288         this.setStore(this.store, true);
16289     }
16290     
16291     if ( this.footer && this.footer.xtype) {
16292            
16293          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16294         
16295         this.footer.dataSource = this.store;
16296         this.footer.container = fctr;
16297         this.footer = Roo.factory(this.footer, Roo);
16298         fctr.insertFirst(this.el);
16299         
16300         // this is a bit insane - as the paging toolbar seems to detach the el..
16301 //        dom.parentNode.parentNode.parentNode
16302          // they get detached?
16303     }
16304     
16305     
16306     Roo.View.superclass.constructor.call(this);
16307     
16308     
16309 };
16310
16311 Roo.extend(Roo.View, Roo.util.Observable, {
16312     
16313      /**
16314      * @cfg {Roo.data.Store} store Data store to load data from.
16315      */
16316     store : false,
16317     
16318     /**
16319      * @cfg {String|Roo.Element} el The container element.
16320      */
16321     el : '',
16322     
16323     /**
16324      * @cfg {String|Roo.Template} tpl The template used by this View 
16325      */
16326     tpl : false,
16327     /**
16328      * @cfg {String} dataName the named area of the template to use as the data area
16329      *                          Works with domtemplates roo-name="name"
16330      */
16331     dataName: false,
16332     /**
16333      * @cfg {String} selectedClass The css class to add to selected nodes
16334      */
16335     selectedClass : "x-view-selected",
16336      /**
16337      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16338      */
16339     emptyText : "",
16340     
16341     /**
16342      * @cfg {String} text to display on mask (default Loading)
16343      */
16344     mask : false,
16345     /**
16346      * @cfg {Boolean} multiSelect Allow multiple selection
16347      */
16348     multiSelect : false,
16349     /**
16350      * @cfg {Boolean} singleSelect Allow single selection
16351      */
16352     singleSelect:  false,
16353     
16354     /**
16355      * @cfg {Boolean} toggleSelect - selecting 
16356      */
16357     toggleSelect : false,
16358     
16359     /**
16360      * @cfg {Boolean} tickable - selecting 
16361      */
16362     tickable : false,
16363     
16364     /**
16365      * Returns the element this view is bound to.
16366      * @return {Roo.Element}
16367      */
16368     getEl : function(){
16369         return this.wrapEl;
16370     },
16371     
16372     
16373
16374     /**
16375      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16376      */
16377     refresh : function(){
16378         //Roo.log('refresh');
16379         var t = this.tpl;
16380         
16381         // if we are using something like 'domtemplate', then
16382         // the what gets used is:
16383         // t.applySubtemplate(NAME, data, wrapping data..)
16384         // the outer template then get' applied with
16385         //     the store 'extra data'
16386         // and the body get's added to the
16387         //      roo-name="data" node?
16388         //      <span class='roo-tpl-{name}'></span> ?????
16389         
16390         
16391         
16392         this.clearSelections();
16393         this.el.update("");
16394         var html = [];
16395         var records = this.store.getRange();
16396         if(records.length < 1) {
16397             
16398             // is this valid??  = should it render a template??
16399             
16400             this.el.update(this.emptyText);
16401             return;
16402         }
16403         var el = this.el;
16404         if (this.dataName) {
16405             this.el.update(t.apply(this.store.meta)); //????
16406             el = this.el.child('.roo-tpl-' + this.dataName);
16407         }
16408         
16409         for(var i = 0, len = records.length; i < len; i++){
16410             var data = this.prepareData(records[i].data, i, records[i]);
16411             this.fireEvent("preparedata", this, data, i, records[i]);
16412             
16413             var d = Roo.apply({}, data);
16414             
16415             if(this.tickable){
16416                 Roo.apply(d, {'roo-id' : Roo.id()});
16417                 
16418                 var _this = this;
16419             
16420                 Roo.each(this.parent.item, function(item){
16421                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16422                         return;
16423                     }
16424                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16425                 });
16426             }
16427             
16428             html[html.length] = Roo.util.Format.trim(
16429                 this.dataName ?
16430                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16431                     t.apply(d)
16432             );
16433         }
16434         
16435         
16436         
16437         el.update(html.join(""));
16438         this.nodes = el.dom.childNodes;
16439         this.updateIndexes(0);
16440     },
16441     
16442
16443     /**
16444      * Function to override to reformat the data that is sent to
16445      * the template for each node.
16446      * DEPRICATED - use the preparedata event handler.
16447      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16448      * a JSON object for an UpdateManager bound view).
16449      */
16450     prepareData : function(data, index, record)
16451     {
16452         this.fireEvent("preparedata", this, data, index, record);
16453         return data;
16454     },
16455
16456     onUpdate : function(ds, record){
16457         // Roo.log('on update');   
16458         this.clearSelections();
16459         var index = this.store.indexOf(record);
16460         var n = this.nodes[index];
16461         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16462         n.parentNode.removeChild(n);
16463         this.updateIndexes(index, index);
16464     },
16465
16466     
16467     
16468 // --------- FIXME     
16469     onAdd : function(ds, records, index)
16470     {
16471         //Roo.log(['on Add', ds, records, index] );        
16472         this.clearSelections();
16473         if(this.nodes.length == 0){
16474             this.refresh();
16475             return;
16476         }
16477         var n = this.nodes[index];
16478         for(var i = 0, len = records.length; i < len; i++){
16479             var d = this.prepareData(records[i].data, i, records[i]);
16480             if(n){
16481                 this.tpl.insertBefore(n, d);
16482             }else{
16483                 
16484                 this.tpl.append(this.el, d);
16485             }
16486         }
16487         this.updateIndexes(index);
16488     },
16489
16490     onRemove : function(ds, record, index){
16491        // Roo.log('onRemove');
16492         this.clearSelections();
16493         var el = this.dataName  ?
16494             this.el.child('.roo-tpl-' + this.dataName) :
16495             this.el; 
16496         
16497         el.dom.removeChild(this.nodes[index]);
16498         this.updateIndexes(index);
16499     },
16500
16501     /**
16502      * Refresh an individual node.
16503      * @param {Number} index
16504      */
16505     refreshNode : function(index){
16506         this.onUpdate(this.store, this.store.getAt(index));
16507     },
16508
16509     updateIndexes : function(startIndex, endIndex){
16510         var ns = this.nodes;
16511         startIndex = startIndex || 0;
16512         endIndex = endIndex || ns.length - 1;
16513         for(var i = startIndex; i <= endIndex; i++){
16514             ns[i].nodeIndex = i;
16515         }
16516     },
16517
16518     /**
16519      * Changes the data store this view uses and refresh the view.
16520      * @param {Store} store
16521      */
16522     setStore : function(store, initial){
16523         if(!initial && this.store){
16524             this.store.un("datachanged", this.refresh);
16525             this.store.un("add", this.onAdd);
16526             this.store.un("remove", this.onRemove);
16527             this.store.un("update", this.onUpdate);
16528             this.store.un("clear", this.refresh);
16529             this.store.un("beforeload", this.onBeforeLoad);
16530             this.store.un("load", this.onLoad);
16531             this.store.un("loadexception", this.onLoad);
16532         }
16533         if(store){
16534           
16535             store.on("datachanged", this.refresh, this);
16536             store.on("add", this.onAdd, this);
16537             store.on("remove", this.onRemove, this);
16538             store.on("update", this.onUpdate, this);
16539             store.on("clear", this.refresh, this);
16540             store.on("beforeload", this.onBeforeLoad, this);
16541             store.on("load", this.onLoad, this);
16542             store.on("loadexception", this.onLoad, this);
16543         }
16544         
16545         if(store){
16546             this.refresh();
16547         }
16548     },
16549     /**
16550      * onbeforeLoad - masks the loading area.
16551      *
16552      */
16553     onBeforeLoad : function(store,opts)
16554     {
16555          //Roo.log('onBeforeLoad');   
16556         if (!opts.add) {
16557             this.el.update("");
16558         }
16559         this.el.mask(this.mask ? this.mask : "Loading" ); 
16560     },
16561     onLoad : function ()
16562     {
16563         this.el.unmask();
16564     },
16565     
16566
16567     /**
16568      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16569      * @param {HTMLElement} node
16570      * @return {HTMLElement} The template node
16571      */
16572     findItemFromChild : function(node){
16573         var el = this.dataName  ?
16574             this.el.child('.roo-tpl-' + this.dataName,true) :
16575             this.el.dom; 
16576         
16577         if(!node || node.parentNode == el){
16578                     return node;
16579             }
16580             var p = node.parentNode;
16581             while(p && p != el){
16582             if(p.parentNode == el){
16583                 return p;
16584             }
16585             p = p.parentNode;
16586         }
16587             return null;
16588     },
16589
16590     /** @ignore */
16591     onClick : function(e){
16592         var item = this.findItemFromChild(e.getTarget());
16593         if(item){
16594             var index = this.indexOf(item);
16595             if(this.onItemClick(item, index, e) !== false){
16596                 this.fireEvent("click", this, index, item, e);
16597             }
16598         }else{
16599             this.clearSelections();
16600         }
16601     },
16602
16603     /** @ignore */
16604     onContextMenu : function(e){
16605         var item = this.findItemFromChild(e.getTarget());
16606         if(item){
16607             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16608         }
16609     },
16610
16611     /** @ignore */
16612     onDblClick : function(e){
16613         var item = this.findItemFromChild(e.getTarget());
16614         if(item){
16615             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16616         }
16617     },
16618
16619     onItemClick : function(item, index, e)
16620     {
16621         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16622             return false;
16623         }
16624         if (this.toggleSelect) {
16625             var m = this.isSelected(item) ? 'unselect' : 'select';
16626             //Roo.log(m);
16627             var _t = this;
16628             _t[m](item, true, false);
16629             return true;
16630         }
16631         if(this.multiSelect || this.singleSelect){
16632             if(this.multiSelect && e.shiftKey && this.lastSelection){
16633                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16634             }else{
16635                 this.select(item, this.multiSelect && e.ctrlKey);
16636                 this.lastSelection = item;
16637             }
16638             
16639             if(!this.tickable){
16640                 e.preventDefault();
16641             }
16642             
16643         }
16644         return true;
16645     },
16646
16647     /**
16648      * Get the number of selected nodes.
16649      * @return {Number}
16650      */
16651     getSelectionCount : function(){
16652         return this.selections.length;
16653     },
16654
16655     /**
16656      * Get the currently selected nodes.
16657      * @return {Array} An array of HTMLElements
16658      */
16659     getSelectedNodes : function(){
16660         return this.selections;
16661     },
16662
16663     /**
16664      * Get the indexes of the selected nodes.
16665      * @return {Array}
16666      */
16667     getSelectedIndexes : function(){
16668         var indexes = [], s = this.selections;
16669         for(var i = 0, len = s.length; i < len; i++){
16670             indexes.push(s[i].nodeIndex);
16671         }
16672         return indexes;
16673     },
16674
16675     /**
16676      * Clear all selections
16677      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16678      */
16679     clearSelections : function(suppressEvent){
16680         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16681             this.cmp.elements = this.selections;
16682             this.cmp.removeClass(this.selectedClass);
16683             this.selections = [];
16684             if(!suppressEvent){
16685                 this.fireEvent("selectionchange", this, this.selections);
16686             }
16687         }
16688     },
16689
16690     /**
16691      * Returns true if the passed node is selected
16692      * @param {HTMLElement/Number} node The node or node index
16693      * @return {Boolean}
16694      */
16695     isSelected : function(node){
16696         var s = this.selections;
16697         if(s.length < 1){
16698             return false;
16699         }
16700         node = this.getNode(node);
16701         return s.indexOf(node) !== -1;
16702     },
16703
16704     /**
16705      * Selects nodes.
16706      * @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
16707      * @param {Boolean} keepExisting (optional) true to keep existing selections
16708      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16709      */
16710     select : function(nodeInfo, keepExisting, suppressEvent){
16711         if(nodeInfo instanceof Array){
16712             if(!keepExisting){
16713                 this.clearSelections(true);
16714             }
16715             for(var i = 0, len = nodeInfo.length; i < len; i++){
16716                 this.select(nodeInfo[i], true, true);
16717             }
16718             return;
16719         } 
16720         var node = this.getNode(nodeInfo);
16721         if(!node || this.isSelected(node)){
16722             return; // already selected.
16723         }
16724         if(!keepExisting){
16725             this.clearSelections(true);
16726         }
16727         
16728         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16729             Roo.fly(node).addClass(this.selectedClass);
16730             this.selections.push(node);
16731             if(!suppressEvent){
16732                 this.fireEvent("selectionchange", this, this.selections);
16733             }
16734         }
16735         
16736         
16737     },
16738       /**
16739      * Unselects nodes.
16740      * @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
16741      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16742      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16743      */
16744     unselect : function(nodeInfo, keepExisting, suppressEvent)
16745     {
16746         if(nodeInfo instanceof Array){
16747             Roo.each(this.selections, function(s) {
16748                 this.unselect(s, nodeInfo);
16749             }, this);
16750             return;
16751         }
16752         var node = this.getNode(nodeInfo);
16753         if(!node || !this.isSelected(node)){
16754             //Roo.log("not selected");
16755             return; // not selected.
16756         }
16757         // fireevent???
16758         var ns = [];
16759         Roo.each(this.selections, function(s) {
16760             if (s == node ) {
16761                 Roo.fly(node).removeClass(this.selectedClass);
16762
16763                 return;
16764             }
16765             ns.push(s);
16766         },this);
16767         
16768         this.selections= ns;
16769         this.fireEvent("selectionchange", this, this.selections);
16770     },
16771
16772     /**
16773      * Gets a template node.
16774      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16775      * @return {HTMLElement} The node or null if it wasn't found
16776      */
16777     getNode : function(nodeInfo){
16778         if(typeof nodeInfo == "string"){
16779             return document.getElementById(nodeInfo);
16780         }else if(typeof nodeInfo == "number"){
16781             return this.nodes[nodeInfo];
16782         }
16783         return nodeInfo;
16784     },
16785
16786     /**
16787      * Gets a range template nodes.
16788      * @param {Number} startIndex
16789      * @param {Number} endIndex
16790      * @return {Array} An array of nodes
16791      */
16792     getNodes : function(start, end){
16793         var ns = this.nodes;
16794         start = start || 0;
16795         end = typeof end == "undefined" ? ns.length - 1 : end;
16796         var nodes = [];
16797         if(start <= end){
16798             for(var i = start; i <= end; i++){
16799                 nodes.push(ns[i]);
16800             }
16801         } else{
16802             for(var i = start; i >= end; i--){
16803                 nodes.push(ns[i]);
16804             }
16805         }
16806         return nodes;
16807     },
16808
16809     /**
16810      * Finds the index of the passed node
16811      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16812      * @return {Number} The index of the node or -1
16813      */
16814     indexOf : function(node){
16815         node = this.getNode(node);
16816         if(typeof node.nodeIndex == "number"){
16817             return node.nodeIndex;
16818         }
16819         var ns = this.nodes;
16820         for(var i = 0, len = ns.length; i < len; i++){
16821             if(ns[i] == node){
16822                 return i;
16823             }
16824         }
16825         return -1;
16826     }
16827 });
16828 /*
16829  * - LGPL
16830  *
16831  * based on jquery fullcalendar
16832  * 
16833  */
16834
16835 Roo.bootstrap = Roo.bootstrap || {};
16836 /**
16837  * @class Roo.bootstrap.Calendar
16838  * @extends Roo.bootstrap.Component
16839  * Bootstrap Calendar class
16840  * @cfg {Boolean} loadMask (true|false) default false
16841  * @cfg {Object} header generate the user specific header of the calendar, default false
16842
16843  * @constructor
16844  * Create a new Container
16845  * @param {Object} config The config object
16846  */
16847
16848
16849
16850 Roo.bootstrap.Calendar = function(config){
16851     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16852      this.addEvents({
16853         /**
16854              * @event select
16855              * Fires when a date is selected
16856              * @param {DatePicker} this
16857              * @param {Date} date The selected date
16858              */
16859         'select': true,
16860         /**
16861              * @event monthchange
16862              * Fires when the displayed month changes 
16863              * @param {DatePicker} this
16864              * @param {Date} date The selected month
16865              */
16866         'monthchange': true,
16867         /**
16868              * @event evententer
16869              * Fires when mouse over an event
16870              * @param {Calendar} this
16871              * @param {event} Event
16872              */
16873         'evententer': true,
16874         /**
16875              * @event eventleave
16876              * Fires when the mouse leaves an
16877              * @param {Calendar} this
16878              * @param {event}
16879              */
16880         'eventleave': true,
16881         /**
16882              * @event eventclick
16883              * Fires when the mouse click an
16884              * @param {Calendar} this
16885              * @param {event}
16886              */
16887         'eventclick': true
16888         
16889     });
16890
16891 };
16892
16893 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16894     
16895      /**
16896      * @cfg {Number} startDay
16897      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16898      */
16899     startDay : 0,
16900     
16901     loadMask : false,
16902     
16903     header : false,
16904       
16905     getAutoCreate : function(){
16906         
16907         
16908         var fc_button = function(name, corner, style, content ) {
16909             return Roo.apply({},{
16910                 tag : 'span',
16911                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16912                          (corner.length ?
16913                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16914                             ''
16915                         ),
16916                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16917                 unselectable: 'on'
16918             });
16919         };
16920         
16921         var header = {};
16922         
16923         if(!this.header){
16924             header = {
16925                 tag : 'table',
16926                 cls : 'fc-header',
16927                 style : 'width:100%',
16928                 cn : [
16929                     {
16930                         tag: 'tr',
16931                         cn : [
16932                             {
16933                                 tag : 'td',
16934                                 cls : 'fc-header-left',
16935                                 cn : [
16936                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16937                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16938                                     { tag: 'span', cls: 'fc-header-space' },
16939                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16940
16941
16942                                 ]
16943                             },
16944
16945                             {
16946                                 tag : 'td',
16947                                 cls : 'fc-header-center',
16948                                 cn : [
16949                                     {
16950                                         tag: 'span',
16951                                         cls: 'fc-header-title',
16952                                         cn : {
16953                                             tag: 'H2',
16954                                             html : 'month / year'
16955                                         }
16956                                     }
16957
16958                                 ]
16959                             },
16960                             {
16961                                 tag : 'td',
16962                                 cls : 'fc-header-right',
16963                                 cn : [
16964                               /*      fc_button('month', 'left', '', 'month' ),
16965                                     fc_button('week', '', '', 'week' ),
16966                                     fc_button('day', 'right', '', 'day' )
16967                                 */    
16968
16969                                 ]
16970                             }
16971
16972                         ]
16973                     }
16974                 ]
16975             };
16976         }
16977         
16978         header = this.header;
16979         
16980        
16981         var cal_heads = function() {
16982             var ret = [];
16983             // fixme - handle this.
16984             
16985             for (var i =0; i < Date.dayNames.length; i++) {
16986                 var d = Date.dayNames[i];
16987                 ret.push({
16988                     tag: 'th',
16989                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16990                     html : d.substring(0,3)
16991                 });
16992                 
16993             }
16994             ret[0].cls += ' fc-first';
16995             ret[6].cls += ' fc-last';
16996             return ret;
16997         };
16998         var cal_cell = function(n) {
16999             return  {
17000                 tag: 'td',
17001                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17002                 cn : [
17003                     {
17004                         cn : [
17005                             {
17006                                 cls: 'fc-day-number',
17007                                 html: 'D'
17008                             },
17009                             {
17010                                 cls: 'fc-day-content',
17011                              
17012                                 cn : [
17013                                      {
17014                                         style: 'position: relative;' // height: 17px;
17015                                     }
17016                                 ]
17017                             }
17018                             
17019                             
17020                         ]
17021                     }
17022                 ]
17023                 
17024             }
17025         };
17026         var cal_rows = function() {
17027             
17028             var ret = [];
17029             for (var r = 0; r < 6; r++) {
17030                 var row= {
17031                     tag : 'tr',
17032                     cls : 'fc-week',
17033                     cn : []
17034                 };
17035                 
17036                 for (var i =0; i < Date.dayNames.length; i++) {
17037                     var d = Date.dayNames[i];
17038                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17039
17040                 }
17041                 row.cn[0].cls+=' fc-first';
17042                 row.cn[0].cn[0].style = 'min-height:90px';
17043                 row.cn[6].cls+=' fc-last';
17044                 ret.push(row);
17045                 
17046             }
17047             ret[0].cls += ' fc-first';
17048             ret[4].cls += ' fc-prev-last';
17049             ret[5].cls += ' fc-last';
17050             return ret;
17051             
17052         };
17053         
17054         var cal_table = {
17055             tag: 'table',
17056             cls: 'fc-border-separate',
17057             style : 'width:100%',
17058             cellspacing  : 0,
17059             cn : [
17060                 { 
17061                     tag: 'thead',
17062                     cn : [
17063                         { 
17064                             tag: 'tr',
17065                             cls : 'fc-first fc-last',
17066                             cn : cal_heads()
17067                         }
17068                     ]
17069                 },
17070                 { 
17071                     tag: 'tbody',
17072                     cn : cal_rows()
17073                 }
17074                   
17075             ]
17076         };
17077          
17078          var cfg = {
17079             cls : 'fc fc-ltr',
17080             cn : [
17081                 header,
17082                 {
17083                     cls : 'fc-content',
17084                     style : "position: relative;",
17085                     cn : [
17086                         {
17087                             cls : 'fc-view fc-view-month fc-grid',
17088                             style : 'position: relative',
17089                             unselectable : 'on',
17090                             cn : [
17091                                 {
17092                                     cls : 'fc-event-container',
17093                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17094                                 },
17095                                 cal_table
17096                             ]
17097                         }
17098                     ]
17099     
17100                 }
17101            ] 
17102             
17103         };
17104         
17105          
17106         
17107         return cfg;
17108     },
17109     
17110     
17111     initEvents : function()
17112     {
17113         if(!this.store){
17114             throw "can not find store for calendar";
17115         }
17116         
17117         var mark = {
17118             tag: "div",
17119             cls:"x-dlg-mask",
17120             style: "text-align:center",
17121             cn: [
17122                 {
17123                     tag: "div",
17124                     style: "background-color:white;width:50%;margin:250 auto",
17125                     cn: [
17126                         {
17127                             tag: "img",
17128                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17129                         },
17130                         {
17131                             tag: "span",
17132                             html: "Loading"
17133                         }
17134                         
17135                     ]
17136                 }
17137             ]
17138         };
17139         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17140         
17141         var size = this.el.select('.fc-content', true).first().getSize();
17142         this.maskEl.setSize(size.width, size.height);
17143         this.maskEl.enableDisplayMode("block");
17144         if(!this.loadMask){
17145             this.maskEl.hide();
17146         }
17147         
17148         this.store = Roo.factory(this.store, Roo.data);
17149         this.store.on('load', this.onLoad, this);
17150         this.store.on('beforeload', this.onBeforeLoad, this);
17151         
17152         this.resize();
17153         
17154         this.cells = this.el.select('.fc-day',true);
17155         //Roo.log(this.cells);
17156         this.textNodes = this.el.query('.fc-day-number');
17157         this.cells.addClassOnOver('fc-state-hover');
17158         
17159         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17160         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17161         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17162         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17163         
17164         this.on('monthchange', this.onMonthChange, this);
17165         
17166         this.update(new Date().clearTime());
17167     },
17168     
17169     resize : function() {
17170         var sz  = this.el.getSize();
17171         
17172         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17173         this.el.select('.fc-day-content div',true).setHeight(34);
17174     },
17175     
17176     
17177     // private
17178     showPrevMonth : function(e){
17179         this.update(this.activeDate.add("mo", -1));
17180     },
17181     showToday : function(e){
17182         this.update(new Date().clearTime());
17183     },
17184     // private
17185     showNextMonth : function(e){
17186         this.update(this.activeDate.add("mo", 1));
17187     },
17188
17189     // private
17190     showPrevYear : function(){
17191         this.update(this.activeDate.add("y", -1));
17192     },
17193
17194     // private
17195     showNextYear : function(){
17196         this.update(this.activeDate.add("y", 1));
17197     },
17198
17199     
17200    // private
17201     update : function(date)
17202     {
17203         var vd = this.activeDate;
17204         this.activeDate = date;
17205 //        if(vd && this.el){
17206 //            var t = date.getTime();
17207 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17208 //                Roo.log('using add remove');
17209 //                
17210 //                this.fireEvent('monthchange', this, date);
17211 //                
17212 //                this.cells.removeClass("fc-state-highlight");
17213 //                this.cells.each(function(c){
17214 //                   if(c.dateValue == t){
17215 //                       c.addClass("fc-state-highlight");
17216 //                       setTimeout(function(){
17217 //                            try{c.dom.firstChild.focus();}catch(e){}
17218 //                       }, 50);
17219 //                       return false;
17220 //                   }
17221 //                   return true;
17222 //                });
17223 //                return;
17224 //            }
17225 //        }
17226         
17227         var days = date.getDaysInMonth();
17228         
17229         var firstOfMonth = date.getFirstDateOfMonth();
17230         var startingPos = firstOfMonth.getDay()-this.startDay;
17231         
17232         if(startingPos < this.startDay){
17233             startingPos += 7;
17234         }
17235         
17236         var pm = date.add(Date.MONTH, -1);
17237         var prevStart = pm.getDaysInMonth()-startingPos;
17238 //        
17239         this.cells = this.el.select('.fc-day',true);
17240         this.textNodes = this.el.query('.fc-day-number');
17241         this.cells.addClassOnOver('fc-state-hover');
17242         
17243         var cells = this.cells.elements;
17244         var textEls = this.textNodes;
17245         
17246         Roo.each(cells, function(cell){
17247             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17248         });
17249         
17250         days += startingPos;
17251
17252         // convert everything to numbers so it's fast
17253         var day = 86400000;
17254         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17255         //Roo.log(d);
17256         //Roo.log(pm);
17257         //Roo.log(prevStart);
17258         
17259         var today = new Date().clearTime().getTime();
17260         var sel = date.clearTime().getTime();
17261         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17262         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17263         var ddMatch = this.disabledDatesRE;
17264         var ddText = this.disabledDatesText;
17265         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17266         var ddaysText = this.disabledDaysText;
17267         var format = this.format;
17268         
17269         var setCellClass = function(cal, cell){
17270             cell.row = 0;
17271             cell.events = [];
17272             cell.more = [];
17273             //Roo.log('set Cell Class');
17274             cell.title = "";
17275             var t = d.getTime();
17276             
17277             //Roo.log(d);
17278             
17279             cell.dateValue = t;
17280             if(t == today){
17281                 cell.className += " fc-today";
17282                 cell.className += " fc-state-highlight";
17283                 cell.title = cal.todayText;
17284             }
17285             if(t == sel){
17286                 // disable highlight in other month..
17287                 //cell.className += " fc-state-highlight";
17288                 
17289             }
17290             // disabling
17291             if(t < min) {
17292                 cell.className = " fc-state-disabled";
17293                 cell.title = cal.minText;
17294                 return;
17295             }
17296             if(t > max) {
17297                 cell.className = " fc-state-disabled";
17298                 cell.title = cal.maxText;
17299                 return;
17300             }
17301             if(ddays){
17302                 if(ddays.indexOf(d.getDay()) != -1){
17303                     cell.title = ddaysText;
17304                     cell.className = " fc-state-disabled";
17305                 }
17306             }
17307             if(ddMatch && format){
17308                 var fvalue = d.dateFormat(format);
17309                 if(ddMatch.test(fvalue)){
17310                     cell.title = ddText.replace("%0", fvalue);
17311                     cell.className = " fc-state-disabled";
17312                 }
17313             }
17314             
17315             if (!cell.initialClassName) {
17316                 cell.initialClassName = cell.dom.className;
17317             }
17318             
17319             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17320         };
17321
17322         var i = 0;
17323         
17324         for(; i < startingPos; i++) {
17325             textEls[i].innerHTML = (++prevStart);
17326             d.setDate(d.getDate()+1);
17327             
17328             cells[i].className = "fc-past fc-other-month";
17329             setCellClass(this, cells[i]);
17330         }
17331         
17332         var intDay = 0;
17333         
17334         for(; i < days; i++){
17335             intDay = i - startingPos + 1;
17336             textEls[i].innerHTML = (intDay);
17337             d.setDate(d.getDate()+1);
17338             
17339             cells[i].className = ''; // "x-date-active";
17340             setCellClass(this, cells[i]);
17341         }
17342         var extraDays = 0;
17343         
17344         for(; i < 42; i++) {
17345             textEls[i].innerHTML = (++extraDays);
17346             d.setDate(d.getDate()+1);
17347             
17348             cells[i].className = "fc-future fc-other-month";
17349             setCellClass(this, cells[i]);
17350         }
17351         
17352         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17353         
17354         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17355         
17356         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17357         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17358         
17359         if(totalRows != 6){
17360             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17361             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17362         }
17363         
17364         this.fireEvent('monthchange', this, date);
17365         
17366         
17367         /*
17368         if(!this.internalRender){
17369             var main = this.el.dom.firstChild;
17370             var w = main.offsetWidth;
17371             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17372             Roo.fly(main).setWidth(w);
17373             this.internalRender = true;
17374             // opera does not respect the auto grow header center column
17375             // then, after it gets a width opera refuses to recalculate
17376             // without a second pass
17377             if(Roo.isOpera && !this.secondPass){
17378                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17379                 this.secondPass = true;
17380                 this.update.defer(10, this, [date]);
17381             }
17382         }
17383         */
17384         
17385     },
17386     
17387     findCell : function(dt) {
17388         dt = dt.clearTime().getTime();
17389         var ret = false;
17390         this.cells.each(function(c){
17391             //Roo.log("check " +c.dateValue + '?=' + dt);
17392             if(c.dateValue == dt){
17393                 ret = c;
17394                 return false;
17395             }
17396             return true;
17397         });
17398         
17399         return ret;
17400     },
17401     
17402     findCells : function(ev) {
17403         var s = ev.start.clone().clearTime().getTime();
17404        // Roo.log(s);
17405         var e= ev.end.clone().clearTime().getTime();
17406        // Roo.log(e);
17407         var ret = [];
17408         this.cells.each(function(c){
17409              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17410             
17411             if(c.dateValue > e){
17412                 return ;
17413             }
17414             if(c.dateValue < s){
17415                 return ;
17416             }
17417             ret.push(c);
17418         });
17419         
17420         return ret;    
17421     },
17422     
17423 //    findBestRow: function(cells)
17424 //    {
17425 //        var ret = 0;
17426 //        
17427 //        for (var i =0 ; i < cells.length;i++) {
17428 //            ret  = Math.max(cells[i].rows || 0,ret);
17429 //        }
17430 //        return ret;
17431 //        
17432 //    },
17433     
17434     
17435     addItem : function(ev)
17436     {
17437         // look for vertical location slot in
17438         var cells = this.findCells(ev);
17439         
17440 //        ev.row = this.findBestRow(cells);
17441         
17442         // work out the location.
17443         
17444         var crow = false;
17445         var rows = [];
17446         for(var i =0; i < cells.length; i++) {
17447             
17448             cells[i].row = cells[0].row;
17449             
17450             if(i == 0){
17451                 cells[i].row = cells[i].row + 1;
17452             }
17453             
17454             if (!crow) {
17455                 crow = {
17456                     start : cells[i],
17457                     end :  cells[i]
17458                 };
17459                 continue;
17460             }
17461             if (crow.start.getY() == cells[i].getY()) {
17462                 // on same row.
17463                 crow.end = cells[i];
17464                 continue;
17465             }
17466             // different row.
17467             rows.push(crow);
17468             crow = {
17469                 start: cells[i],
17470                 end : cells[i]
17471             };
17472             
17473         }
17474         
17475         rows.push(crow);
17476         ev.els = [];
17477         ev.rows = rows;
17478         ev.cells = cells;
17479         
17480         cells[0].events.push(ev);
17481         
17482         this.calevents.push(ev);
17483     },
17484     
17485     clearEvents: function() {
17486         
17487         if(!this.calevents){
17488             return;
17489         }
17490         
17491         Roo.each(this.cells.elements, function(c){
17492             c.row = 0;
17493             c.events = [];
17494             c.more = [];
17495         });
17496         
17497         Roo.each(this.calevents, function(e) {
17498             Roo.each(e.els, function(el) {
17499                 el.un('mouseenter' ,this.onEventEnter, this);
17500                 el.un('mouseleave' ,this.onEventLeave, this);
17501                 el.remove();
17502             },this);
17503         },this);
17504         
17505         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17506             e.remove();
17507         });
17508         
17509     },
17510     
17511     renderEvents: function()
17512     {   
17513         var _this = this;
17514         
17515         this.cells.each(function(c) {
17516             
17517             if(c.row < 5){
17518                 return;
17519             }
17520             
17521             var ev = c.events;
17522             
17523             var r = 4;
17524             if(c.row != c.events.length){
17525                 r = 4 - (4 - (c.row - c.events.length));
17526             }
17527             
17528             c.events = ev.slice(0, r);
17529             c.more = ev.slice(r);
17530             
17531             if(c.more.length && c.more.length == 1){
17532                 c.events.push(c.more.pop());
17533             }
17534             
17535             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17536             
17537         });
17538             
17539         this.cells.each(function(c) {
17540             
17541             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17542             
17543             
17544             for (var e = 0; e < c.events.length; e++){
17545                 var ev = c.events[e];
17546                 var rows = ev.rows;
17547                 
17548                 for(var i = 0; i < rows.length; i++) {
17549                 
17550                     // how many rows should it span..
17551
17552                     var  cfg = {
17553                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17554                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17555
17556                         unselectable : "on",
17557                         cn : [
17558                             {
17559                                 cls: 'fc-event-inner',
17560                                 cn : [
17561     //                                {
17562     //                                  tag:'span',
17563     //                                  cls: 'fc-event-time',
17564     //                                  html : cells.length > 1 ? '' : ev.time
17565     //                                },
17566                                     {
17567                                       tag:'span',
17568                                       cls: 'fc-event-title',
17569                                       html : String.format('{0}', ev.title)
17570                                     }
17571
17572
17573                                 ]
17574                             },
17575                             {
17576                                 cls: 'ui-resizable-handle ui-resizable-e',
17577                                 html : '&nbsp;&nbsp;&nbsp'
17578                             }
17579
17580                         ]
17581                     };
17582
17583                     if (i == 0) {
17584                         cfg.cls += ' fc-event-start';
17585                     }
17586                     if ((i+1) == rows.length) {
17587                         cfg.cls += ' fc-event-end';
17588                     }
17589
17590                     var ctr = _this.el.select('.fc-event-container',true).first();
17591                     var cg = ctr.createChild(cfg);
17592
17593                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17594                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17595
17596                     var r = (c.more.length) ? 1 : 0;
17597                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17598                     cg.setWidth(ebox.right - sbox.x -2);
17599
17600                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17601                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17602                     cg.on('click', _this.onEventClick, _this, ev);
17603
17604                     ev.els.push(cg);
17605                     
17606                 }
17607                 
17608             }
17609             
17610             
17611             if(c.more.length){
17612                 var  cfg = {
17613                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17614                     style : 'position: absolute',
17615                     unselectable : "on",
17616                     cn : [
17617                         {
17618                             cls: 'fc-event-inner',
17619                             cn : [
17620                                 {
17621                                   tag:'span',
17622                                   cls: 'fc-event-title',
17623                                   html : 'More'
17624                                 }
17625
17626
17627                             ]
17628                         },
17629                         {
17630                             cls: 'ui-resizable-handle ui-resizable-e',
17631                             html : '&nbsp;&nbsp;&nbsp'
17632                         }
17633
17634                     ]
17635                 };
17636
17637                 var ctr = _this.el.select('.fc-event-container',true).first();
17638                 var cg = ctr.createChild(cfg);
17639
17640                 var sbox = c.select('.fc-day-content',true).first().getBox();
17641                 var ebox = c.select('.fc-day-content',true).first().getBox();
17642                 //Roo.log(cg);
17643                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17644                 cg.setWidth(ebox.right - sbox.x -2);
17645
17646                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17647                 
17648             }
17649             
17650         });
17651         
17652         
17653         
17654     },
17655     
17656     onEventEnter: function (e, el,event,d) {
17657         this.fireEvent('evententer', this, el, event);
17658     },
17659     
17660     onEventLeave: function (e, el,event,d) {
17661         this.fireEvent('eventleave', this, el, event);
17662     },
17663     
17664     onEventClick: function (e, el,event,d) {
17665         this.fireEvent('eventclick', this, el, event);
17666     },
17667     
17668     onMonthChange: function () {
17669         this.store.load();
17670     },
17671     
17672     onMoreEventClick: function(e, el, more)
17673     {
17674         var _this = this;
17675         
17676         this.calpopover.placement = 'right';
17677         this.calpopover.setTitle('More');
17678         
17679         this.calpopover.setContent('');
17680         
17681         var ctr = this.calpopover.el.select('.popover-content', true).first();
17682         
17683         Roo.each(more, function(m){
17684             var cfg = {
17685                 cls : 'fc-event-hori fc-event-draggable',
17686                 html : m.title
17687             };
17688             var cg = ctr.createChild(cfg);
17689             
17690             cg.on('click', _this.onEventClick, _this, m);
17691         });
17692         
17693         this.calpopover.show(el);
17694         
17695         
17696     },
17697     
17698     onLoad: function () 
17699     {   
17700         this.calevents = [];
17701         var cal = this;
17702         
17703         if(this.store.getCount() > 0){
17704             this.store.data.each(function(d){
17705                cal.addItem({
17706                     id : d.data.id,
17707                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17708                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17709                     time : d.data.start_time,
17710                     title : d.data.title,
17711                     description : d.data.description,
17712                     venue : d.data.venue
17713                 });
17714             });
17715         }
17716         
17717         this.renderEvents();
17718         
17719         if(this.calevents.length && this.loadMask){
17720             this.maskEl.hide();
17721         }
17722     },
17723     
17724     onBeforeLoad: function()
17725     {
17726         this.clearEvents();
17727         if(this.loadMask){
17728             this.maskEl.show();
17729         }
17730     }
17731 });
17732
17733  
17734  /*
17735  * - LGPL
17736  *
17737  * element
17738  * 
17739  */
17740
17741 /**
17742  * @class Roo.bootstrap.Popover
17743  * @extends Roo.bootstrap.Component
17744  * Bootstrap Popover class
17745  * @cfg {String} html contents of the popover   (or false to use children..)
17746  * @cfg {String} title of popover (or false to hide)
17747  * @cfg {String} placement how it is placed
17748  * @cfg {String} trigger click || hover (or false to trigger manually)
17749  * @cfg {String} over what (parent or false to trigger manually.)
17750  * @cfg {Number} delay - delay before showing
17751  
17752  * @constructor
17753  * Create a new Popover
17754  * @param {Object} config The config object
17755  */
17756
17757 Roo.bootstrap.Popover = function(config){
17758     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17759     
17760     this.addEvents({
17761         // raw events
17762          /**
17763          * @event show
17764          * After the popover show
17765          * 
17766          * @param {Roo.bootstrap.Popover} this
17767          */
17768         "show" : true,
17769         /**
17770          * @event hide
17771          * After the popover hide
17772          * 
17773          * @param {Roo.bootstrap.Popover} this
17774          */
17775         "hide" : true
17776     });
17777 };
17778
17779 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17780     
17781     title: 'Fill in a title',
17782     html: false,
17783     
17784     placement : 'right',
17785     trigger : 'hover', // hover
17786     
17787     delay : 0,
17788     
17789     over: 'parent',
17790     
17791     can_build_overlaid : false,
17792     
17793     getChildContainer : function()
17794     {
17795         return this.el.select('.popover-content',true).first();
17796     },
17797     
17798     getAutoCreate : function(){
17799          
17800         var cfg = {
17801            cls : 'popover roo-dynamic',
17802            style: 'display:block',
17803            cn : [
17804                 {
17805                     cls : 'arrow'
17806                 },
17807                 {
17808                     cls : 'popover-inner',
17809                     cn : [
17810                         {
17811                             tag: 'h3',
17812                             cls: 'popover-title popover-header',
17813                             html : this.title
17814                         },
17815                         {
17816                             cls : 'popover-content popover-body',
17817                             html : this.html
17818                         }
17819                     ]
17820                     
17821                 }
17822            ]
17823         };
17824         
17825         return cfg;
17826     },
17827     setTitle: function(str)
17828     {
17829         this.title = str;
17830         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17831     },
17832     setContent: function(str)
17833     {
17834         this.html = str;
17835         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17836     },
17837     // as it get's added to the bottom of the page.
17838     onRender : function(ct, position)
17839     {
17840         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17841         if(!this.el){
17842             var cfg = Roo.apply({},  this.getAutoCreate());
17843             cfg.id = Roo.id();
17844             
17845             if (this.cls) {
17846                 cfg.cls += ' ' + this.cls;
17847             }
17848             if (this.style) {
17849                 cfg.style = this.style;
17850             }
17851             //Roo.log("adding to ");
17852             this.el = Roo.get(document.body).createChild(cfg, position);
17853 //            Roo.log(this.el);
17854         }
17855         this.initEvents();
17856     },
17857     
17858     initEvents : function()
17859     {
17860         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17861         this.el.enableDisplayMode('block');
17862         this.el.hide();
17863         if (this.over === false) {
17864             return; 
17865         }
17866         if (this.triggers === false) {
17867             return;
17868         }
17869         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17870         var triggers = this.trigger ? this.trigger.split(' ') : [];
17871         Roo.each(triggers, function(trigger) {
17872         
17873             if (trigger == 'click') {
17874                 on_el.on('click', this.toggle, this);
17875             } else if (trigger != 'manual') {
17876                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17877                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17878       
17879                 on_el.on(eventIn  ,this.enter, this);
17880                 on_el.on(eventOut, this.leave, this);
17881             }
17882         }, this);
17883         
17884     },
17885     
17886     
17887     // private
17888     timeout : null,
17889     hoverState : null,
17890     
17891     toggle : function () {
17892         this.hoverState == 'in' ? this.leave() : this.enter();
17893     },
17894     
17895     enter : function () {
17896         
17897         clearTimeout(this.timeout);
17898     
17899         this.hoverState = 'in';
17900     
17901         if (!this.delay || !this.delay.show) {
17902             this.show();
17903             return;
17904         }
17905         var _t = this;
17906         this.timeout = setTimeout(function () {
17907             if (_t.hoverState == 'in') {
17908                 _t.show();
17909             }
17910         }, this.delay.show)
17911     },
17912     
17913     leave : function() {
17914         clearTimeout(this.timeout);
17915     
17916         this.hoverState = 'out';
17917     
17918         if (!this.delay || !this.delay.hide) {
17919             this.hide();
17920             return;
17921         }
17922         var _t = this;
17923         this.timeout = setTimeout(function () {
17924             if (_t.hoverState == 'out') {
17925                 _t.hide();
17926             }
17927         }, this.delay.hide)
17928     },
17929     
17930     show : function (on_el)
17931     {
17932         if (!on_el) {
17933             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17934         }
17935         
17936         // set content.
17937         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17938         if (this.html !== false) {
17939             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17940         }
17941         this.el.removeClass([
17942             'fade','top','bottom', 'left', 'right','in',
17943             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17944         ]);
17945         if (!this.title.length) {
17946             this.el.select('.popover-title',true).hide();
17947         }
17948         
17949         var placement = typeof this.placement == 'function' ?
17950             this.placement.call(this, this.el, on_el) :
17951             this.placement;
17952             
17953         var autoToken = /\s?auto?\s?/i;
17954         var autoPlace = autoToken.test(placement);
17955         if (autoPlace) {
17956             placement = placement.replace(autoToken, '') || 'top';
17957         }
17958         
17959         //this.el.detach()
17960         //this.el.setXY([0,0]);
17961         this.el.show();
17962         this.el.dom.style.display='block';
17963         this.el.addClass(placement);
17964         
17965         //this.el.appendTo(on_el);
17966         
17967         var p = this.getPosition();
17968         var box = this.el.getBox();
17969         
17970         if (autoPlace) {
17971             // fixme..
17972         }
17973         var align = Roo.bootstrap.Popover.alignment[placement];
17974         
17975 //        Roo.log(align);
17976         this.el.alignTo(on_el, align[0],align[1]);
17977         //var arrow = this.el.select('.arrow',true).first();
17978         //arrow.set(align[2], 
17979         
17980         this.el.addClass('in');
17981         
17982         
17983         if (this.el.hasClass('fade')) {
17984             // fade it?
17985         }
17986         
17987         this.hoverState = 'in';
17988         
17989         this.fireEvent('show', this);
17990         
17991     },
17992     hide : function()
17993     {
17994         this.el.setXY([0,0]);
17995         this.el.removeClass('in');
17996         this.el.hide();
17997         this.hoverState = null;
17998         
17999         this.fireEvent('hide', this);
18000     }
18001     
18002 });
18003
18004 Roo.bootstrap.Popover.alignment = {
18005     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18006     'right' : ['l-r', [10,0], 'left bs-popover-left'],
18007     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18008     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18009 };
18010
18011  /*
18012  * - LGPL
18013  *
18014  * Progress
18015  * 
18016  */
18017
18018 /**
18019  * @class Roo.bootstrap.Progress
18020  * @extends Roo.bootstrap.Component
18021  * Bootstrap Progress class
18022  * @cfg {Boolean} striped striped of the progress bar
18023  * @cfg {Boolean} active animated of the progress bar
18024  * 
18025  * 
18026  * @constructor
18027  * Create a new Progress
18028  * @param {Object} config The config object
18029  */
18030
18031 Roo.bootstrap.Progress = function(config){
18032     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18033 };
18034
18035 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
18036     
18037     striped : false,
18038     active: false,
18039     
18040     getAutoCreate : function(){
18041         var cfg = {
18042             tag: 'div',
18043             cls: 'progress'
18044         };
18045         
18046         
18047         if(this.striped){
18048             cfg.cls += ' progress-striped';
18049         }
18050       
18051         if(this.active){
18052             cfg.cls += ' active';
18053         }
18054         
18055         
18056         return cfg;
18057     }
18058    
18059 });
18060
18061  
18062
18063  /*
18064  * - LGPL
18065  *
18066  * ProgressBar
18067  * 
18068  */
18069
18070 /**
18071  * @class Roo.bootstrap.ProgressBar
18072  * @extends Roo.bootstrap.Component
18073  * Bootstrap ProgressBar class
18074  * @cfg {Number} aria_valuenow aria-value now
18075  * @cfg {Number} aria_valuemin aria-value min
18076  * @cfg {Number} aria_valuemax aria-value max
18077  * @cfg {String} label label for the progress bar
18078  * @cfg {String} panel (success | info | warning | danger )
18079  * @cfg {String} role role of the progress bar
18080  * @cfg {String} sr_only text
18081  * 
18082  * 
18083  * @constructor
18084  * Create a new ProgressBar
18085  * @param {Object} config The config object
18086  */
18087
18088 Roo.bootstrap.ProgressBar = function(config){
18089     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18090 };
18091
18092 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18093     
18094     aria_valuenow : 0,
18095     aria_valuemin : 0,
18096     aria_valuemax : 100,
18097     label : false,
18098     panel : false,
18099     role : false,
18100     sr_only: false,
18101     
18102     getAutoCreate : function()
18103     {
18104         
18105         var cfg = {
18106             tag: 'div',
18107             cls: 'progress-bar',
18108             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18109         };
18110         
18111         if(this.sr_only){
18112             cfg.cn = {
18113                 tag: 'span',
18114                 cls: 'sr-only',
18115                 html: this.sr_only
18116             }
18117         }
18118         
18119         if(this.role){
18120             cfg.role = this.role;
18121         }
18122         
18123         if(this.aria_valuenow){
18124             cfg['aria-valuenow'] = this.aria_valuenow;
18125         }
18126         
18127         if(this.aria_valuemin){
18128             cfg['aria-valuemin'] = this.aria_valuemin;
18129         }
18130         
18131         if(this.aria_valuemax){
18132             cfg['aria-valuemax'] = this.aria_valuemax;
18133         }
18134         
18135         if(this.label && !this.sr_only){
18136             cfg.html = this.label;
18137         }
18138         
18139         if(this.panel){
18140             cfg.cls += ' progress-bar-' + this.panel;
18141         }
18142         
18143         return cfg;
18144     },
18145     
18146     update : function(aria_valuenow)
18147     {
18148         this.aria_valuenow = aria_valuenow;
18149         
18150         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18151     }
18152    
18153 });
18154
18155  
18156
18157  /*
18158  * - LGPL
18159  *
18160  * column
18161  * 
18162  */
18163
18164 /**
18165  * @class Roo.bootstrap.TabGroup
18166  * @extends Roo.bootstrap.Column
18167  * Bootstrap Column class
18168  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18169  * @cfg {Boolean} carousel true to make the group behave like a carousel
18170  * @cfg {Boolean} bullets show bullets for the panels
18171  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18172  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18173  * @cfg {Boolean} showarrow (true|false) show arrow default true
18174  * 
18175  * @constructor
18176  * Create a new TabGroup
18177  * @param {Object} config The config object
18178  */
18179
18180 Roo.bootstrap.TabGroup = function(config){
18181     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18182     if (!this.navId) {
18183         this.navId = Roo.id();
18184     }
18185     this.tabs = [];
18186     Roo.bootstrap.TabGroup.register(this);
18187     
18188 };
18189
18190 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18191     
18192     carousel : false,
18193     transition : false,
18194     bullets : 0,
18195     timer : 0,
18196     autoslide : false,
18197     slideFn : false,
18198     slideOnTouch : false,
18199     showarrow : true,
18200     
18201     getAutoCreate : function()
18202     {
18203         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18204         
18205         cfg.cls += ' tab-content';
18206         
18207         if (this.carousel) {
18208             cfg.cls += ' carousel slide';
18209             
18210             cfg.cn = [{
18211                cls : 'carousel-inner',
18212                cn : []
18213             }];
18214         
18215             if(this.bullets  && !Roo.isTouch){
18216                 
18217                 var bullets = {
18218                     cls : 'carousel-bullets',
18219                     cn : []
18220                 };
18221                
18222                 if(this.bullets_cls){
18223                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18224                 }
18225                 
18226                 bullets.cn.push({
18227                     cls : 'clear'
18228                 });
18229                 
18230                 cfg.cn[0].cn.push(bullets);
18231             }
18232             
18233             if(this.showarrow){
18234                 cfg.cn[0].cn.push({
18235                     tag : 'div',
18236                     class : 'carousel-arrow',
18237                     cn : [
18238                         {
18239                             tag : 'div',
18240                             class : 'carousel-prev',
18241                             cn : [
18242                                 {
18243                                     tag : 'i',
18244                                     class : 'fa fa-chevron-left'
18245                                 }
18246                             ]
18247                         },
18248                         {
18249                             tag : 'div',
18250                             class : 'carousel-next',
18251                             cn : [
18252                                 {
18253                                     tag : 'i',
18254                                     class : 'fa fa-chevron-right'
18255                                 }
18256                             ]
18257                         }
18258                     ]
18259                 });
18260             }
18261             
18262         }
18263         
18264         return cfg;
18265     },
18266     
18267     initEvents:  function()
18268     {
18269 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18270 //            this.el.on("touchstart", this.onTouchStart, this);
18271 //        }
18272         
18273         if(this.autoslide){
18274             var _this = this;
18275             
18276             this.slideFn = window.setInterval(function() {
18277                 _this.showPanelNext();
18278             }, this.timer);
18279         }
18280         
18281         if(this.showarrow){
18282             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18283             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18284         }
18285         
18286         
18287     },
18288     
18289 //    onTouchStart : function(e, el, o)
18290 //    {
18291 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18292 //            return;
18293 //        }
18294 //        
18295 //        this.showPanelNext();
18296 //    },
18297     
18298     
18299     getChildContainer : function()
18300     {
18301         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18302     },
18303     
18304     /**
18305     * register a Navigation item
18306     * @param {Roo.bootstrap.NavItem} the navitem to add
18307     */
18308     register : function(item)
18309     {
18310         this.tabs.push( item);
18311         item.navId = this.navId; // not really needed..
18312         this.addBullet();
18313     
18314     },
18315     
18316     getActivePanel : function()
18317     {
18318         var r = false;
18319         Roo.each(this.tabs, function(t) {
18320             if (t.active) {
18321                 r = t;
18322                 return false;
18323             }
18324             return null;
18325         });
18326         return r;
18327         
18328     },
18329     getPanelByName : function(n)
18330     {
18331         var r = false;
18332         Roo.each(this.tabs, function(t) {
18333             if (t.tabId == n) {
18334                 r = t;
18335                 return false;
18336             }
18337             return null;
18338         });
18339         return r;
18340     },
18341     indexOfPanel : function(p)
18342     {
18343         var r = false;
18344         Roo.each(this.tabs, function(t,i) {
18345             if (t.tabId == p.tabId) {
18346                 r = i;
18347                 return false;
18348             }
18349             return null;
18350         });
18351         return r;
18352     },
18353     /**
18354      * show a specific panel
18355      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18356      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18357      */
18358     showPanel : function (pan)
18359     {
18360         if(this.transition || typeof(pan) == 'undefined'){
18361             Roo.log("waiting for the transitionend");
18362             return false;
18363         }
18364         
18365         if (typeof(pan) == 'number') {
18366             pan = this.tabs[pan];
18367         }
18368         
18369         if (typeof(pan) == 'string') {
18370             pan = this.getPanelByName(pan);
18371         }
18372         
18373         var cur = this.getActivePanel();
18374         
18375         if(!pan || !cur){
18376             Roo.log('pan or acitve pan is undefined');
18377             return false;
18378         }
18379         
18380         if (pan.tabId == this.getActivePanel().tabId) {
18381             return true;
18382         }
18383         
18384         if (false === cur.fireEvent('beforedeactivate')) {
18385             return false;
18386         }
18387         
18388         if(this.bullets > 0 && !Roo.isTouch){
18389             this.setActiveBullet(this.indexOfPanel(pan));
18390         }
18391         
18392         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18393             
18394             //class="carousel-item carousel-item-next carousel-item-left"
18395             
18396             this.transition = true;
18397             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18398             var lr = dir == 'next' ? 'left' : 'right';
18399             pan.el.addClass(dir); // or prev
18400             pan.el.addClass('carousel-item-' + dir); // or prev
18401             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18402             cur.el.addClass(lr); // or right
18403             pan.el.addClass(lr);
18404             cur.el.addClass('carousel-item-' +lr); // or right
18405             pan.el.addClass('carousel-item-' +lr);
18406             
18407             
18408             var _this = this;
18409             cur.el.on('transitionend', function() {
18410                 Roo.log("trans end?");
18411                 
18412                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18413                 pan.setActive(true);
18414                 
18415                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18416                 cur.setActive(false);
18417                 
18418                 _this.transition = false;
18419                 
18420             }, this, { single:  true } );
18421             
18422             return true;
18423         }
18424         
18425         cur.setActive(false);
18426         pan.setActive(true);
18427         
18428         return true;
18429         
18430     },
18431     showPanelNext : function()
18432     {
18433         var i = this.indexOfPanel(this.getActivePanel());
18434         
18435         if (i >= this.tabs.length - 1 && !this.autoslide) {
18436             return;
18437         }
18438         
18439         if (i >= this.tabs.length - 1 && this.autoslide) {
18440             i = -1;
18441         }
18442         
18443         this.showPanel(this.tabs[i+1]);
18444     },
18445     
18446     showPanelPrev : function()
18447     {
18448         var i = this.indexOfPanel(this.getActivePanel());
18449         
18450         if (i  < 1 && !this.autoslide) {
18451             return;
18452         }
18453         
18454         if (i < 1 && this.autoslide) {
18455             i = this.tabs.length;
18456         }
18457         
18458         this.showPanel(this.tabs[i-1]);
18459     },
18460     
18461     
18462     addBullet: function()
18463     {
18464         if(!this.bullets || Roo.isTouch){
18465             return;
18466         }
18467         var ctr = this.el.select('.carousel-bullets',true).first();
18468         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18469         var bullet = ctr.createChild({
18470             cls : 'bullet bullet-' + i
18471         },ctr.dom.lastChild);
18472         
18473         
18474         var _this = this;
18475         
18476         bullet.on('click', (function(e, el, o, ii, t){
18477
18478             e.preventDefault();
18479
18480             this.showPanel(ii);
18481
18482             if(this.autoslide && this.slideFn){
18483                 clearInterval(this.slideFn);
18484                 this.slideFn = window.setInterval(function() {
18485                     _this.showPanelNext();
18486                 }, this.timer);
18487             }
18488
18489         }).createDelegate(this, [i, bullet], true));
18490                 
18491         
18492     },
18493      
18494     setActiveBullet : function(i)
18495     {
18496         if(Roo.isTouch){
18497             return;
18498         }
18499         
18500         Roo.each(this.el.select('.bullet', true).elements, function(el){
18501             el.removeClass('selected');
18502         });
18503
18504         var bullet = this.el.select('.bullet-' + i, true).first();
18505         
18506         if(!bullet){
18507             return;
18508         }
18509         
18510         bullet.addClass('selected');
18511     }
18512     
18513     
18514   
18515 });
18516
18517  
18518
18519  
18520  
18521 Roo.apply(Roo.bootstrap.TabGroup, {
18522     
18523     groups: {},
18524      /**
18525     * register a Navigation Group
18526     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18527     */
18528     register : function(navgrp)
18529     {
18530         this.groups[navgrp.navId] = navgrp;
18531         
18532     },
18533     /**
18534     * fetch a Navigation Group based on the navigation ID
18535     * if one does not exist , it will get created.
18536     * @param {string} the navgroup to add
18537     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18538     */
18539     get: function(navId) {
18540         if (typeof(this.groups[navId]) == 'undefined') {
18541             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18542         }
18543         return this.groups[navId] ;
18544     }
18545     
18546     
18547     
18548 });
18549
18550  /*
18551  * - LGPL
18552  *
18553  * TabPanel
18554  * 
18555  */
18556
18557 /**
18558  * @class Roo.bootstrap.TabPanel
18559  * @extends Roo.bootstrap.Component
18560  * Bootstrap TabPanel class
18561  * @cfg {Boolean} active panel active
18562  * @cfg {String} html panel content
18563  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18564  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18565  * @cfg {String} href click to link..
18566  * 
18567  * 
18568  * @constructor
18569  * Create a new TabPanel
18570  * @param {Object} config The config object
18571  */
18572
18573 Roo.bootstrap.TabPanel = function(config){
18574     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18575     this.addEvents({
18576         /**
18577              * @event changed
18578              * Fires when the active status changes
18579              * @param {Roo.bootstrap.TabPanel} this
18580              * @param {Boolean} state the new state
18581             
18582          */
18583         'changed': true,
18584         /**
18585              * @event beforedeactivate
18586              * Fires before a tab is de-activated - can be used to do validation on a form.
18587              * @param {Roo.bootstrap.TabPanel} this
18588              * @return {Boolean} false if there is an error
18589             
18590          */
18591         'beforedeactivate': true
18592      });
18593     
18594     this.tabId = this.tabId || Roo.id();
18595   
18596 };
18597
18598 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18599     
18600     active: false,
18601     html: false,
18602     tabId: false,
18603     navId : false,
18604     href : '',
18605     
18606     getAutoCreate : function(){
18607         
18608         
18609         var cfg = {
18610             tag: 'div',
18611             // item is needed for carousel - not sure if it has any effect otherwise
18612             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18613             html: this.html || ''
18614         };
18615         
18616         if(this.active){
18617             cfg.cls += ' active';
18618         }
18619         
18620         if(this.tabId){
18621             cfg.tabId = this.tabId;
18622         }
18623         
18624         
18625         
18626         return cfg;
18627     },
18628     
18629     initEvents:  function()
18630     {
18631         var p = this.parent();
18632         
18633         this.navId = this.navId || p.navId;
18634         
18635         if (typeof(this.navId) != 'undefined') {
18636             // not really needed.. but just in case.. parent should be a NavGroup.
18637             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18638             
18639             tg.register(this);
18640             
18641             var i = tg.tabs.length - 1;
18642             
18643             if(this.active && tg.bullets > 0 && i < tg.bullets){
18644                 tg.setActiveBullet(i);
18645             }
18646         }
18647         
18648         this.el.on('click', this.onClick, this);
18649         
18650         if(Roo.isTouch){
18651             this.el.on("touchstart", this.onTouchStart, this);
18652             this.el.on("touchmove", this.onTouchMove, this);
18653             this.el.on("touchend", this.onTouchEnd, this);
18654         }
18655         
18656     },
18657     
18658     onRender : function(ct, position)
18659     {
18660         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18661     },
18662     
18663     setActive : function(state)
18664     {
18665         Roo.log("panel - set active " + this.tabId + "=" + state);
18666         
18667         this.active = state;
18668         if (!state) {
18669             this.el.removeClass('active');
18670             
18671         } else  if (!this.el.hasClass('active')) {
18672             this.el.addClass('active');
18673         }
18674         
18675         this.fireEvent('changed', this, state);
18676     },
18677     
18678     onClick : function(e)
18679     {
18680         e.preventDefault();
18681         
18682         if(!this.href.length){
18683             return;
18684         }
18685         
18686         window.location.href = this.href;
18687     },
18688     
18689     startX : 0,
18690     startY : 0,
18691     endX : 0,
18692     endY : 0,
18693     swiping : false,
18694     
18695     onTouchStart : function(e)
18696     {
18697         this.swiping = false;
18698         
18699         this.startX = e.browserEvent.touches[0].clientX;
18700         this.startY = e.browserEvent.touches[0].clientY;
18701     },
18702     
18703     onTouchMove : function(e)
18704     {
18705         this.swiping = true;
18706         
18707         this.endX = e.browserEvent.touches[0].clientX;
18708         this.endY = e.browserEvent.touches[0].clientY;
18709     },
18710     
18711     onTouchEnd : function(e)
18712     {
18713         if(!this.swiping){
18714             this.onClick(e);
18715             return;
18716         }
18717         
18718         var tabGroup = this.parent();
18719         
18720         if(this.endX > this.startX){ // swiping right
18721             tabGroup.showPanelPrev();
18722             return;
18723         }
18724         
18725         if(this.startX > this.endX){ // swiping left
18726             tabGroup.showPanelNext();
18727             return;
18728         }
18729     }
18730     
18731     
18732 });
18733  
18734
18735  
18736
18737  /*
18738  * - LGPL
18739  *
18740  * DateField
18741  * 
18742  */
18743
18744 /**
18745  * @class Roo.bootstrap.DateField
18746  * @extends Roo.bootstrap.Input
18747  * Bootstrap DateField class
18748  * @cfg {Number} weekStart default 0
18749  * @cfg {String} viewMode default empty, (months|years)
18750  * @cfg {String} minViewMode default empty, (months|years)
18751  * @cfg {Number} startDate default -Infinity
18752  * @cfg {Number} endDate default Infinity
18753  * @cfg {Boolean} todayHighlight default false
18754  * @cfg {Boolean} todayBtn default false
18755  * @cfg {Boolean} calendarWeeks default false
18756  * @cfg {Object} daysOfWeekDisabled default empty
18757  * @cfg {Boolean} singleMode default false (true | false)
18758  * 
18759  * @cfg {Boolean} keyboardNavigation default true
18760  * @cfg {String} language default en
18761  * 
18762  * @constructor
18763  * Create a new DateField
18764  * @param {Object} config The config object
18765  */
18766
18767 Roo.bootstrap.DateField = function(config){
18768     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18769      this.addEvents({
18770             /**
18771              * @event show
18772              * Fires when this field show.
18773              * @param {Roo.bootstrap.DateField} this
18774              * @param {Mixed} date The date value
18775              */
18776             show : true,
18777             /**
18778              * @event show
18779              * Fires when this field hide.
18780              * @param {Roo.bootstrap.DateField} this
18781              * @param {Mixed} date The date value
18782              */
18783             hide : true,
18784             /**
18785              * @event select
18786              * Fires when select a date.
18787              * @param {Roo.bootstrap.DateField} this
18788              * @param {Mixed} date The date value
18789              */
18790             select : true,
18791             /**
18792              * @event beforeselect
18793              * Fires when before select a date.
18794              * @param {Roo.bootstrap.DateField} this
18795              * @param {Mixed} date The date value
18796              */
18797             beforeselect : true
18798         });
18799 };
18800
18801 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18802     
18803     /**
18804      * @cfg {String} format
18805      * The default date format string which can be overriden for localization support.  The format must be
18806      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18807      */
18808     format : "m/d/y",
18809     /**
18810      * @cfg {String} altFormats
18811      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18812      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18813      */
18814     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18815     
18816     weekStart : 0,
18817     
18818     viewMode : '',
18819     
18820     minViewMode : '',
18821     
18822     todayHighlight : false,
18823     
18824     todayBtn: false,
18825     
18826     language: 'en',
18827     
18828     keyboardNavigation: true,
18829     
18830     calendarWeeks: false,
18831     
18832     startDate: -Infinity,
18833     
18834     endDate: Infinity,
18835     
18836     daysOfWeekDisabled: [],
18837     
18838     _events: [],
18839     
18840     singleMode : false,
18841     
18842     UTCDate: function()
18843     {
18844         return new Date(Date.UTC.apply(Date, arguments));
18845     },
18846     
18847     UTCToday: function()
18848     {
18849         var today = new Date();
18850         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18851     },
18852     
18853     getDate: function() {
18854             var d = this.getUTCDate();
18855             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18856     },
18857     
18858     getUTCDate: function() {
18859             return this.date;
18860     },
18861     
18862     setDate: function(d) {
18863             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18864     },
18865     
18866     setUTCDate: function(d) {
18867             this.date = d;
18868             this.setValue(this.formatDate(this.date));
18869     },
18870         
18871     onRender: function(ct, position)
18872     {
18873         
18874         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18875         
18876         this.language = this.language || 'en';
18877         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18878         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18879         
18880         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18881         this.format = this.format || 'm/d/y';
18882         this.isInline = false;
18883         this.isInput = true;
18884         this.component = this.el.select('.add-on', true).first() || false;
18885         this.component = (this.component && this.component.length === 0) ? false : this.component;
18886         this.hasInput = this.component && this.inputEl().length;
18887         
18888         if (typeof(this.minViewMode === 'string')) {
18889             switch (this.minViewMode) {
18890                 case 'months':
18891                     this.minViewMode = 1;
18892                     break;
18893                 case 'years':
18894                     this.minViewMode = 2;
18895                     break;
18896                 default:
18897                     this.minViewMode = 0;
18898                     break;
18899             }
18900         }
18901         
18902         if (typeof(this.viewMode === 'string')) {
18903             switch (this.viewMode) {
18904                 case 'months':
18905                     this.viewMode = 1;
18906                     break;
18907                 case 'years':
18908                     this.viewMode = 2;
18909                     break;
18910                 default:
18911                     this.viewMode = 0;
18912                     break;
18913             }
18914         }
18915                 
18916         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18917         
18918 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18919         
18920         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18921         
18922         this.picker().on('mousedown', this.onMousedown, this);
18923         this.picker().on('click', this.onClick, this);
18924         
18925         this.picker().addClass('datepicker-dropdown');
18926         
18927         this.startViewMode = this.viewMode;
18928         
18929         if(this.singleMode){
18930             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18931                 v.setVisibilityMode(Roo.Element.DISPLAY);
18932                 v.hide();
18933             });
18934             
18935             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18936                 v.setStyle('width', '189px');
18937             });
18938         }
18939         
18940         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18941             if(!this.calendarWeeks){
18942                 v.remove();
18943                 return;
18944             }
18945             
18946             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18947             v.attr('colspan', function(i, val){
18948                 return parseInt(val) + 1;
18949             });
18950         });
18951                         
18952         
18953         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18954         
18955         this.setStartDate(this.startDate);
18956         this.setEndDate(this.endDate);
18957         
18958         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18959         
18960         this.fillDow();
18961         this.fillMonths();
18962         this.update();
18963         this.showMode();
18964         
18965         if(this.isInline) {
18966             this.showPopup();
18967         }
18968     },
18969     
18970     picker : function()
18971     {
18972         return this.pickerEl;
18973 //        return this.el.select('.datepicker', true).first();
18974     },
18975     
18976     fillDow: function()
18977     {
18978         var dowCnt = this.weekStart;
18979         
18980         var dow = {
18981             tag: 'tr',
18982             cn: [
18983                 
18984             ]
18985         };
18986         
18987         if(this.calendarWeeks){
18988             dow.cn.push({
18989                 tag: 'th',
18990                 cls: 'cw',
18991                 html: '&nbsp;'
18992             })
18993         }
18994         
18995         while (dowCnt < this.weekStart + 7) {
18996             dow.cn.push({
18997                 tag: 'th',
18998                 cls: 'dow',
18999                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19000             });
19001         }
19002         
19003         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19004     },
19005     
19006     fillMonths: function()
19007     {    
19008         var i = 0;
19009         var months = this.picker().select('>.datepicker-months td', true).first();
19010         
19011         months.dom.innerHTML = '';
19012         
19013         while (i < 12) {
19014             var month = {
19015                 tag: 'span',
19016                 cls: 'month',
19017                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19018             };
19019             
19020             months.createChild(month);
19021         }
19022         
19023     },
19024     
19025     update: function()
19026     {
19027         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;
19028         
19029         if (this.date < this.startDate) {
19030             this.viewDate = new Date(this.startDate);
19031         } else if (this.date > this.endDate) {
19032             this.viewDate = new Date(this.endDate);
19033         } else {
19034             this.viewDate = new Date(this.date);
19035         }
19036         
19037         this.fill();
19038     },
19039     
19040     fill: function() 
19041     {
19042         var d = new Date(this.viewDate),
19043                 year = d.getUTCFullYear(),
19044                 month = d.getUTCMonth(),
19045                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19046                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19047                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19048                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19049                 currentDate = this.date && this.date.valueOf(),
19050                 today = this.UTCToday();
19051         
19052         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19053         
19054 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19055         
19056 //        this.picker.select('>tfoot th.today').
19057 //                                              .text(dates[this.language].today)
19058 //                                              .toggle(this.todayBtn !== false);
19059     
19060         this.updateNavArrows();
19061         this.fillMonths();
19062                                                 
19063         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19064         
19065         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19066          
19067         prevMonth.setUTCDate(day);
19068         
19069         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19070         
19071         var nextMonth = new Date(prevMonth);
19072         
19073         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19074         
19075         nextMonth = nextMonth.valueOf();
19076         
19077         var fillMonths = false;
19078         
19079         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19080         
19081         while(prevMonth.valueOf() <= nextMonth) {
19082             var clsName = '';
19083             
19084             if (prevMonth.getUTCDay() === this.weekStart) {
19085                 if(fillMonths){
19086                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19087                 }
19088                     
19089                 fillMonths = {
19090                     tag: 'tr',
19091                     cn: []
19092                 };
19093                 
19094                 if(this.calendarWeeks){
19095                     // ISO 8601: First week contains first thursday.
19096                     // ISO also states week starts on Monday, but we can be more abstract here.
19097                     var
19098                     // Start of current week: based on weekstart/current date
19099                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19100                     // Thursday of this week
19101                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19102                     // First Thursday of year, year from thursday
19103                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19104                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19105                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19106                     
19107                     fillMonths.cn.push({
19108                         tag: 'td',
19109                         cls: 'cw',
19110                         html: calWeek
19111                     });
19112                 }
19113             }
19114             
19115             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19116                 clsName += ' old';
19117             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19118                 clsName += ' new';
19119             }
19120             if (this.todayHighlight &&
19121                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19122                 prevMonth.getUTCMonth() == today.getMonth() &&
19123                 prevMonth.getUTCDate() == today.getDate()) {
19124                 clsName += ' today';
19125             }
19126             
19127             if (currentDate && prevMonth.valueOf() === currentDate) {
19128                 clsName += ' active';
19129             }
19130             
19131             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19132                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19133                     clsName += ' disabled';
19134             }
19135             
19136             fillMonths.cn.push({
19137                 tag: 'td',
19138                 cls: 'day ' + clsName,
19139                 html: prevMonth.getDate()
19140             });
19141             
19142             prevMonth.setDate(prevMonth.getDate()+1);
19143         }
19144           
19145         var currentYear = this.date && this.date.getUTCFullYear();
19146         var currentMonth = this.date && this.date.getUTCMonth();
19147         
19148         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19149         
19150         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19151             v.removeClass('active');
19152             
19153             if(currentYear === year && k === currentMonth){
19154                 v.addClass('active');
19155             }
19156             
19157             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19158                 v.addClass('disabled');
19159             }
19160             
19161         });
19162         
19163         
19164         year = parseInt(year/10, 10) * 10;
19165         
19166         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19167         
19168         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19169         
19170         year -= 1;
19171         for (var i = -1; i < 11; i++) {
19172             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19173                 tag: 'span',
19174                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19175                 html: year
19176             });
19177             
19178             year += 1;
19179         }
19180     },
19181     
19182     showMode: function(dir) 
19183     {
19184         if (dir) {
19185             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19186         }
19187         
19188         Roo.each(this.picker().select('>div',true).elements, function(v){
19189             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19190             v.hide();
19191         });
19192         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19193     },
19194     
19195     place: function()
19196     {
19197         if(this.isInline) {
19198             return;
19199         }
19200         
19201         this.picker().removeClass(['bottom', 'top']);
19202         
19203         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19204             /*
19205              * place to the top of element!
19206              *
19207              */
19208             
19209             this.picker().addClass('top');
19210             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19211             
19212             return;
19213         }
19214         
19215         this.picker().addClass('bottom');
19216         
19217         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19218     },
19219     
19220     parseDate : function(value)
19221     {
19222         if(!value || value instanceof Date){
19223             return value;
19224         }
19225         var v = Date.parseDate(value, this.format);
19226         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19227             v = Date.parseDate(value, 'Y-m-d');
19228         }
19229         if(!v && this.altFormats){
19230             if(!this.altFormatsArray){
19231                 this.altFormatsArray = this.altFormats.split("|");
19232             }
19233             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19234                 v = Date.parseDate(value, this.altFormatsArray[i]);
19235             }
19236         }
19237         return v;
19238     },
19239     
19240     formatDate : function(date, fmt)
19241     {   
19242         return (!date || !(date instanceof Date)) ?
19243         date : date.dateFormat(fmt || this.format);
19244     },
19245     
19246     onFocus : function()
19247     {
19248         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19249         this.showPopup();
19250     },
19251     
19252     onBlur : function()
19253     {
19254         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19255         
19256         var d = this.inputEl().getValue();
19257         
19258         this.setValue(d);
19259                 
19260         this.hidePopup();
19261     },
19262     
19263     showPopup : function()
19264     {
19265         this.picker().show();
19266         this.update();
19267         this.place();
19268         
19269         this.fireEvent('showpopup', this, this.date);
19270     },
19271     
19272     hidePopup : function()
19273     {
19274         if(this.isInline) {
19275             return;
19276         }
19277         this.picker().hide();
19278         this.viewMode = this.startViewMode;
19279         this.showMode();
19280         
19281         this.fireEvent('hidepopup', this, this.date);
19282         
19283     },
19284     
19285     onMousedown: function(e)
19286     {
19287         e.stopPropagation();
19288         e.preventDefault();
19289     },
19290     
19291     keyup: function(e)
19292     {
19293         Roo.bootstrap.DateField.superclass.keyup.call(this);
19294         this.update();
19295     },
19296
19297     setValue: function(v)
19298     {
19299         if(this.fireEvent('beforeselect', this, v) !== false){
19300             var d = new Date(this.parseDate(v) ).clearTime();
19301         
19302             if(isNaN(d.getTime())){
19303                 this.date = this.viewDate = '';
19304                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19305                 return;
19306             }
19307
19308             v = this.formatDate(d);
19309
19310             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19311
19312             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19313
19314             this.update();
19315
19316             this.fireEvent('select', this, this.date);
19317         }
19318     },
19319     
19320     getValue: function()
19321     {
19322         return this.formatDate(this.date);
19323     },
19324     
19325     fireKey: function(e)
19326     {
19327         if (!this.picker().isVisible()){
19328             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19329                 this.showPopup();
19330             }
19331             return;
19332         }
19333         
19334         var dateChanged = false,
19335         dir, day, month,
19336         newDate, newViewDate;
19337         
19338         switch(e.keyCode){
19339             case 27: // escape
19340                 this.hidePopup();
19341                 e.preventDefault();
19342                 break;
19343             case 37: // left
19344             case 39: // right
19345                 if (!this.keyboardNavigation) {
19346                     break;
19347                 }
19348                 dir = e.keyCode == 37 ? -1 : 1;
19349                 
19350                 if (e.ctrlKey){
19351                     newDate = this.moveYear(this.date, dir);
19352                     newViewDate = this.moveYear(this.viewDate, dir);
19353                 } else if (e.shiftKey){
19354                     newDate = this.moveMonth(this.date, dir);
19355                     newViewDate = this.moveMonth(this.viewDate, dir);
19356                 } else {
19357                     newDate = new Date(this.date);
19358                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19359                     newViewDate = new Date(this.viewDate);
19360                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19361                 }
19362                 if (this.dateWithinRange(newDate)){
19363                     this.date = newDate;
19364                     this.viewDate = newViewDate;
19365                     this.setValue(this.formatDate(this.date));
19366 //                    this.update();
19367                     e.preventDefault();
19368                     dateChanged = true;
19369                 }
19370                 break;
19371             case 38: // up
19372             case 40: // down
19373                 if (!this.keyboardNavigation) {
19374                     break;
19375                 }
19376                 dir = e.keyCode == 38 ? -1 : 1;
19377                 if (e.ctrlKey){
19378                     newDate = this.moveYear(this.date, dir);
19379                     newViewDate = this.moveYear(this.viewDate, dir);
19380                 } else if (e.shiftKey){
19381                     newDate = this.moveMonth(this.date, dir);
19382                     newViewDate = this.moveMonth(this.viewDate, dir);
19383                 } else {
19384                     newDate = new Date(this.date);
19385                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19386                     newViewDate = new Date(this.viewDate);
19387                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19388                 }
19389                 if (this.dateWithinRange(newDate)){
19390                     this.date = newDate;
19391                     this.viewDate = newViewDate;
19392                     this.setValue(this.formatDate(this.date));
19393 //                    this.update();
19394                     e.preventDefault();
19395                     dateChanged = true;
19396                 }
19397                 break;
19398             case 13: // enter
19399                 this.setValue(this.formatDate(this.date));
19400                 this.hidePopup();
19401                 e.preventDefault();
19402                 break;
19403             case 9: // tab
19404                 this.setValue(this.formatDate(this.date));
19405                 this.hidePopup();
19406                 break;
19407             case 16: // shift
19408             case 17: // ctrl
19409             case 18: // alt
19410                 break;
19411             default :
19412                 this.hidePopup();
19413                 
19414         }
19415     },
19416     
19417     
19418     onClick: function(e) 
19419     {
19420         e.stopPropagation();
19421         e.preventDefault();
19422         
19423         var target = e.getTarget();
19424         
19425         if(target.nodeName.toLowerCase() === 'i'){
19426             target = Roo.get(target).dom.parentNode;
19427         }
19428         
19429         var nodeName = target.nodeName;
19430         var className = target.className;
19431         var html = target.innerHTML;
19432         //Roo.log(nodeName);
19433         
19434         switch(nodeName.toLowerCase()) {
19435             case 'th':
19436                 switch(className) {
19437                     case 'switch':
19438                         this.showMode(1);
19439                         break;
19440                     case 'prev':
19441                     case 'next':
19442                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19443                         switch(this.viewMode){
19444                                 case 0:
19445                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19446                                         break;
19447                                 case 1:
19448                                 case 2:
19449                                         this.viewDate = this.moveYear(this.viewDate, dir);
19450                                         break;
19451                         }
19452                         this.fill();
19453                         break;
19454                     case 'today':
19455                         var date = new Date();
19456                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19457 //                        this.fill()
19458                         this.setValue(this.formatDate(this.date));
19459                         
19460                         this.hidePopup();
19461                         break;
19462                 }
19463                 break;
19464             case 'span':
19465                 if (className.indexOf('disabled') < 0) {
19466                     this.viewDate.setUTCDate(1);
19467                     if (className.indexOf('month') > -1) {
19468                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19469                     } else {
19470                         var year = parseInt(html, 10) || 0;
19471                         this.viewDate.setUTCFullYear(year);
19472                         
19473                     }
19474                     
19475                     if(this.singleMode){
19476                         this.setValue(this.formatDate(this.viewDate));
19477                         this.hidePopup();
19478                         return;
19479                     }
19480                     
19481                     this.showMode(-1);
19482                     this.fill();
19483                 }
19484                 break;
19485                 
19486             case 'td':
19487                 //Roo.log(className);
19488                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19489                     var day = parseInt(html, 10) || 1;
19490                     var year = this.viewDate.getUTCFullYear(),
19491                         month = this.viewDate.getUTCMonth();
19492
19493                     if (className.indexOf('old') > -1) {
19494                         if(month === 0 ){
19495                             month = 11;
19496                             year -= 1;
19497                         }else{
19498                             month -= 1;
19499                         }
19500                     } else if (className.indexOf('new') > -1) {
19501                         if (month == 11) {
19502                             month = 0;
19503                             year += 1;
19504                         } else {
19505                             month += 1;
19506                         }
19507                     }
19508                     //Roo.log([year,month,day]);
19509                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19510                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19511 //                    this.fill();
19512                     //Roo.log(this.formatDate(this.date));
19513                     this.setValue(this.formatDate(this.date));
19514                     this.hidePopup();
19515                 }
19516                 break;
19517         }
19518     },
19519     
19520     setStartDate: function(startDate)
19521     {
19522         this.startDate = startDate || -Infinity;
19523         if (this.startDate !== -Infinity) {
19524             this.startDate = this.parseDate(this.startDate);
19525         }
19526         this.update();
19527         this.updateNavArrows();
19528     },
19529
19530     setEndDate: function(endDate)
19531     {
19532         this.endDate = endDate || Infinity;
19533         if (this.endDate !== Infinity) {
19534             this.endDate = this.parseDate(this.endDate);
19535         }
19536         this.update();
19537         this.updateNavArrows();
19538     },
19539     
19540     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19541     {
19542         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19543         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19544             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19545         }
19546         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19547             return parseInt(d, 10);
19548         });
19549         this.update();
19550         this.updateNavArrows();
19551     },
19552     
19553     updateNavArrows: function() 
19554     {
19555         if(this.singleMode){
19556             return;
19557         }
19558         
19559         var d = new Date(this.viewDate),
19560         year = d.getUTCFullYear(),
19561         month = d.getUTCMonth();
19562         
19563         Roo.each(this.picker().select('.prev', true).elements, function(v){
19564             v.show();
19565             switch (this.viewMode) {
19566                 case 0:
19567
19568                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19569                         v.hide();
19570                     }
19571                     break;
19572                 case 1:
19573                 case 2:
19574                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19575                         v.hide();
19576                     }
19577                     break;
19578             }
19579         });
19580         
19581         Roo.each(this.picker().select('.next', true).elements, function(v){
19582             v.show();
19583             switch (this.viewMode) {
19584                 case 0:
19585
19586                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19587                         v.hide();
19588                     }
19589                     break;
19590                 case 1:
19591                 case 2:
19592                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19593                         v.hide();
19594                     }
19595                     break;
19596             }
19597         })
19598     },
19599     
19600     moveMonth: function(date, dir)
19601     {
19602         if (!dir) {
19603             return date;
19604         }
19605         var new_date = new Date(date.valueOf()),
19606         day = new_date.getUTCDate(),
19607         month = new_date.getUTCMonth(),
19608         mag = Math.abs(dir),
19609         new_month, test;
19610         dir = dir > 0 ? 1 : -1;
19611         if (mag == 1){
19612             test = dir == -1
19613             // If going back one month, make sure month is not current month
19614             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19615             ? function(){
19616                 return new_date.getUTCMonth() == month;
19617             }
19618             // If going forward one month, make sure month is as expected
19619             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19620             : function(){
19621                 return new_date.getUTCMonth() != new_month;
19622             };
19623             new_month = month + dir;
19624             new_date.setUTCMonth(new_month);
19625             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19626             if (new_month < 0 || new_month > 11) {
19627                 new_month = (new_month + 12) % 12;
19628             }
19629         } else {
19630             // For magnitudes >1, move one month at a time...
19631             for (var i=0; i<mag; i++) {
19632                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19633                 new_date = this.moveMonth(new_date, dir);
19634             }
19635             // ...then reset the day, keeping it in the new month
19636             new_month = new_date.getUTCMonth();
19637             new_date.setUTCDate(day);
19638             test = function(){
19639                 return new_month != new_date.getUTCMonth();
19640             };
19641         }
19642         // Common date-resetting loop -- if date is beyond end of month, make it
19643         // end of month
19644         while (test()){
19645             new_date.setUTCDate(--day);
19646             new_date.setUTCMonth(new_month);
19647         }
19648         return new_date;
19649     },
19650
19651     moveYear: function(date, dir)
19652     {
19653         return this.moveMonth(date, dir*12);
19654     },
19655
19656     dateWithinRange: function(date)
19657     {
19658         return date >= this.startDate && date <= this.endDate;
19659     },
19660
19661     
19662     remove: function() 
19663     {
19664         this.picker().remove();
19665     },
19666     
19667     validateValue : function(value)
19668     {
19669         if(this.getVisibilityEl().hasClass('hidden')){
19670             return true;
19671         }
19672         
19673         if(value.length < 1)  {
19674             if(this.allowBlank){
19675                 return true;
19676             }
19677             return false;
19678         }
19679         
19680         if(value.length < this.minLength){
19681             return false;
19682         }
19683         if(value.length > this.maxLength){
19684             return false;
19685         }
19686         if(this.vtype){
19687             var vt = Roo.form.VTypes;
19688             if(!vt[this.vtype](value, this)){
19689                 return false;
19690             }
19691         }
19692         if(typeof this.validator == "function"){
19693             var msg = this.validator(value);
19694             if(msg !== true){
19695                 return false;
19696             }
19697         }
19698         
19699         if(this.regex && !this.regex.test(value)){
19700             return false;
19701         }
19702         
19703         if(typeof(this.parseDate(value)) == 'undefined'){
19704             return false;
19705         }
19706         
19707         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19708             return false;
19709         }      
19710         
19711         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19712             return false;
19713         } 
19714         
19715         
19716         return true;
19717     },
19718     
19719     reset : function()
19720     {
19721         this.date = this.viewDate = '';
19722         
19723         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19724     }
19725    
19726 });
19727
19728 Roo.apply(Roo.bootstrap.DateField,  {
19729     
19730     head : {
19731         tag: 'thead',
19732         cn: [
19733         {
19734             tag: 'tr',
19735             cn: [
19736             {
19737                 tag: 'th',
19738                 cls: 'prev',
19739                 html: '<i class="fa fa-arrow-left"/>'
19740             },
19741             {
19742                 tag: 'th',
19743                 cls: 'switch',
19744                 colspan: '5'
19745             },
19746             {
19747                 tag: 'th',
19748                 cls: 'next',
19749                 html: '<i class="fa fa-arrow-right"/>'
19750             }
19751
19752             ]
19753         }
19754         ]
19755     },
19756     
19757     content : {
19758         tag: 'tbody',
19759         cn: [
19760         {
19761             tag: 'tr',
19762             cn: [
19763             {
19764                 tag: 'td',
19765                 colspan: '7'
19766             }
19767             ]
19768         }
19769         ]
19770     },
19771     
19772     footer : {
19773         tag: 'tfoot',
19774         cn: [
19775         {
19776             tag: 'tr',
19777             cn: [
19778             {
19779                 tag: 'th',
19780                 colspan: '7',
19781                 cls: 'today'
19782             }
19783                     
19784             ]
19785         }
19786         ]
19787     },
19788     
19789     dates:{
19790         en: {
19791             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19792             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19793             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19794             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19795             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19796             today: "Today"
19797         }
19798     },
19799     
19800     modes: [
19801     {
19802         clsName: 'days',
19803         navFnc: 'Month',
19804         navStep: 1
19805     },
19806     {
19807         clsName: 'months',
19808         navFnc: 'FullYear',
19809         navStep: 1
19810     },
19811     {
19812         clsName: 'years',
19813         navFnc: 'FullYear',
19814         navStep: 10
19815     }]
19816 });
19817
19818 Roo.apply(Roo.bootstrap.DateField,  {
19819   
19820     template : {
19821         tag: 'div',
19822         cls: 'datepicker dropdown-menu roo-dynamic',
19823         cn: [
19824         {
19825             tag: 'div',
19826             cls: 'datepicker-days',
19827             cn: [
19828             {
19829                 tag: 'table',
19830                 cls: 'table-condensed',
19831                 cn:[
19832                 Roo.bootstrap.DateField.head,
19833                 {
19834                     tag: 'tbody'
19835                 },
19836                 Roo.bootstrap.DateField.footer
19837                 ]
19838             }
19839             ]
19840         },
19841         {
19842             tag: 'div',
19843             cls: 'datepicker-months',
19844             cn: [
19845             {
19846                 tag: 'table',
19847                 cls: 'table-condensed',
19848                 cn:[
19849                 Roo.bootstrap.DateField.head,
19850                 Roo.bootstrap.DateField.content,
19851                 Roo.bootstrap.DateField.footer
19852                 ]
19853             }
19854             ]
19855         },
19856         {
19857             tag: 'div',
19858             cls: 'datepicker-years',
19859             cn: [
19860             {
19861                 tag: 'table',
19862                 cls: 'table-condensed',
19863                 cn:[
19864                 Roo.bootstrap.DateField.head,
19865                 Roo.bootstrap.DateField.content,
19866                 Roo.bootstrap.DateField.footer
19867                 ]
19868             }
19869             ]
19870         }
19871         ]
19872     }
19873 });
19874
19875  
19876
19877  /*
19878  * - LGPL
19879  *
19880  * TimeField
19881  * 
19882  */
19883
19884 /**
19885  * @class Roo.bootstrap.TimeField
19886  * @extends Roo.bootstrap.Input
19887  * Bootstrap DateField class
19888  * 
19889  * 
19890  * @constructor
19891  * Create a new TimeField
19892  * @param {Object} config The config object
19893  */
19894
19895 Roo.bootstrap.TimeField = function(config){
19896     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19897     this.addEvents({
19898             /**
19899              * @event show
19900              * Fires when this field show.
19901              * @param {Roo.bootstrap.DateField} thisthis
19902              * @param {Mixed} date The date value
19903              */
19904             show : true,
19905             /**
19906              * @event show
19907              * Fires when this field hide.
19908              * @param {Roo.bootstrap.DateField} this
19909              * @param {Mixed} date The date value
19910              */
19911             hide : true,
19912             /**
19913              * @event select
19914              * Fires when select a date.
19915              * @param {Roo.bootstrap.DateField} this
19916              * @param {Mixed} date The date value
19917              */
19918             select : true
19919         });
19920 };
19921
19922 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19923     
19924     /**
19925      * @cfg {String} format
19926      * The default time format string which can be overriden for localization support.  The format must be
19927      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19928      */
19929     format : "H:i",
19930        
19931     onRender: function(ct, position)
19932     {
19933         
19934         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19935                 
19936         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19937         
19938         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19939         
19940         this.pop = this.picker().select('>.datepicker-time',true).first();
19941         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19942         
19943         this.picker().on('mousedown', this.onMousedown, this);
19944         this.picker().on('click', this.onClick, this);
19945         
19946         this.picker().addClass('datepicker-dropdown');
19947     
19948         this.fillTime();
19949         this.update();
19950             
19951         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19952         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19953         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19954         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19955         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19956         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19957
19958     },
19959     
19960     fireKey: function(e){
19961         if (!this.picker().isVisible()){
19962             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19963                 this.show();
19964             }
19965             return;
19966         }
19967
19968         e.preventDefault();
19969         
19970         switch(e.keyCode){
19971             case 27: // escape
19972                 this.hide();
19973                 break;
19974             case 37: // left
19975             case 39: // right
19976                 this.onTogglePeriod();
19977                 break;
19978             case 38: // up
19979                 this.onIncrementMinutes();
19980                 break;
19981             case 40: // down
19982                 this.onDecrementMinutes();
19983                 break;
19984             case 13: // enter
19985             case 9: // tab
19986                 this.setTime();
19987                 break;
19988         }
19989     },
19990     
19991     onClick: function(e) {
19992         e.stopPropagation();
19993         e.preventDefault();
19994     },
19995     
19996     picker : function()
19997     {
19998         return this.el.select('.datepicker', true).first();
19999     },
20000     
20001     fillTime: function()
20002     {    
20003         var time = this.pop.select('tbody', true).first();
20004         
20005         time.dom.innerHTML = '';
20006         
20007         time.createChild({
20008             tag: 'tr',
20009             cn: [
20010                 {
20011                     tag: 'td',
20012                     cn: [
20013                         {
20014                             tag: 'a',
20015                             href: '#',
20016                             cls: 'btn',
20017                             cn: [
20018                                 {
20019                                     tag: 'span',
20020                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
20021                                 }
20022                             ]
20023                         } 
20024                     ]
20025                 },
20026                 {
20027                     tag: 'td',
20028                     cls: 'separator'
20029                 },
20030                 {
20031                     tag: 'td',
20032                     cn: [
20033                         {
20034                             tag: 'a',
20035                             href: '#',
20036                             cls: 'btn',
20037                             cn: [
20038                                 {
20039                                     tag: 'span',
20040                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
20041                                 }
20042                             ]
20043                         }
20044                     ]
20045                 },
20046                 {
20047                     tag: 'td',
20048                     cls: 'separator'
20049                 }
20050             ]
20051         });
20052         
20053         time.createChild({
20054             tag: 'tr',
20055             cn: [
20056                 {
20057                     tag: 'td',
20058                     cn: [
20059                         {
20060                             tag: 'span',
20061                             cls: 'timepicker-hour',
20062                             html: '00'
20063                         }  
20064                     ]
20065                 },
20066                 {
20067                     tag: 'td',
20068                     cls: 'separator',
20069                     html: ':'
20070                 },
20071                 {
20072                     tag: 'td',
20073                     cn: [
20074                         {
20075                             tag: 'span',
20076                             cls: 'timepicker-minute',
20077                             html: '00'
20078                         }  
20079                     ]
20080                 },
20081                 {
20082                     tag: 'td',
20083                     cls: 'separator'
20084                 },
20085                 {
20086                     tag: 'td',
20087                     cn: [
20088                         {
20089                             tag: 'button',
20090                             type: 'button',
20091                             cls: 'btn btn-primary period',
20092                             html: 'AM'
20093                             
20094                         }
20095                     ]
20096                 }
20097             ]
20098         });
20099         
20100         time.createChild({
20101             tag: 'tr',
20102             cn: [
20103                 {
20104                     tag: 'td',
20105                     cn: [
20106                         {
20107                             tag: 'a',
20108                             href: '#',
20109                             cls: 'btn',
20110                             cn: [
20111                                 {
20112                                     tag: 'span',
20113                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20114                                 }
20115                             ]
20116                         }
20117                     ]
20118                 },
20119                 {
20120                     tag: 'td',
20121                     cls: 'separator'
20122                 },
20123                 {
20124                     tag: 'td',
20125                     cn: [
20126                         {
20127                             tag: 'a',
20128                             href: '#',
20129                             cls: 'btn',
20130                             cn: [
20131                                 {
20132                                     tag: 'span',
20133                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20134                                 }
20135                             ]
20136                         }
20137                     ]
20138                 },
20139                 {
20140                     tag: 'td',
20141                     cls: 'separator'
20142                 }
20143             ]
20144         });
20145         
20146     },
20147     
20148     update: function()
20149     {
20150         
20151         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20152         
20153         this.fill();
20154     },
20155     
20156     fill: function() 
20157     {
20158         var hours = this.time.getHours();
20159         var minutes = this.time.getMinutes();
20160         var period = 'AM';
20161         
20162         if(hours > 11){
20163             period = 'PM';
20164         }
20165         
20166         if(hours == 0){
20167             hours = 12;
20168         }
20169         
20170         
20171         if(hours > 12){
20172             hours = hours - 12;
20173         }
20174         
20175         if(hours < 10){
20176             hours = '0' + hours;
20177         }
20178         
20179         if(minutes < 10){
20180             minutes = '0' + minutes;
20181         }
20182         
20183         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20184         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20185         this.pop.select('button', true).first().dom.innerHTML = period;
20186         
20187     },
20188     
20189     place: function()
20190     {   
20191         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20192         
20193         var cls = ['bottom'];
20194         
20195         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20196             cls.pop();
20197             cls.push('top');
20198         }
20199         
20200         cls.push('right');
20201         
20202         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20203             cls.pop();
20204             cls.push('left');
20205         }
20206         
20207         this.picker().addClass(cls.join('-'));
20208         
20209         var _this = this;
20210         
20211         Roo.each(cls, function(c){
20212             if(c == 'bottom'){
20213                 _this.picker().setTop(_this.inputEl().getHeight());
20214                 return;
20215             }
20216             if(c == 'top'){
20217                 _this.picker().setTop(0 - _this.picker().getHeight());
20218                 return;
20219             }
20220             
20221             if(c == 'left'){
20222                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20223                 return;
20224             }
20225             if(c == 'right'){
20226                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20227                 return;
20228             }
20229         });
20230         
20231     },
20232   
20233     onFocus : function()
20234     {
20235         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20236         this.show();
20237     },
20238     
20239     onBlur : function()
20240     {
20241         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20242         this.hide();
20243     },
20244     
20245     show : function()
20246     {
20247         this.picker().show();
20248         this.pop.show();
20249         this.update();
20250         this.place();
20251         
20252         this.fireEvent('show', this, this.date);
20253     },
20254     
20255     hide : function()
20256     {
20257         this.picker().hide();
20258         this.pop.hide();
20259         
20260         this.fireEvent('hide', this, this.date);
20261     },
20262     
20263     setTime : function()
20264     {
20265         this.hide();
20266         this.setValue(this.time.format(this.format));
20267         
20268         this.fireEvent('select', this, this.date);
20269         
20270         
20271     },
20272     
20273     onMousedown: function(e){
20274         e.stopPropagation();
20275         e.preventDefault();
20276     },
20277     
20278     onIncrementHours: function()
20279     {
20280         Roo.log('onIncrementHours');
20281         this.time = this.time.add(Date.HOUR, 1);
20282         this.update();
20283         
20284     },
20285     
20286     onDecrementHours: function()
20287     {
20288         Roo.log('onDecrementHours');
20289         this.time = this.time.add(Date.HOUR, -1);
20290         this.update();
20291     },
20292     
20293     onIncrementMinutes: function()
20294     {
20295         Roo.log('onIncrementMinutes');
20296         this.time = this.time.add(Date.MINUTE, 1);
20297         this.update();
20298     },
20299     
20300     onDecrementMinutes: function()
20301     {
20302         Roo.log('onDecrementMinutes');
20303         this.time = this.time.add(Date.MINUTE, -1);
20304         this.update();
20305     },
20306     
20307     onTogglePeriod: function()
20308     {
20309         Roo.log('onTogglePeriod');
20310         this.time = this.time.add(Date.HOUR, 12);
20311         this.update();
20312     }
20313     
20314    
20315 });
20316
20317 Roo.apply(Roo.bootstrap.TimeField,  {
20318     
20319     content : {
20320         tag: 'tbody',
20321         cn: [
20322             {
20323                 tag: 'tr',
20324                 cn: [
20325                 {
20326                     tag: 'td',
20327                     colspan: '7'
20328                 }
20329                 ]
20330             }
20331         ]
20332     },
20333     
20334     footer : {
20335         tag: 'tfoot',
20336         cn: [
20337             {
20338                 tag: 'tr',
20339                 cn: [
20340                 {
20341                     tag: 'th',
20342                     colspan: '7',
20343                     cls: '',
20344                     cn: [
20345                         {
20346                             tag: 'button',
20347                             cls: 'btn btn-info ok',
20348                             html: 'OK'
20349                         }
20350                     ]
20351                 }
20352
20353                 ]
20354             }
20355         ]
20356     }
20357 });
20358
20359 Roo.apply(Roo.bootstrap.TimeField,  {
20360   
20361     template : {
20362         tag: 'div',
20363         cls: 'datepicker dropdown-menu',
20364         cn: [
20365             {
20366                 tag: 'div',
20367                 cls: 'datepicker-time',
20368                 cn: [
20369                 {
20370                     tag: 'table',
20371                     cls: 'table-condensed',
20372                     cn:[
20373                     Roo.bootstrap.TimeField.content,
20374                     Roo.bootstrap.TimeField.footer
20375                     ]
20376                 }
20377                 ]
20378             }
20379         ]
20380     }
20381 });
20382
20383  
20384
20385  /*
20386  * - LGPL
20387  *
20388  * MonthField
20389  * 
20390  */
20391
20392 /**
20393  * @class Roo.bootstrap.MonthField
20394  * @extends Roo.bootstrap.Input
20395  * Bootstrap MonthField class
20396  * 
20397  * @cfg {String} language default en
20398  * 
20399  * @constructor
20400  * Create a new MonthField
20401  * @param {Object} config The config object
20402  */
20403
20404 Roo.bootstrap.MonthField = function(config){
20405     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20406     
20407     this.addEvents({
20408         /**
20409          * @event show
20410          * Fires when this field show.
20411          * @param {Roo.bootstrap.MonthField} this
20412          * @param {Mixed} date The date value
20413          */
20414         show : true,
20415         /**
20416          * @event show
20417          * Fires when this field hide.
20418          * @param {Roo.bootstrap.MonthField} this
20419          * @param {Mixed} date The date value
20420          */
20421         hide : true,
20422         /**
20423          * @event select
20424          * Fires when select a date.
20425          * @param {Roo.bootstrap.MonthField} this
20426          * @param {String} oldvalue The old value
20427          * @param {String} newvalue The new value
20428          */
20429         select : true
20430     });
20431 };
20432
20433 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20434     
20435     onRender: function(ct, position)
20436     {
20437         
20438         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20439         
20440         this.language = this.language || 'en';
20441         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20442         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20443         
20444         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20445         this.isInline = false;
20446         this.isInput = true;
20447         this.component = this.el.select('.add-on', true).first() || false;
20448         this.component = (this.component && this.component.length === 0) ? false : this.component;
20449         this.hasInput = this.component && this.inputEL().length;
20450         
20451         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20452         
20453         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20454         
20455         this.picker().on('mousedown', this.onMousedown, this);
20456         this.picker().on('click', this.onClick, this);
20457         
20458         this.picker().addClass('datepicker-dropdown');
20459         
20460         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20461             v.setStyle('width', '189px');
20462         });
20463         
20464         this.fillMonths();
20465         
20466         this.update();
20467         
20468         if(this.isInline) {
20469             this.show();
20470         }
20471         
20472     },
20473     
20474     setValue: function(v, suppressEvent)
20475     {   
20476         var o = this.getValue();
20477         
20478         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20479         
20480         this.update();
20481
20482         if(suppressEvent !== true){
20483             this.fireEvent('select', this, o, v);
20484         }
20485         
20486     },
20487     
20488     getValue: function()
20489     {
20490         return this.value;
20491     },
20492     
20493     onClick: function(e) 
20494     {
20495         e.stopPropagation();
20496         e.preventDefault();
20497         
20498         var target = e.getTarget();
20499         
20500         if(target.nodeName.toLowerCase() === 'i'){
20501             target = Roo.get(target).dom.parentNode;
20502         }
20503         
20504         var nodeName = target.nodeName;
20505         var className = target.className;
20506         var html = target.innerHTML;
20507         
20508         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20509             return;
20510         }
20511         
20512         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20513         
20514         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20515         
20516         this.hide();
20517                         
20518     },
20519     
20520     picker : function()
20521     {
20522         return this.pickerEl;
20523     },
20524     
20525     fillMonths: function()
20526     {    
20527         var i = 0;
20528         var months = this.picker().select('>.datepicker-months td', true).first();
20529         
20530         months.dom.innerHTML = '';
20531         
20532         while (i < 12) {
20533             var month = {
20534                 tag: 'span',
20535                 cls: 'month',
20536                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20537             };
20538             
20539             months.createChild(month);
20540         }
20541         
20542     },
20543     
20544     update: function()
20545     {
20546         var _this = this;
20547         
20548         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20549             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20550         }
20551         
20552         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20553             e.removeClass('active');
20554             
20555             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20556                 e.addClass('active');
20557             }
20558         })
20559     },
20560     
20561     place: function()
20562     {
20563         if(this.isInline) {
20564             return;
20565         }
20566         
20567         this.picker().removeClass(['bottom', 'top']);
20568         
20569         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20570             /*
20571              * place to the top of element!
20572              *
20573              */
20574             
20575             this.picker().addClass('top');
20576             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20577             
20578             return;
20579         }
20580         
20581         this.picker().addClass('bottom');
20582         
20583         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20584     },
20585     
20586     onFocus : function()
20587     {
20588         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20589         this.show();
20590     },
20591     
20592     onBlur : function()
20593     {
20594         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20595         
20596         var d = this.inputEl().getValue();
20597         
20598         this.setValue(d);
20599                 
20600         this.hide();
20601     },
20602     
20603     show : function()
20604     {
20605         this.picker().show();
20606         this.picker().select('>.datepicker-months', true).first().show();
20607         this.update();
20608         this.place();
20609         
20610         this.fireEvent('show', this, this.date);
20611     },
20612     
20613     hide : function()
20614     {
20615         if(this.isInline) {
20616             return;
20617         }
20618         this.picker().hide();
20619         this.fireEvent('hide', this, this.date);
20620         
20621     },
20622     
20623     onMousedown: function(e)
20624     {
20625         e.stopPropagation();
20626         e.preventDefault();
20627     },
20628     
20629     keyup: function(e)
20630     {
20631         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20632         this.update();
20633     },
20634
20635     fireKey: function(e)
20636     {
20637         if (!this.picker().isVisible()){
20638             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20639                 this.show();
20640             }
20641             return;
20642         }
20643         
20644         var dir;
20645         
20646         switch(e.keyCode){
20647             case 27: // escape
20648                 this.hide();
20649                 e.preventDefault();
20650                 break;
20651             case 37: // left
20652             case 39: // right
20653                 dir = e.keyCode == 37 ? -1 : 1;
20654                 
20655                 this.vIndex = this.vIndex + dir;
20656                 
20657                 if(this.vIndex < 0){
20658                     this.vIndex = 0;
20659                 }
20660                 
20661                 if(this.vIndex > 11){
20662                     this.vIndex = 11;
20663                 }
20664                 
20665                 if(isNaN(this.vIndex)){
20666                     this.vIndex = 0;
20667                 }
20668                 
20669                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20670                 
20671                 break;
20672             case 38: // up
20673             case 40: // down
20674                 
20675                 dir = e.keyCode == 38 ? -1 : 1;
20676                 
20677                 this.vIndex = this.vIndex + dir * 4;
20678                 
20679                 if(this.vIndex < 0){
20680                     this.vIndex = 0;
20681                 }
20682                 
20683                 if(this.vIndex > 11){
20684                     this.vIndex = 11;
20685                 }
20686                 
20687                 if(isNaN(this.vIndex)){
20688                     this.vIndex = 0;
20689                 }
20690                 
20691                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20692                 break;
20693                 
20694             case 13: // enter
20695                 
20696                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20697                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20698                 }
20699                 
20700                 this.hide();
20701                 e.preventDefault();
20702                 break;
20703             case 9: // tab
20704                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20705                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20706                 }
20707                 this.hide();
20708                 break;
20709             case 16: // shift
20710             case 17: // ctrl
20711             case 18: // alt
20712                 break;
20713             default :
20714                 this.hide();
20715                 
20716         }
20717     },
20718     
20719     remove: function() 
20720     {
20721         this.picker().remove();
20722     }
20723    
20724 });
20725
20726 Roo.apply(Roo.bootstrap.MonthField,  {
20727     
20728     content : {
20729         tag: 'tbody',
20730         cn: [
20731         {
20732             tag: 'tr',
20733             cn: [
20734             {
20735                 tag: 'td',
20736                 colspan: '7'
20737             }
20738             ]
20739         }
20740         ]
20741     },
20742     
20743     dates:{
20744         en: {
20745             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20746             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20747         }
20748     }
20749 });
20750
20751 Roo.apply(Roo.bootstrap.MonthField,  {
20752   
20753     template : {
20754         tag: 'div',
20755         cls: 'datepicker dropdown-menu roo-dynamic',
20756         cn: [
20757             {
20758                 tag: 'div',
20759                 cls: 'datepicker-months',
20760                 cn: [
20761                 {
20762                     tag: 'table',
20763                     cls: 'table-condensed',
20764                     cn:[
20765                         Roo.bootstrap.DateField.content
20766                     ]
20767                 }
20768                 ]
20769             }
20770         ]
20771     }
20772 });
20773
20774  
20775
20776  
20777  /*
20778  * - LGPL
20779  *
20780  * CheckBox
20781  * 
20782  */
20783
20784 /**
20785  * @class Roo.bootstrap.CheckBox
20786  * @extends Roo.bootstrap.Input
20787  * Bootstrap CheckBox class
20788  * 
20789  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20790  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20791  * @cfg {String} boxLabel The text that appears beside the checkbox
20792  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20793  * @cfg {Boolean} checked initnal the element
20794  * @cfg {Boolean} inline inline the element (default false)
20795  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20796  * @cfg {String} tooltip label tooltip
20797  * 
20798  * @constructor
20799  * Create a new CheckBox
20800  * @param {Object} config The config object
20801  */
20802
20803 Roo.bootstrap.CheckBox = function(config){
20804     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20805    
20806     this.addEvents({
20807         /**
20808         * @event check
20809         * Fires when the element is checked or unchecked.
20810         * @param {Roo.bootstrap.CheckBox} this This input
20811         * @param {Boolean} checked The new checked value
20812         */
20813        check : true,
20814        /**
20815         * @event click
20816         * Fires when the element is click.
20817         * @param {Roo.bootstrap.CheckBox} this This input
20818         */
20819        click : true
20820     });
20821     
20822 };
20823
20824 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20825   
20826     inputType: 'checkbox',
20827     inputValue: 1,
20828     valueOff: 0,
20829     boxLabel: false,
20830     checked: false,
20831     weight : false,
20832     inline: false,
20833     tooltip : '',
20834     
20835     getAutoCreate : function()
20836     {
20837         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20838         
20839         var id = Roo.id();
20840         
20841         var cfg = {};
20842         
20843         cfg.cls = 'form-group ' + this.inputType; //input-group
20844         
20845         if(this.inline){
20846             cfg.cls += ' ' + this.inputType + '-inline';
20847         }
20848         
20849         var input =  {
20850             tag: 'input',
20851             id : id,
20852             type : this.inputType,
20853             value : this.inputValue,
20854             cls : 'roo-' + this.inputType, //'form-box',
20855             placeholder : this.placeholder || ''
20856             
20857         };
20858         
20859         if(this.inputType != 'radio'){
20860             var hidden =  {
20861                 tag: 'input',
20862                 type : 'hidden',
20863                 cls : 'roo-hidden-value',
20864                 value : this.checked ? this.inputValue : this.valueOff
20865             };
20866         }
20867         
20868             
20869         if (this.weight) { // Validity check?
20870             cfg.cls += " " + this.inputType + "-" + this.weight;
20871         }
20872         
20873         if (this.disabled) {
20874             input.disabled=true;
20875         }
20876         
20877         if(this.checked){
20878             input.checked = this.checked;
20879         }
20880         
20881         if (this.name) {
20882             
20883             input.name = this.name;
20884             
20885             if(this.inputType != 'radio'){
20886                 hidden.name = this.name;
20887                 input.name = '_hidden_' + this.name;
20888             }
20889         }
20890         
20891         if (this.size) {
20892             input.cls += ' input-' + this.size;
20893         }
20894         
20895         var settings=this;
20896         
20897         ['xs','sm','md','lg'].map(function(size){
20898             if (settings[size]) {
20899                 cfg.cls += ' col-' + size + '-' + settings[size];
20900             }
20901         });
20902         
20903         var inputblock = input;
20904          
20905         if (this.before || this.after) {
20906             
20907             inputblock = {
20908                 cls : 'input-group',
20909                 cn :  [] 
20910             };
20911             
20912             if (this.before) {
20913                 inputblock.cn.push({
20914                     tag :'span',
20915                     cls : 'input-group-addon',
20916                     html : this.before
20917                 });
20918             }
20919             
20920             inputblock.cn.push(input);
20921             
20922             if(this.inputType != 'radio'){
20923                 inputblock.cn.push(hidden);
20924             }
20925             
20926             if (this.after) {
20927                 inputblock.cn.push({
20928                     tag :'span',
20929                     cls : 'input-group-addon',
20930                     html : this.after
20931                 });
20932             }
20933             
20934         }
20935         
20936         if (align ==='left' && this.fieldLabel.length) {
20937 //                Roo.log("left and has label");
20938             cfg.cn = [
20939                 {
20940                     tag: 'label',
20941                     'for' :  id,
20942                     cls : 'control-label',
20943                     html : this.fieldLabel
20944                 },
20945                 {
20946                     cls : "", 
20947                     cn: [
20948                         inputblock
20949                     ]
20950                 }
20951             ];
20952             
20953             if(this.labelWidth > 12){
20954                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20955             }
20956             
20957             if(this.labelWidth < 13 && this.labelmd == 0){
20958                 this.labelmd = this.labelWidth;
20959             }
20960             
20961             if(this.labellg > 0){
20962                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20963                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20964             }
20965             
20966             if(this.labelmd > 0){
20967                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20968                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20969             }
20970             
20971             if(this.labelsm > 0){
20972                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20973                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20974             }
20975             
20976             if(this.labelxs > 0){
20977                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20978                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20979             }
20980             
20981         } else if ( this.fieldLabel.length) {
20982 //                Roo.log(" label");
20983                 cfg.cn = [
20984                    
20985                     {
20986                         tag: this.boxLabel ? 'span' : 'label',
20987                         'for': id,
20988                         cls: 'control-label box-input-label',
20989                         //cls : 'input-group-addon',
20990                         html : this.fieldLabel
20991                     },
20992                     
20993                     inputblock
20994                     
20995                 ];
20996
20997         } else {
20998             
20999 //                Roo.log(" no label && no align");
21000                 cfg.cn = [  inputblock ] ;
21001                 
21002                 
21003         }
21004         
21005         if(this.boxLabel){
21006              var boxLabelCfg = {
21007                 tag: 'label',
21008                 //'for': id, // box label is handled by onclick - so no for...
21009                 cls: 'box-label',
21010                 html: this.boxLabel
21011             };
21012             
21013             if(this.tooltip){
21014                 boxLabelCfg.tooltip = this.tooltip;
21015             }
21016              
21017             cfg.cn.push(boxLabelCfg);
21018         }
21019         
21020         if(this.inputType != 'radio'){
21021             cfg.cn.push(hidden);
21022         }
21023         
21024         return cfg;
21025         
21026     },
21027     
21028     /**
21029      * return the real input element.
21030      */
21031     inputEl: function ()
21032     {
21033         return this.el.select('input.roo-' + this.inputType,true).first();
21034     },
21035     hiddenEl: function ()
21036     {
21037         return this.el.select('input.roo-hidden-value',true).first();
21038     },
21039     
21040     labelEl: function()
21041     {
21042         return this.el.select('label.control-label',true).first();
21043     },
21044     /* depricated... */
21045     
21046     label: function()
21047     {
21048         return this.labelEl();
21049     },
21050     
21051     boxLabelEl: function()
21052     {
21053         return this.el.select('label.box-label',true).first();
21054     },
21055     
21056     initEvents : function()
21057     {
21058 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21059         
21060         this.inputEl().on('click', this.onClick,  this);
21061         
21062         if (this.boxLabel) { 
21063             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21064         }
21065         
21066         this.startValue = this.getValue();
21067         
21068         if(this.groupId){
21069             Roo.bootstrap.CheckBox.register(this);
21070         }
21071     },
21072     
21073     onClick : function(e)
21074     {   
21075         if(this.fireEvent('click', this, e) !== false){
21076             this.setChecked(!this.checked);
21077         }
21078         
21079     },
21080     
21081     setChecked : function(state,suppressEvent)
21082     {
21083         this.startValue = this.getValue();
21084
21085         if(this.inputType == 'radio'){
21086             
21087             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21088                 e.dom.checked = false;
21089             });
21090             
21091             this.inputEl().dom.checked = true;
21092             
21093             this.inputEl().dom.value = this.inputValue;
21094             
21095             if(suppressEvent !== true){
21096                 this.fireEvent('check', this, true);
21097             }
21098             
21099             this.validate();
21100             
21101             return;
21102         }
21103         
21104         this.checked = state;
21105         
21106         this.inputEl().dom.checked = state;
21107         
21108         
21109         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21110         
21111         if(suppressEvent !== true){
21112             this.fireEvent('check', this, state);
21113         }
21114         
21115         this.validate();
21116     },
21117     
21118     getValue : function()
21119     {
21120         if(this.inputType == 'radio'){
21121             return this.getGroupValue();
21122         }
21123         
21124         return this.hiddenEl().dom.value;
21125         
21126     },
21127     
21128     getGroupValue : function()
21129     {
21130         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21131             return '';
21132         }
21133         
21134         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21135     },
21136     
21137     setValue : function(v,suppressEvent)
21138     {
21139         if(this.inputType == 'radio'){
21140             this.setGroupValue(v, suppressEvent);
21141             return;
21142         }
21143         
21144         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21145         
21146         this.validate();
21147     },
21148     
21149     setGroupValue : function(v, suppressEvent)
21150     {
21151         this.startValue = this.getValue();
21152         
21153         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21154             e.dom.checked = false;
21155             
21156             if(e.dom.value == v){
21157                 e.dom.checked = true;
21158             }
21159         });
21160         
21161         if(suppressEvent !== true){
21162             this.fireEvent('check', this, true);
21163         }
21164
21165         this.validate();
21166         
21167         return;
21168     },
21169     
21170     validate : function()
21171     {
21172         if(this.getVisibilityEl().hasClass('hidden')){
21173             return true;
21174         }
21175         
21176         if(
21177                 this.disabled || 
21178                 (this.inputType == 'radio' && this.validateRadio()) ||
21179                 (this.inputType == 'checkbox' && this.validateCheckbox())
21180         ){
21181             this.markValid();
21182             return true;
21183         }
21184         
21185         this.markInvalid();
21186         return false;
21187     },
21188     
21189     validateRadio : function()
21190     {
21191         if(this.getVisibilityEl().hasClass('hidden')){
21192             return true;
21193         }
21194         
21195         if(this.allowBlank){
21196             return true;
21197         }
21198         
21199         var valid = false;
21200         
21201         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21202             if(!e.dom.checked){
21203                 return;
21204             }
21205             
21206             valid = true;
21207             
21208             return false;
21209         });
21210         
21211         return valid;
21212     },
21213     
21214     validateCheckbox : function()
21215     {
21216         if(!this.groupId){
21217             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21218             //return (this.getValue() == this.inputValue) ? true : false;
21219         }
21220         
21221         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21222         
21223         if(!group){
21224             return false;
21225         }
21226         
21227         var r = false;
21228         
21229         for(var i in group){
21230             if(group[i].el.isVisible(true)){
21231                 r = false;
21232                 break;
21233             }
21234             
21235             r = true;
21236         }
21237         
21238         for(var i in group){
21239             if(r){
21240                 break;
21241             }
21242             
21243             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21244         }
21245         
21246         return r;
21247     },
21248     
21249     /**
21250      * Mark this field as valid
21251      */
21252     markValid : function()
21253     {
21254         var _this = this;
21255         
21256         this.fireEvent('valid', this);
21257         
21258         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21259         
21260         if(this.groupId){
21261             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21262         }
21263         
21264         if(label){
21265             label.markValid();
21266         }
21267
21268         if(this.inputType == 'radio'){
21269             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21270                 var fg = e.findParent('.form-group', false, true);
21271                 if (Roo.bootstrap.version == 3) {
21272                     fg.removeClass([_this.invalidClass, _this.validClass]);
21273                     fg.addClass(_this.validClass);
21274                 } else {
21275                     fg.removeClass(['is-valid', 'is-invalid']);
21276                     fg.addClass('is-valid');
21277                 }
21278             });
21279             
21280             return;
21281         }
21282
21283         if(!this.groupId){
21284             var fg = this.el.findParent('.form-group', false, true);
21285             if (Roo.bootstrap.version == 3) {
21286                 fg.removeClass([this.invalidClass, this.validClass]);
21287                 fg.addClass(this.validClass);
21288             } else {
21289                 fg.removeClass(['is-valid', 'is-invalid']);
21290                 fg.addClass('is-valid');
21291             }
21292             return;
21293         }
21294         
21295         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21296         
21297         if(!group){
21298             return;
21299         }
21300         
21301         for(var i in group){
21302             var fg = group[i].el.findParent('.form-group', false, true);
21303             if (Roo.bootstrap.version == 3) {
21304                 fg.removeClass([this.invalidClass, this.validClass]);
21305                 fg.addClass(this.validClass);
21306             } else {
21307                 fg.removeClass(['is-valid', 'is-invalid']);
21308                 fg.addClass('is-valid');
21309             }
21310         }
21311     },
21312     
21313      /**
21314      * Mark this field as invalid
21315      * @param {String} msg The validation message
21316      */
21317     markInvalid : function(msg)
21318     {
21319         if(this.allowBlank){
21320             return;
21321         }
21322         
21323         var _this = this;
21324         
21325         this.fireEvent('invalid', this, msg);
21326         
21327         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21328         
21329         if(this.groupId){
21330             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21331         }
21332         
21333         if(label){
21334             label.markInvalid();
21335         }
21336             
21337         if(this.inputType == 'radio'){
21338             
21339             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21340                 var fg = e.findParent('.form-group', false, true);
21341                 if (Roo.bootstrap.version == 3) {
21342                     fg.removeClass([_this.invalidClass, _this.validClass]);
21343                     fg.addClass(_this.invalidClass);
21344                 } else {
21345                     fg.removeClass(['is-invalid', 'is-valid']);
21346                     fg.addClass('is-invalid');
21347                 }
21348             });
21349             
21350             return;
21351         }
21352         
21353         if(!this.groupId){
21354             var fg = this.el.findParent('.form-group', false, true);
21355             if (Roo.bootstrap.version == 3) {
21356                 fg.removeClass([_this.invalidClass, _this.validClass]);
21357                 fg.addClass(_this.invalidClass);
21358             } else {
21359                 fg.removeClass(['is-invalid', 'is-valid']);
21360                 fg.addClass('is-invalid');
21361             }
21362             return;
21363         }
21364         
21365         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21366         
21367         if(!group){
21368             return;
21369         }
21370         
21371         for(var i in group){
21372             var fg = group[i].el.findParent('.form-group', false, true);
21373             if (Roo.bootstrap.version == 3) {
21374                 fg.removeClass([_this.invalidClass, _this.validClass]);
21375                 fg.addClass(_this.invalidClass);
21376             } else {
21377                 fg.removeClass(['is-invalid', 'is-valid']);
21378                 fg.addClass('is-invalid');
21379             }
21380         }
21381         
21382     },
21383     
21384     clearInvalid : function()
21385     {
21386         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21387         
21388         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21389         
21390         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21391         
21392         if (label && label.iconEl) {
21393             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21394             label.iconEl.removeClass(['is-invalid', 'is-valid']);
21395         }
21396     },
21397     
21398     disable : function()
21399     {
21400         if(this.inputType != 'radio'){
21401             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21402             return;
21403         }
21404         
21405         var _this = this;
21406         
21407         if(this.rendered){
21408             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21409                 _this.getActionEl().addClass(this.disabledClass);
21410                 e.dom.disabled = true;
21411             });
21412         }
21413         
21414         this.disabled = true;
21415         this.fireEvent("disable", this);
21416         return this;
21417     },
21418
21419     enable : function()
21420     {
21421         if(this.inputType != 'radio'){
21422             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21423             return;
21424         }
21425         
21426         var _this = this;
21427         
21428         if(this.rendered){
21429             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21430                 _this.getActionEl().removeClass(this.disabledClass);
21431                 e.dom.disabled = false;
21432             });
21433         }
21434         
21435         this.disabled = false;
21436         this.fireEvent("enable", this);
21437         return this;
21438     },
21439     
21440     setBoxLabel : function(v)
21441     {
21442         this.boxLabel = v;
21443         
21444         if(this.rendered){
21445             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21446         }
21447     }
21448
21449 });
21450
21451 Roo.apply(Roo.bootstrap.CheckBox, {
21452     
21453     groups: {},
21454     
21455      /**
21456     * register a CheckBox Group
21457     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21458     */
21459     register : function(checkbox)
21460     {
21461         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21462             this.groups[checkbox.groupId] = {};
21463         }
21464         
21465         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21466             return;
21467         }
21468         
21469         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21470         
21471     },
21472     /**
21473     * fetch a CheckBox Group based on the group ID
21474     * @param {string} the group ID
21475     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21476     */
21477     get: function(groupId) {
21478         if (typeof(this.groups[groupId]) == 'undefined') {
21479             return false;
21480         }
21481         
21482         return this.groups[groupId] ;
21483     }
21484     
21485     
21486 });
21487 /*
21488  * - LGPL
21489  *
21490  * RadioItem
21491  * 
21492  */
21493
21494 /**
21495  * @class Roo.bootstrap.Radio
21496  * @extends Roo.bootstrap.Component
21497  * Bootstrap Radio class
21498  * @cfg {String} boxLabel - the label associated
21499  * @cfg {String} value - the value of radio
21500  * 
21501  * @constructor
21502  * Create a new Radio
21503  * @param {Object} config The config object
21504  */
21505 Roo.bootstrap.Radio = function(config){
21506     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21507     
21508 };
21509
21510 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21511     
21512     boxLabel : '',
21513     
21514     value : '',
21515     
21516     getAutoCreate : function()
21517     {
21518         var cfg = {
21519             tag : 'div',
21520             cls : 'form-group radio',
21521             cn : [
21522                 {
21523                     tag : 'label',
21524                     cls : 'box-label',
21525                     html : this.boxLabel
21526                 }
21527             ]
21528         };
21529         
21530         return cfg;
21531     },
21532     
21533     initEvents : function() 
21534     {
21535         this.parent().register(this);
21536         
21537         this.el.on('click', this.onClick, this);
21538         
21539     },
21540     
21541     onClick : function(e)
21542     {
21543         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21544             this.setChecked(true);
21545         }
21546     },
21547     
21548     setChecked : function(state, suppressEvent)
21549     {
21550         this.parent().setValue(this.value, suppressEvent);
21551         
21552     },
21553     
21554     setBoxLabel : function(v)
21555     {
21556         this.boxLabel = v;
21557         
21558         if(this.rendered){
21559             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21560         }
21561     }
21562     
21563 });
21564  
21565
21566  /*
21567  * - LGPL
21568  *
21569  * Input
21570  * 
21571  */
21572
21573 /**
21574  * @class Roo.bootstrap.SecurePass
21575  * @extends Roo.bootstrap.Input
21576  * Bootstrap SecurePass class
21577  *
21578  * 
21579  * @constructor
21580  * Create a new SecurePass
21581  * @param {Object} config The config object
21582  */
21583  
21584 Roo.bootstrap.SecurePass = function (config) {
21585     // these go here, so the translation tool can replace them..
21586     this.errors = {
21587         PwdEmpty: "Please type a password, and then retype it to confirm.",
21588         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21589         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21590         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21591         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21592         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21593         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21594         TooWeak: "Your password is Too Weak."
21595     },
21596     this.meterLabel = "Password strength:";
21597     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21598     this.meterClass = [
21599         "roo-password-meter-tooweak", 
21600         "roo-password-meter-weak", 
21601         "roo-password-meter-medium", 
21602         "roo-password-meter-strong", 
21603         "roo-password-meter-grey"
21604     ];
21605     
21606     this.errors = {};
21607     
21608     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21609 }
21610
21611 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21612     /**
21613      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21614      * {
21615      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21616      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21617      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21618      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21619      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21620      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21621      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21622      * })
21623      */
21624     // private
21625     
21626     meterWidth: 300,
21627     errorMsg :'',    
21628     errors: false,
21629     imageRoot: '/',
21630     /**
21631      * @cfg {String/Object} Label for the strength meter (defaults to
21632      * 'Password strength:')
21633      */
21634     // private
21635     meterLabel: '',
21636     /**
21637      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21638      * ['Weak', 'Medium', 'Strong'])
21639      */
21640     // private    
21641     pwdStrengths: false,    
21642     // private
21643     strength: 0,
21644     // private
21645     _lastPwd: null,
21646     // private
21647     kCapitalLetter: 0,
21648     kSmallLetter: 1,
21649     kDigit: 2,
21650     kPunctuation: 3,
21651     
21652     insecure: false,
21653     // private
21654     initEvents: function ()
21655     {
21656         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21657
21658         if (this.el.is('input[type=password]') && Roo.isSafari) {
21659             this.el.on('keydown', this.SafariOnKeyDown, this);
21660         }
21661
21662         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21663     },
21664     // private
21665     onRender: function (ct, position)
21666     {
21667         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21668         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21669         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21670
21671         this.trigger.createChild({
21672                    cn: [
21673                     {
21674                     //id: 'PwdMeter',
21675                     tag: 'div',
21676                     cls: 'roo-password-meter-grey col-xs-12',
21677                     style: {
21678                         //width: 0,
21679                         //width: this.meterWidth + 'px'                                                
21680                         }
21681                     },
21682                     {                            
21683                          cls: 'roo-password-meter-text'                          
21684                     }
21685                 ]            
21686         });
21687
21688          
21689         if (this.hideTrigger) {
21690             this.trigger.setDisplayed(false);
21691         }
21692         this.setSize(this.width || '', this.height || '');
21693     },
21694     // private
21695     onDestroy: function ()
21696     {
21697         if (this.trigger) {
21698             this.trigger.removeAllListeners();
21699             this.trigger.remove();
21700         }
21701         if (this.wrap) {
21702             this.wrap.remove();
21703         }
21704         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21705     },
21706     // private
21707     checkStrength: function ()
21708     {
21709         var pwd = this.inputEl().getValue();
21710         if (pwd == this._lastPwd) {
21711             return;
21712         }
21713
21714         var strength;
21715         if (this.ClientSideStrongPassword(pwd)) {
21716             strength = 3;
21717         } else if (this.ClientSideMediumPassword(pwd)) {
21718             strength = 2;
21719         } else if (this.ClientSideWeakPassword(pwd)) {
21720             strength = 1;
21721         } else {
21722             strength = 0;
21723         }
21724         
21725         Roo.log('strength1: ' + strength);
21726         
21727         //var pm = this.trigger.child('div/div/div').dom;
21728         var pm = this.trigger.child('div/div');
21729         pm.removeClass(this.meterClass);
21730         pm.addClass(this.meterClass[strength]);
21731                 
21732         
21733         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21734                 
21735         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21736         
21737         this._lastPwd = pwd;
21738     },
21739     reset: function ()
21740     {
21741         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21742         
21743         this._lastPwd = '';
21744         
21745         var pm = this.trigger.child('div/div');
21746         pm.removeClass(this.meterClass);
21747         pm.addClass('roo-password-meter-grey');        
21748         
21749         
21750         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21751         
21752         pt.innerHTML = '';
21753         this.inputEl().dom.type='password';
21754     },
21755     // private
21756     validateValue: function (value)
21757     {
21758         
21759         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21760             return false;
21761         }
21762         if (value.length == 0) {
21763             if (this.allowBlank) {
21764                 this.clearInvalid();
21765                 return true;
21766             }
21767
21768             this.markInvalid(this.errors.PwdEmpty);
21769             this.errorMsg = this.errors.PwdEmpty;
21770             return false;
21771         }
21772         
21773         if(this.insecure){
21774             return true;
21775         }
21776         
21777         if ('[\x21-\x7e]*'.match(value)) {
21778             this.markInvalid(this.errors.PwdBadChar);
21779             this.errorMsg = this.errors.PwdBadChar;
21780             return false;
21781         }
21782         if (value.length < 6) {
21783             this.markInvalid(this.errors.PwdShort);
21784             this.errorMsg = this.errors.PwdShort;
21785             return false;
21786         }
21787         if (value.length > 16) {
21788             this.markInvalid(this.errors.PwdLong);
21789             this.errorMsg = this.errors.PwdLong;
21790             return false;
21791         }
21792         var strength;
21793         if (this.ClientSideStrongPassword(value)) {
21794             strength = 3;
21795         } else if (this.ClientSideMediumPassword(value)) {
21796             strength = 2;
21797         } else if (this.ClientSideWeakPassword(value)) {
21798             strength = 1;
21799         } else {
21800             strength = 0;
21801         }
21802
21803         
21804         if (strength < 2) {
21805             //this.markInvalid(this.errors.TooWeak);
21806             this.errorMsg = this.errors.TooWeak;
21807             //return false;
21808         }
21809         
21810         
21811         console.log('strength2: ' + strength);
21812         
21813         //var pm = this.trigger.child('div/div/div').dom;
21814         
21815         var pm = this.trigger.child('div/div');
21816         pm.removeClass(this.meterClass);
21817         pm.addClass(this.meterClass[strength]);
21818                 
21819         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21820                 
21821         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21822         
21823         this.errorMsg = ''; 
21824         return true;
21825     },
21826     // private
21827     CharacterSetChecks: function (type)
21828     {
21829         this.type = type;
21830         this.fResult = false;
21831     },
21832     // private
21833     isctype: function (character, type)
21834     {
21835         switch (type) {  
21836             case this.kCapitalLetter:
21837                 if (character >= 'A' && character <= 'Z') {
21838                     return true;
21839                 }
21840                 break;
21841             
21842             case this.kSmallLetter:
21843                 if (character >= 'a' && character <= 'z') {
21844                     return true;
21845                 }
21846                 break;
21847             
21848             case this.kDigit:
21849                 if (character >= '0' && character <= '9') {
21850                     return true;
21851                 }
21852                 break;
21853             
21854             case this.kPunctuation:
21855                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21856                     return true;
21857                 }
21858                 break;
21859             
21860             default:
21861                 return false;
21862         }
21863
21864     },
21865     // private
21866     IsLongEnough: function (pwd, size)
21867     {
21868         return !(pwd == null || isNaN(size) || pwd.length < size);
21869     },
21870     // private
21871     SpansEnoughCharacterSets: function (word, nb)
21872     {
21873         if (!this.IsLongEnough(word, nb))
21874         {
21875             return false;
21876         }
21877
21878         var characterSetChecks = new Array(
21879             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21880             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21881         );
21882         
21883         for (var index = 0; index < word.length; ++index) {
21884             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21885                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21886                     characterSetChecks[nCharSet].fResult = true;
21887                     break;
21888                 }
21889             }
21890         }
21891
21892         var nCharSets = 0;
21893         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21894             if (characterSetChecks[nCharSet].fResult) {
21895                 ++nCharSets;
21896             }
21897         }
21898
21899         if (nCharSets < nb) {
21900             return false;
21901         }
21902         return true;
21903     },
21904     // private
21905     ClientSideStrongPassword: function (pwd)
21906     {
21907         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21908     },
21909     // private
21910     ClientSideMediumPassword: function (pwd)
21911     {
21912         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21913     },
21914     // private
21915     ClientSideWeakPassword: function (pwd)
21916     {
21917         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21918     }
21919           
21920 })//<script type="text/javascript">
21921
21922 /*
21923  * Based  Ext JS Library 1.1.1
21924  * Copyright(c) 2006-2007, Ext JS, LLC.
21925  * LGPL
21926  *
21927  */
21928  
21929 /**
21930  * @class Roo.HtmlEditorCore
21931  * @extends Roo.Component
21932  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21933  *
21934  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21935  */
21936
21937 Roo.HtmlEditorCore = function(config){
21938     
21939     
21940     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21941     
21942     
21943     this.addEvents({
21944         /**
21945          * @event initialize
21946          * Fires when the editor is fully initialized (including the iframe)
21947          * @param {Roo.HtmlEditorCore} this
21948          */
21949         initialize: true,
21950         /**
21951          * @event activate
21952          * Fires when the editor is first receives the focus. Any insertion must wait
21953          * until after this event.
21954          * @param {Roo.HtmlEditorCore} this
21955          */
21956         activate: true,
21957          /**
21958          * @event beforesync
21959          * Fires before the textarea is updated with content from the editor iframe. Return false
21960          * to cancel the sync.
21961          * @param {Roo.HtmlEditorCore} this
21962          * @param {String} html
21963          */
21964         beforesync: true,
21965          /**
21966          * @event beforepush
21967          * Fires before the iframe editor is updated with content from the textarea. Return false
21968          * to cancel the push.
21969          * @param {Roo.HtmlEditorCore} this
21970          * @param {String} html
21971          */
21972         beforepush: true,
21973          /**
21974          * @event sync
21975          * Fires when the textarea is updated with content from the editor iframe.
21976          * @param {Roo.HtmlEditorCore} this
21977          * @param {String} html
21978          */
21979         sync: true,
21980          /**
21981          * @event push
21982          * Fires when the iframe editor is updated with content from the textarea.
21983          * @param {Roo.HtmlEditorCore} this
21984          * @param {String} html
21985          */
21986         push: true,
21987         
21988         /**
21989          * @event editorevent
21990          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21991          * @param {Roo.HtmlEditorCore} this
21992          */
21993         editorevent: true
21994         
21995     });
21996     
21997     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21998     
21999     // defaults : white / black...
22000     this.applyBlacklists();
22001     
22002     
22003     
22004 };
22005
22006
22007 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
22008
22009
22010      /**
22011      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
22012      */
22013     
22014     owner : false,
22015     
22016      /**
22017      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22018      *                        Roo.resizable.
22019      */
22020     resizable : false,
22021      /**
22022      * @cfg {Number} height (in pixels)
22023      */   
22024     height: 300,
22025    /**
22026      * @cfg {Number} width (in pixels)
22027      */   
22028     width: 500,
22029     
22030     /**
22031      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22032      * 
22033      */
22034     stylesheets: false,
22035     
22036     // id of frame..
22037     frameId: false,
22038     
22039     // private properties
22040     validationEvent : false,
22041     deferHeight: true,
22042     initialized : false,
22043     activated : false,
22044     sourceEditMode : false,
22045     onFocus : Roo.emptyFn,
22046     iframePad:3,
22047     hideMode:'offsets',
22048     
22049     clearUp: true,
22050     
22051     // blacklist + whitelisted elements..
22052     black: false,
22053     white: false,
22054      
22055     bodyCls : '',
22056
22057     /**
22058      * Protected method that will not generally be called directly. It
22059      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22060      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22061      */
22062     getDocMarkup : function(){
22063         // body styles..
22064         var st = '';
22065         
22066         // inherit styels from page...?? 
22067         if (this.stylesheets === false) {
22068             
22069             Roo.get(document.head).select('style').each(function(node) {
22070                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22071             });
22072             
22073             Roo.get(document.head).select('link').each(function(node) { 
22074                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22075             });
22076             
22077         } else if (!this.stylesheets.length) {
22078                 // simple..
22079                 st = '<style type="text/css">' +
22080                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22081                    '</style>';
22082         } else { 
22083             st = '<style type="text/css">' +
22084                     this.stylesheets +
22085                 '</style>';
22086         }
22087         
22088         st +=  '<style type="text/css">' +
22089             'IMG { cursor: pointer } ' +
22090         '</style>';
22091
22092         var cls = 'roo-htmleditor-body';
22093         
22094         if(this.bodyCls.length){
22095             cls += ' ' + this.bodyCls;
22096         }
22097         
22098         return '<html><head>' + st  +
22099             //<style type="text/css">' +
22100             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22101             //'</style>' +
22102             ' </head><body class="' +  cls + '"></body></html>';
22103     },
22104
22105     // private
22106     onRender : function(ct, position)
22107     {
22108         var _t = this;
22109         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22110         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22111         
22112         
22113         this.el.dom.style.border = '0 none';
22114         this.el.dom.setAttribute('tabIndex', -1);
22115         this.el.addClass('x-hidden hide');
22116         
22117         
22118         
22119         if(Roo.isIE){ // fix IE 1px bogus margin
22120             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22121         }
22122        
22123         
22124         this.frameId = Roo.id();
22125         
22126          
22127         
22128         var iframe = this.owner.wrap.createChild({
22129             tag: 'iframe',
22130             cls: 'form-control', // bootstrap..
22131             id: this.frameId,
22132             name: this.frameId,
22133             frameBorder : 'no',
22134             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22135         }, this.el
22136         );
22137         
22138         
22139         this.iframe = iframe.dom;
22140
22141          this.assignDocWin();
22142         
22143         this.doc.designMode = 'on';
22144        
22145         this.doc.open();
22146         this.doc.write(this.getDocMarkup());
22147         this.doc.close();
22148
22149         
22150         var task = { // must defer to wait for browser to be ready
22151             run : function(){
22152                 //console.log("run task?" + this.doc.readyState);
22153                 this.assignDocWin();
22154                 if(this.doc.body || this.doc.readyState == 'complete'){
22155                     try {
22156                         this.doc.designMode="on";
22157                     } catch (e) {
22158                         return;
22159                     }
22160                     Roo.TaskMgr.stop(task);
22161                     this.initEditor.defer(10, this);
22162                 }
22163             },
22164             interval : 10,
22165             duration: 10000,
22166             scope: this
22167         };
22168         Roo.TaskMgr.start(task);
22169
22170     },
22171
22172     // private
22173     onResize : function(w, h)
22174     {
22175          Roo.log('resize: ' +w + ',' + h );
22176         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22177         if(!this.iframe){
22178             return;
22179         }
22180         if(typeof w == 'number'){
22181             
22182             this.iframe.style.width = w + 'px';
22183         }
22184         if(typeof h == 'number'){
22185             
22186             this.iframe.style.height = h + 'px';
22187             if(this.doc){
22188                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22189             }
22190         }
22191         
22192     },
22193
22194     /**
22195      * Toggles the editor between standard and source edit mode.
22196      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22197      */
22198     toggleSourceEdit : function(sourceEditMode){
22199         
22200         this.sourceEditMode = sourceEditMode === true;
22201         
22202         if(this.sourceEditMode){
22203  
22204             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22205             
22206         }else{
22207             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22208             //this.iframe.className = '';
22209             this.deferFocus();
22210         }
22211         //this.setSize(this.owner.wrap.getSize());
22212         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22213     },
22214
22215     
22216   
22217
22218     /**
22219      * Protected method that will not generally be called directly. If you need/want
22220      * custom HTML cleanup, this is the method you should override.
22221      * @param {String} html The HTML to be cleaned
22222      * return {String} The cleaned HTML
22223      */
22224     cleanHtml : function(html){
22225         html = String(html);
22226         if(html.length > 5){
22227             if(Roo.isSafari){ // strip safari nonsense
22228                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22229             }
22230         }
22231         if(html == '&nbsp;'){
22232             html = '';
22233         }
22234         return html;
22235     },
22236
22237     /**
22238      * HTML Editor -> Textarea
22239      * Protected method that will not generally be called directly. Syncs the contents
22240      * of the editor iframe with the textarea.
22241      */
22242     syncValue : function(){
22243         if(this.initialized){
22244             var bd = (this.doc.body || this.doc.documentElement);
22245             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22246             var html = bd.innerHTML;
22247             if(Roo.isSafari){
22248                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22249                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22250                 if(m && m[1]){
22251                     html = '<div style="'+m[0]+'">' + html + '</div>';
22252                 }
22253             }
22254             html = this.cleanHtml(html);
22255             // fix up the special chars.. normaly like back quotes in word...
22256             // however we do not want to do this with chinese..
22257             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22258                 var cc = b.charCodeAt();
22259                 if (
22260                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22261                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22262                     (cc >= 0xf900 && cc < 0xfb00 )
22263                 ) {
22264                         return b;
22265                 }
22266                 return "&#"+cc+";" 
22267             });
22268             if(this.owner.fireEvent('beforesync', this, html) !== false){
22269                 this.el.dom.value = html;
22270                 this.owner.fireEvent('sync', this, html);
22271             }
22272         }
22273     },
22274
22275     /**
22276      * Protected method that will not generally be called directly. Pushes the value of the textarea
22277      * into the iframe editor.
22278      */
22279     pushValue : function(){
22280         if(this.initialized){
22281             var v = this.el.dom.value.trim();
22282             
22283 //            if(v.length < 1){
22284 //                v = '&#160;';
22285 //            }
22286             
22287             if(this.owner.fireEvent('beforepush', this, v) !== false){
22288                 var d = (this.doc.body || this.doc.documentElement);
22289                 d.innerHTML = v;
22290                 this.cleanUpPaste();
22291                 this.el.dom.value = d.innerHTML;
22292                 this.owner.fireEvent('push', this, v);
22293             }
22294         }
22295     },
22296
22297     // private
22298     deferFocus : function(){
22299         this.focus.defer(10, this);
22300     },
22301
22302     // doc'ed in Field
22303     focus : function(){
22304         if(this.win && !this.sourceEditMode){
22305             this.win.focus();
22306         }else{
22307             this.el.focus();
22308         }
22309     },
22310     
22311     assignDocWin: function()
22312     {
22313         var iframe = this.iframe;
22314         
22315          if(Roo.isIE){
22316             this.doc = iframe.contentWindow.document;
22317             this.win = iframe.contentWindow;
22318         } else {
22319 //            if (!Roo.get(this.frameId)) {
22320 //                return;
22321 //            }
22322 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22323 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22324             
22325             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22326                 return;
22327             }
22328             
22329             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22330             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22331         }
22332     },
22333     
22334     // private
22335     initEditor : function(){
22336         //console.log("INIT EDITOR");
22337         this.assignDocWin();
22338         
22339         
22340         
22341         this.doc.designMode="on";
22342         this.doc.open();
22343         this.doc.write(this.getDocMarkup());
22344         this.doc.close();
22345         
22346         var dbody = (this.doc.body || this.doc.documentElement);
22347         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22348         // this copies styles from the containing element into thsi one..
22349         // not sure why we need all of this..
22350         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22351         
22352         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22353         //ss['background-attachment'] = 'fixed'; // w3c
22354         dbody.bgProperties = 'fixed'; // ie
22355         //Roo.DomHelper.applyStyles(dbody, ss);
22356         Roo.EventManager.on(this.doc, {
22357             //'mousedown': this.onEditorEvent,
22358             'mouseup': this.onEditorEvent,
22359             'dblclick': this.onEditorEvent,
22360             'click': this.onEditorEvent,
22361             'keyup': this.onEditorEvent,
22362             buffer:100,
22363             scope: this
22364         });
22365         if(Roo.isGecko){
22366             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22367         }
22368         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22369             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22370         }
22371         this.initialized = true;
22372
22373         this.owner.fireEvent('initialize', this);
22374         this.pushValue();
22375     },
22376
22377     // private
22378     onDestroy : function(){
22379         
22380         
22381         
22382         if(this.rendered){
22383             
22384             //for (var i =0; i < this.toolbars.length;i++) {
22385             //    // fixme - ask toolbars for heights?
22386             //    this.toolbars[i].onDestroy();
22387            // }
22388             
22389             //this.wrap.dom.innerHTML = '';
22390             //this.wrap.remove();
22391         }
22392     },
22393
22394     // private
22395     onFirstFocus : function(){
22396         
22397         this.assignDocWin();
22398         
22399         
22400         this.activated = true;
22401          
22402     
22403         if(Roo.isGecko){ // prevent silly gecko errors
22404             this.win.focus();
22405             var s = this.win.getSelection();
22406             if(!s.focusNode || s.focusNode.nodeType != 3){
22407                 var r = s.getRangeAt(0);
22408                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22409                 r.collapse(true);
22410                 this.deferFocus();
22411             }
22412             try{
22413                 this.execCmd('useCSS', true);
22414                 this.execCmd('styleWithCSS', false);
22415             }catch(e){}
22416         }
22417         this.owner.fireEvent('activate', this);
22418     },
22419
22420     // private
22421     adjustFont: function(btn){
22422         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22423         //if(Roo.isSafari){ // safari
22424         //    adjust *= 2;
22425        // }
22426         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22427         if(Roo.isSafari){ // safari
22428             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22429             v =  (v < 10) ? 10 : v;
22430             v =  (v > 48) ? 48 : v;
22431             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22432             
22433         }
22434         
22435         
22436         v = Math.max(1, v+adjust);
22437         
22438         this.execCmd('FontSize', v  );
22439     },
22440
22441     onEditorEvent : function(e)
22442     {
22443         this.owner.fireEvent('editorevent', this, e);
22444       //  this.updateToolbar();
22445         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22446     },
22447
22448     insertTag : function(tg)
22449     {
22450         // could be a bit smarter... -> wrap the current selected tRoo..
22451         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22452             
22453             range = this.createRange(this.getSelection());
22454             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22455             wrappingNode.appendChild(range.extractContents());
22456             range.insertNode(wrappingNode);
22457
22458             return;
22459             
22460             
22461             
22462         }
22463         this.execCmd("formatblock",   tg);
22464         
22465     },
22466     
22467     insertText : function(txt)
22468     {
22469         
22470         
22471         var range = this.createRange();
22472         range.deleteContents();
22473                //alert(Sender.getAttribute('label'));
22474                
22475         range.insertNode(this.doc.createTextNode(txt));
22476     } ,
22477     
22478      
22479
22480     /**
22481      * Executes a Midas editor command on the editor document and performs necessary focus and
22482      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22483      * @param {String} cmd The Midas command
22484      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22485      */
22486     relayCmd : function(cmd, value){
22487         this.win.focus();
22488         this.execCmd(cmd, value);
22489         this.owner.fireEvent('editorevent', this);
22490         //this.updateToolbar();
22491         this.owner.deferFocus();
22492     },
22493
22494     /**
22495      * Executes a Midas editor command directly on the editor document.
22496      * For visual commands, you should use {@link #relayCmd} instead.
22497      * <b>This should only be called after the editor is initialized.</b>
22498      * @param {String} cmd The Midas command
22499      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22500      */
22501     execCmd : function(cmd, value){
22502         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22503         this.syncValue();
22504     },
22505  
22506  
22507    
22508     /**
22509      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22510      * to insert tRoo.
22511      * @param {String} text | dom node.. 
22512      */
22513     insertAtCursor : function(text)
22514     {
22515         
22516         if(!this.activated){
22517             return;
22518         }
22519         /*
22520         if(Roo.isIE){
22521             this.win.focus();
22522             var r = this.doc.selection.createRange();
22523             if(r){
22524                 r.collapse(true);
22525                 r.pasteHTML(text);
22526                 this.syncValue();
22527                 this.deferFocus();
22528             
22529             }
22530             return;
22531         }
22532         */
22533         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22534             this.win.focus();
22535             
22536             
22537             // from jquery ui (MIT licenced)
22538             var range, node;
22539             var win = this.win;
22540             
22541             if (win.getSelection && win.getSelection().getRangeAt) {
22542                 range = win.getSelection().getRangeAt(0);
22543                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22544                 range.insertNode(node);
22545             } else if (win.document.selection && win.document.selection.createRange) {
22546                 // no firefox support
22547                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22548                 win.document.selection.createRange().pasteHTML(txt);
22549             } else {
22550                 // no firefox support
22551                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22552                 this.execCmd('InsertHTML', txt);
22553             } 
22554             
22555             this.syncValue();
22556             
22557             this.deferFocus();
22558         }
22559     },
22560  // private
22561     mozKeyPress : function(e){
22562         if(e.ctrlKey){
22563             var c = e.getCharCode(), cmd;
22564           
22565             if(c > 0){
22566                 c = String.fromCharCode(c).toLowerCase();
22567                 switch(c){
22568                     case 'b':
22569                         cmd = 'bold';
22570                         break;
22571                     case 'i':
22572                         cmd = 'italic';
22573                         break;
22574                     
22575                     case 'u':
22576                         cmd = 'underline';
22577                         break;
22578                     
22579                     case 'v':
22580                         this.cleanUpPaste.defer(100, this);
22581                         return;
22582                         
22583                 }
22584                 if(cmd){
22585                     this.win.focus();
22586                     this.execCmd(cmd);
22587                     this.deferFocus();
22588                     e.preventDefault();
22589                 }
22590                 
22591             }
22592         }
22593     },
22594
22595     // private
22596     fixKeys : function(){ // load time branching for fastest keydown performance
22597         if(Roo.isIE){
22598             return function(e){
22599                 var k = e.getKey(), r;
22600                 if(k == e.TAB){
22601                     e.stopEvent();
22602                     r = this.doc.selection.createRange();
22603                     if(r){
22604                         r.collapse(true);
22605                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22606                         this.deferFocus();
22607                     }
22608                     return;
22609                 }
22610                 
22611                 if(k == e.ENTER){
22612                     r = this.doc.selection.createRange();
22613                     if(r){
22614                         var target = r.parentElement();
22615                         if(!target || target.tagName.toLowerCase() != 'li'){
22616                             e.stopEvent();
22617                             r.pasteHTML('<br />');
22618                             r.collapse(false);
22619                             r.select();
22620                         }
22621                     }
22622                 }
22623                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22624                     this.cleanUpPaste.defer(100, this);
22625                     return;
22626                 }
22627                 
22628                 
22629             };
22630         }else if(Roo.isOpera){
22631             return function(e){
22632                 var k = e.getKey();
22633                 if(k == e.TAB){
22634                     e.stopEvent();
22635                     this.win.focus();
22636                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22637                     this.deferFocus();
22638                 }
22639                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22640                     this.cleanUpPaste.defer(100, this);
22641                     return;
22642                 }
22643                 
22644             };
22645         }else if(Roo.isSafari){
22646             return function(e){
22647                 var k = e.getKey();
22648                 
22649                 if(k == e.TAB){
22650                     e.stopEvent();
22651                     this.execCmd('InsertText','\t');
22652                     this.deferFocus();
22653                     return;
22654                 }
22655                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22656                     this.cleanUpPaste.defer(100, this);
22657                     return;
22658                 }
22659                 
22660              };
22661         }
22662     }(),
22663     
22664     getAllAncestors: function()
22665     {
22666         var p = this.getSelectedNode();
22667         var a = [];
22668         if (!p) {
22669             a.push(p); // push blank onto stack..
22670             p = this.getParentElement();
22671         }
22672         
22673         
22674         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22675             a.push(p);
22676             p = p.parentNode;
22677         }
22678         a.push(this.doc.body);
22679         return a;
22680     },
22681     lastSel : false,
22682     lastSelNode : false,
22683     
22684     
22685     getSelection : function() 
22686     {
22687         this.assignDocWin();
22688         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22689     },
22690     
22691     getSelectedNode: function() 
22692     {
22693         // this may only work on Gecko!!!
22694         
22695         // should we cache this!!!!
22696         
22697         
22698         
22699          
22700         var range = this.createRange(this.getSelection()).cloneRange();
22701         
22702         if (Roo.isIE) {
22703             var parent = range.parentElement();
22704             while (true) {
22705                 var testRange = range.duplicate();
22706                 testRange.moveToElementText(parent);
22707                 if (testRange.inRange(range)) {
22708                     break;
22709                 }
22710                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22711                     break;
22712                 }
22713                 parent = parent.parentElement;
22714             }
22715             return parent;
22716         }
22717         
22718         // is ancestor a text element.
22719         var ac =  range.commonAncestorContainer;
22720         if (ac.nodeType == 3) {
22721             ac = ac.parentNode;
22722         }
22723         
22724         var ar = ac.childNodes;
22725          
22726         var nodes = [];
22727         var other_nodes = [];
22728         var has_other_nodes = false;
22729         for (var i=0;i<ar.length;i++) {
22730             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22731                 continue;
22732             }
22733             // fullly contained node.
22734             
22735             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22736                 nodes.push(ar[i]);
22737                 continue;
22738             }
22739             
22740             // probably selected..
22741             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22742                 other_nodes.push(ar[i]);
22743                 continue;
22744             }
22745             // outer..
22746             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22747                 continue;
22748             }
22749             
22750             
22751             has_other_nodes = true;
22752         }
22753         if (!nodes.length && other_nodes.length) {
22754             nodes= other_nodes;
22755         }
22756         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22757             return false;
22758         }
22759         
22760         return nodes[0];
22761     },
22762     createRange: function(sel)
22763     {
22764         // this has strange effects when using with 
22765         // top toolbar - not sure if it's a great idea.
22766         //this.editor.contentWindow.focus();
22767         if (typeof sel != "undefined") {
22768             try {
22769                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22770             } catch(e) {
22771                 return this.doc.createRange();
22772             }
22773         } else {
22774             return this.doc.createRange();
22775         }
22776     },
22777     getParentElement: function()
22778     {
22779         
22780         this.assignDocWin();
22781         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22782         
22783         var range = this.createRange(sel);
22784          
22785         try {
22786             var p = range.commonAncestorContainer;
22787             while (p.nodeType == 3) { // text node
22788                 p = p.parentNode;
22789             }
22790             return p;
22791         } catch (e) {
22792             return null;
22793         }
22794     
22795     },
22796     /***
22797      *
22798      * Range intersection.. the hard stuff...
22799      *  '-1' = before
22800      *  '0' = hits..
22801      *  '1' = after.
22802      *         [ -- selected range --- ]
22803      *   [fail]                        [fail]
22804      *
22805      *    basically..
22806      *      if end is before start or  hits it. fail.
22807      *      if start is after end or hits it fail.
22808      *
22809      *   if either hits (but other is outside. - then it's not 
22810      *   
22811      *    
22812      **/
22813     
22814     
22815     // @see http://www.thismuchiknow.co.uk/?p=64.
22816     rangeIntersectsNode : function(range, node)
22817     {
22818         var nodeRange = node.ownerDocument.createRange();
22819         try {
22820             nodeRange.selectNode(node);
22821         } catch (e) {
22822             nodeRange.selectNodeContents(node);
22823         }
22824     
22825         var rangeStartRange = range.cloneRange();
22826         rangeStartRange.collapse(true);
22827     
22828         var rangeEndRange = range.cloneRange();
22829         rangeEndRange.collapse(false);
22830     
22831         var nodeStartRange = nodeRange.cloneRange();
22832         nodeStartRange.collapse(true);
22833     
22834         var nodeEndRange = nodeRange.cloneRange();
22835         nodeEndRange.collapse(false);
22836     
22837         return rangeStartRange.compareBoundaryPoints(
22838                  Range.START_TO_START, nodeEndRange) == -1 &&
22839                rangeEndRange.compareBoundaryPoints(
22840                  Range.START_TO_START, nodeStartRange) == 1;
22841         
22842          
22843     },
22844     rangeCompareNode : function(range, node)
22845     {
22846         var nodeRange = node.ownerDocument.createRange();
22847         try {
22848             nodeRange.selectNode(node);
22849         } catch (e) {
22850             nodeRange.selectNodeContents(node);
22851         }
22852         
22853         
22854         range.collapse(true);
22855     
22856         nodeRange.collapse(true);
22857      
22858         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22859         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22860          
22861         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22862         
22863         var nodeIsBefore   =  ss == 1;
22864         var nodeIsAfter    = ee == -1;
22865         
22866         if (nodeIsBefore && nodeIsAfter) {
22867             return 0; // outer
22868         }
22869         if (!nodeIsBefore && nodeIsAfter) {
22870             return 1; //right trailed.
22871         }
22872         
22873         if (nodeIsBefore && !nodeIsAfter) {
22874             return 2;  // left trailed.
22875         }
22876         // fully contined.
22877         return 3;
22878     },
22879
22880     // private? - in a new class?
22881     cleanUpPaste :  function()
22882     {
22883         // cleans up the whole document..
22884         Roo.log('cleanuppaste');
22885         
22886         this.cleanUpChildren(this.doc.body);
22887         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22888         if (clean != this.doc.body.innerHTML) {
22889             this.doc.body.innerHTML = clean;
22890         }
22891         
22892     },
22893     
22894     cleanWordChars : function(input) {// change the chars to hex code
22895         var he = Roo.HtmlEditorCore;
22896         
22897         var output = input;
22898         Roo.each(he.swapCodes, function(sw) { 
22899             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22900             
22901             output = output.replace(swapper, sw[1]);
22902         });
22903         
22904         return output;
22905     },
22906     
22907     
22908     cleanUpChildren : function (n)
22909     {
22910         if (!n.childNodes.length) {
22911             return;
22912         }
22913         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22914            this.cleanUpChild(n.childNodes[i]);
22915         }
22916     },
22917     
22918     
22919         
22920     
22921     cleanUpChild : function (node)
22922     {
22923         var ed = this;
22924         //console.log(node);
22925         if (node.nodeName == "#text") {
22926             // clean up silly Windows -- stuff?
22927             return; 
22928         }
22929         if (node.nodeName == "#comment") {
22930             node.parentNode.removeChild(node);
22931             // clean up silly Windows -- stuff?
22932             return; 
22933         }
22934         var lcname = node.tagName.toLowerCase();
22935         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22936         // whitelist of tags..
22937         
22938         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22939             // remove node.
22940             node.parentNode.removeChild(node);
22941             return;
22942             
22943         }
22944         
22945         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22946         
22947         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22948         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22949         
22950         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22951         //    remove_keep_children = true;
22952         //}
22953         
22954         if (remove_keep_children) {
22955             this.cleanUpChildren(node);
22956             // inserts everything just before this node...
22957             while (node.childNodes.length) {
22958                 var cn = node.childNodes[0];
22959                 node.removeChild(cn);
22960                 node.parentNode.insertBefore(cn, node);
22961             }
22962             node.parentNode.removeChild(node);
22963             return;
22964         }
22965         
22966         if (!node.attributes || !node.attributes.length) {
22967             this.cleanUpChildren(node);
22968             return;
22969         }
22970         
22971         function cleanAttr(n,v)
22972         {
22973             
22974             if (v.match(/^\./) || v.match(/^\//)) {
22975                 return;
22976             }
22977             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22978                 return;
22979             }
22980             if (v.match(/^#/)) {
22981                 return;
22982             }
22983 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22984             node.removeAttribute(n);
22985             
22986         }
22987         
22988         var cwhite = this.cwhite;
22989         var cblack = this.cblack;
22990             
22991         function cleanStyle(n,v)
22992         {
22993             if (v.match(/expression/)) { //XSS?? should we even bother..
22994                 node.removeAttribute(n);
22995                 return;
22996             }
22997             
22998             var parts = v.split(/;/);
22999             var clean = [];
23000             
23001             Roo.each(parts, function(p) {
23002                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23003                 if (!p.length) {
23004                     return true;
23005                 }
23006                 var l = p.split(':').shift().replace(/\s+/g,'');
23007                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23008                 
23009                 if ( cwhite.length && cblack.indexOf(l) > -1) {
23010 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23011                     //node.removeAttribute(n);
23012                     return true;
23013                 }
23014                 //Roo.log()
23015                 // only allow 'c whitelisted system attributes'
23016                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
23017 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23018                     //node.removeAttribute(n);
23019                     return true;
23020                 }
23021                 
23022                 
23023                  
23024                 
23025                 clean.push(p);
23026                 return true;
23027             });
23028             if (clean.length) { 
23029                 node.setAttribute(n, clean.join(';'));
23030             } else {
23031                 node.removeAttribute(n);
23032             }
23033             
23034         }
23035         
23036         
23037         for (var i = node.attributes.length-1; i > -1 ; i--) {
23038             var a = node.attributes[i];
23039             //console.log(a);
23040             
23041             if (a.name.toLowerCase().substr(0,2)=='on')  {
23042                 node.removeAttribute(a.name);
23043                 continue;
23044             }
23045             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23046                 node.removeAttribute(a.name);
23047                 continue;
23048             }
23049             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23050                 cleanAttr(a.name,a.value); // fixme..
23051                 continue;
23052             }
23053             if (a.name == 'style') {
23054                 cleanStyle(a.name,a.value);
23055                 continue;
23056             }
23057             /// clean up MS crap..
23058             // tecnically this should be a list of valid class'es..
23059             
23060             
23061             if (a.name == 'class') {
23062                 if (a.value.match(/^Mso/)) {
23063                     node.className = '';
23064                 }
23065                 
23066                 if (a.value.match(/^body$/)) {
23067                     node.className = '';
23068                 }
23069                 continue;
23070             }
23071             
23072             // style cleanup!?
23073             // class cleanup?
23074             
23075         }
23076         
23077         
23078         this.cleanUpChildren(node);
23079         
23080         
23081     },
23082     
23083     /**
23084      * Clean up MS wordisms...
23085      */
23086     cleanWord : function(node)
23087     {
23088         
23089         
23090         if (!node) {
23091             this.cleanWord(this.doc.body);
23092             return;
23093         }
23094         if (node.nodeName == "#text") {
23095             // clean up silly Windows -- stuff?
23096             return; 
23097         }
23098         if (node.nodeName == "#comment") {
23099             node.parentNode.removeChild(node);
23100             // clean up silly Windows -- stuff?
23101             return; 
23102         }
23103         
23104         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23105             node.parentNode.removeChild(node);
23106             return;
23107         }
23108         
23109         // remove - but keep children..
23110         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
23111             while (node.childNodes.length) {
23112                 var cn = node.childNodes[0];
23113                 node.removeChild(cn);
23114                 node.parentNode.insertBefore(cn, node);
23115             }
23116             node.parentNode.removeChild(node);
23117             this.iterateChildren(node, this.cleanWord);
23118             return;
23119         }
23120         // clean styles
23121         if (node.className.length) {
23122             
23123             var cn = node.className.split(/\W+/);
23124             var cna = [];
23125             Roo.each(cn, function(cls) {
23126                 if (cls.match(/Mso[a-zA-Z]+/)) {
23127                     return;
23128                 }
23129                 cna.push(cls);
23130             });
23131             node.className = cna.length ? cna.join(' ') : '';
23132             if (!cna.length) {
23133                 node.removeAttribute("class");
23134             }
23135         }
23136         
23137         if (node.hasAttribute("lang")) {
23138             node.removeAttribute("lang");
23139         }
23140         
23141         if (node.hasAttribute("style")) {
23142             
23143             var styles = node.getAttribute("style").split(";");
23144             var nstyle = [];
23145             Roo.each(styles, function(s) {
23146                 if (!s.match(/:/)) {
23147                     return;
23148                 }
23149                 var kv = s.split(":");
23150                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23151                     return;
23152                 }
23153                 // what ever is left... we allow.
23154                 nstyle.push(s);
23155             });
23156             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23157             if (!nstyle.length) {
23158                 node.removeAttribute('style');
23159             }
23160         }
23161         this.iterateChildren(node, this.cleanWord);
23162         
23163         
23164         
23165     },
23166     /**
23167      * iterateChildren of a Node, calling fn each time, using this as the scole..
23168      * @param {DomNode} node node to iterate children of.
23169      * @param {Function} fn method of this class to call on each item.
23170      */
23171     iterateChildren : function(node, fn)
23172     {
23173         if (!node.childNodes.length) {
23174                 return;
23175         }
23176         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23177            fn.call(this, node.childNodes[i])
23178         }
23179     },
23180     
23181     
23182     /**
23183      * cleanTableWidths.
23184      *
23185      * Quite often pasting from word etc.. results in tables with column and widths.
23186      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23187      *
23188      */
23189     cleanTableWidths : function(node)
23190     {
23191          
23192          
23193         if (!node) {
23194             this.cleanTableWidths(this.doc.body);
23195             return;
23196         }
23197         
23198         // ignore list...
23199         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23200             return; 
23201         }
23202         Roo.log(node.tagName);
23203         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23204             this.iterateChildren(node, this.cleanTableWidths);
23205             return;
23206         }
23207         if (node.hasAttribute('width')) {
23208             node.removeAttribute('width');
23209         }
23210         
23211          
23212         if (node.hasAttribute("style")) {
23213             // pretty basic...
23214             
23215             var styles = node.getAttribute("style").split(";");
23216             var nstyle = [];
23217             Roo.each(styles, function(s) {
23218                 if (!s.match(/:/)) {
23219                     return;
23220                 }
23221                 var kv = s.split(":");
23222                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23223                     return;
23224                 }
23225                 // what ever is left... we allow.
23226                 nstyle.push(s);
23227             });
23228             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23229             if (!nstyle.length) {
23230                 node.removeAttribute('style');
23231             }
23232         }
23233         
23234         this.iterateChildren(node, this.cleanTableWidths);
23235         
23236         
23237     },
23238     
23239     
23240     
23241     
23242     domToHTML : function(currentElement, depth, nopadtext) {
23243         
23244         depth = depth || 0;
23245         nopadtext = nopadtext || false;
23246     
23247         if (!currentElement) {
23248             return this.domToHTML(this.doc.body);
23249         }
23250         
23251         //Roo.log(currentElement);
23252         var j;
23253         var allText = false;
23254         var nodeName = currentElement.nodeName;
23255         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23256         
23257         if  (nodeName == '#text') {
23258             
23259             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23260         }
23261         
23262         
23263         var ret = '';
23264         if (nodeName != 'BODY') {
23265              
23266             var i = 0;
23267             // Prints the node tagName, such as <A>, <IMG>, etc
23268             if (tagName) {
23269                 var attr = [];
23270                 for(i = 0; i < currentElement.attributes.length;i++) {
23271                     // quoting?
23272                     var aname = currentElement.attributes.item(i).name;
23273                     if (!currentElement.attributes.item(i).value.length) {
23274                         continue;
23275                     }
23276                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23277                 }
23278                 
23279                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23280             } 
23281             else {
23282                 
23283                 // eack
23284             }
23285         } else {
23286             tagName = false;
23287         }
23288         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23289             return ret;
23290         }
23291         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23292             nopadtext = true;
23293         }
23294         
23295         
23296         // Traverse the tree
23297         i = 0;
23298         var currentElementChild = currentElement.childNodes.item(i);
23299         var allText = true;
23300         var innerHTML  = '';
23301         lastnode = '';
23302         while (currentElementChild) {
23303             // Formatting code (indent the tree so it looks nice on the screen)
23304             var nopad = nopadtext;
23305             if (lastnode == 'SPAN') {
23306                 nopad  = true;
23307             }
23308             // text
23309             if  (currentElementChild.nodeName == '#text') {
23310                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23311                 toadd = nopadtext ? toadd : toadd.trim();
23312                 if (!nopad && toadd.length > 80) {
23313                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23314                 }
23315                 innerHTML  += toadd;
23316                 
23317                 i++;
23318                 currentElementChild = currentElement.childNodes.item(i);
23319                 lastNode = '';
23320                 continue;
23321             }
23322             allText = false;
23323             
23324             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23325                 
23326             // Recursively traverse the tree structure of the child node
23327             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23328             lastnode = currentElementChild.nodeName;
23329             i++;
23330             currentElementChild=currentElement.childNodes.item(i);
23331         }
23332         
23333         ret += innerHTML;
23334         
23335         if (!allText) {
23336                 // The remaining code is mostly for formatting the tree
23337             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23338         }
23339         
23340         
23341         if (tagName) {
23342             ret+= "</"+tagName+">";
23343         }
23344         return ret;
23345         
23346     },
23347         
23348     applyBlacklists : function()
23349     {
23350         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23351         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23352         
23353         this.white = [];
23354         this.black = [];
23355         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23356             if (b.indexOf(tag) > -1) {
23357                 return;
23358             }
23359             this.white.push(tag);
23360             
23361         }, this);
23362         
23363         Roo.each(w, function(tag) {
23364             if (b.indexOf(tag) > -1) {
23365                 return;
23366             }
23367             if (this.white.indexOf(tag) > -1) {
23368                 return;
23369             }
23370             this.white.push(tag);
23371             
23372         }, this);
23373         
23374         
23375         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23376             if (w.indexOf(tag) > -1) {
23377                 return;
23378             }
23379             this.black.push(tag);
23380             
23381         }, this);
23382         
23383         Roo.each(b, function(tag) {
23384             if (w.indexOf(tag) > -1) {
23385                 return;
23386             }
23387             if (this.black.indexOf(tag) > -1) {
23388                 return;
23389             }
23390             this.black.push(tag);
23391             
23392         }, this);
23393         
23394         
23395         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23396         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23397         
23398         this.cwhite = [];
23399         this.cblack = [];
23400         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23401             if (b.indexOf(tag) > -1) {
23402                 return;
23403             }
23404             this.cwhite.push(tag);
23405             
23406         }, this);
23407         
23408         Roo.each(w, function(tag) {
23409             if (b.indexOf(tag) > -1) {
23410                 return;
23411             }
23412             if (this.cwhite.indexOf(tag) > -1) {
23413                 return;
23414             }
23415             this.cwhite.push(tag);
23416             
23417         }, this);
23418         
23419         
23420         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23421             if (w.indexOf(tag) > -1) {
23422                 return;
23423             }
23424             this.cblack.push(tag);
23425             
23426         }, this);
23427         
23428         Roo.each(b, function(tag) {
23429             if (w.indexOf(tag) > -1) {
23430                 return;
23431             }
23432             if (this.cblack.indexOf(tag) > -1) {
23433                 return;
23434             }
23435             this.cblack.push(tag);
23436             
23437         }, this);
23438     },
23439     
23440     setStylesheets : function(stylesheets)
23441     {
23442         if(typeof(stylesheets) == 'string'){
23443             Roo.get(this.iframe.contentDocument.head).createChild({
23444                 tag : 'link',
23445                 rel : 'stylesheet',
23446                 type : 'text/css',
23447                 href : stylesheets
23448             });
23449             
23450             return;
23451         }
23452         var _this = this;
23453      
23454         Roo.each(stylesheets, function(s) {
23455             if(!s.length){
23456                 return;
23457             }
23458             
23459             Roo.get(_this.iframe.contentDocument.head).createChild({
23460                 tag : 'link',
23461                 rel : 'stylesheet',
23462                 type : 'text/css',
23463                 href : s
23464             });
23465         });
23466
23467         
23468     },
23469     
23470     removeStylesheets : function()
23471     {
23472         var _this = this;
23473         
23474         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23475             s.remove();
23476         });
23477     },
23478     
23479     setStyle : function(style)
23480     {
23481         Roo.get(this.iframe.contentDocument.head).createChild({
23482             tag : 'style',
23483             type : 'text/css',
23484             html : style
23485         });
23486
23487         return;
23488     }
23489     
23490     // hide stuff that is not compatible
23491     /**
23492      * @event blur
23493      * @hide
23494      */
23495     /**
23496      * @event change
23497      * @hide
23498      */
23499     /**
23500      * @event focus
23501      * @hide
23502      */
23503     /**
23504      * @event specialkey
23505      * @hide
23506      */
23507     /**
23508      * @cfg {String} fieldClass @hide
23509      */
23510     /**
23511      * @cfg {String} focusClass @hide
23512      */
23513     /**
23514      * @cfg {String} autoCreate @hide
23515      */
23516     /**
23517      * @cfg {String} inputType @hide
23518      */
23519     /**
23520      * @cfg {String} invalidClass @hide
23521      */
23522     /**
23523      * @cfg {String} invalidText @hide
23524      */
23525     /**
23526      * @cfg {String} msgFx @hide
23527      */
23528     /**
23529      * @cfg {String} validateOnBlur @hide
23530      */
23531 });
23532
23533 Roo.HtmlEditorCore.white = [
23534         'area', 'br', 'img', 'input', 'hr', 'wbr',
23535         
23536        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23537        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23538        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23539        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23540        'table',   'ul',         'xmp', 
23541        
23542        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23543       'thead',   'tr', 
23544      
23545       'dir', 'menu', 'ol', 'ul', 'dl',
23546        
23547       'embed',  'object'
23548 ];
23549
23550
23551 Roo.HtmlEditorCore.black = [
23552     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23553         'applet', // 
23554         'base',   'basefont', 'bgsound', 'blink',  'body', 
23555         'frame',  'frameset', 'head',    'html',   'ilayer', 
23556         'iframe', 'layer',  'link',     'meta',    'object',   
23557         'script', 'style' ,'title',  'xml' // clean later..
23558 ];
23559 Roo.HtmlEditorCore.clean = [
23560     'script', 'style', 'title', 'xml'
23561 ];
23562 Roo.HtmlEditorCore.remove = [
23563     'font'
23564 ];
23565 // attributes..
23566
23567 Roo.HtmlEditorCore.ablack = [
23568     'on'
23569 ];
23570     
23571 Roo.HtmlEditorCore.aclean = [ 
23572     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23573 ];
23574
23575 // protocols..
23576 Roo.HtmlEditorCore.pwhite= [
23577         'http',  'https',  'mailto'
23578 ];
23579
23580 // white listed style attributes.
23581 Roo.HtmlEditorCore.cwhite= [
23582       //  'text-align', /// default is to allow most things..
23583       
23584          
23585 //        'font-size'//??
23586 ];
23587
23588 // black listed style attributes.
23589 Roo.HtmlEditorCore.cblack= [
23590       //  'font-size' -- this can be set by the project 
23591 ];
23592
23593
23594 Roo.HtmlEditorCore.swapCodes   =[ 
23595     [    8211, "--" ], 
23596     [    8212, "--" ], 
23597     [    8216,  "'" ],  
23598     [    8217, "'" ],  
23599     [    8220, '"' ],  
23600     [    8221, '"' ],  
23601     [    8226, "*" ],  
23602     [    8230, "..." ]
23603 ]; 
23604
23605     /*
23606  * - LGPL
23607  *
23608  * HtmlEditor
23609  * 
23610  */
23611
23612 /**
23613  * @class Roo.bootstrap.HtmlEditor
23614  * @extends Roo.bootstrap.TextArea
23615  * Bootstrap HtmlEditor class
23616
23617  * @constructor
23618  * Create a new HtmlEditor
23619  * @param {Object} config The config object
23620  */
23621
23622 Roo.bootstrap.HtmlEditor = function(config){
23623     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23624     if (!this.toolbars) {
23625         this.toolbars = [];
23626     }
23627     
23628     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23629     this.addEvents({
23630             /**
23631              * @event initialize
23632              * Fires when the editor is fully initialized (including the iframe)
23633              * @param {HtmlEditor} this
23634              */
23635             initialize: true,
23636             /**
23637              * @event activate
23638              * Fires when the editor is first receives the focus. Any insertion must wait
23639              * until after this event.
23640              * @param {HtmlEditor} this
23641              */
23642             activate: true,
23643              /**
23644              * @event beforesync
23645              * Fires before the textarea is updated with content from the editor iframe. Return false
23646              * to cancel the sync.
23647              * @param {HtmlEditor} this
23648              * @param {String} html
23649              */
23650             beforesync: true,
23651              /**
23652              * @event beforepush
23653              * Fires before the iframe editor is updated with content from the textarea. Return false
23654              * to cancel the push.
23655              * @param {HtmlEditor} this
23656              * @param {String} html
23657              */
23658             beforepush: true,
23659              /**
23660              * @event sync
23661              * Fires when the textarea is updated with content from the editor iframe.
23662              * @param {HtmlEditor} this
23663              * @param {String} html
23664              */
23665             sync: true,
23666              /**
23667              * @event push
23668              * Fires when the iframe editor is updated with content from the textarea.
23669              * @param {HtmlEditor} this
23670              * @param {String} html
23671              */
23672             push: true,
23673              /**
23674              * @event editmodechange
23675              * Fires when the editor switches edit modes
23676              * @param {HtmlEditor} this
23677              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23678              */
23679             editmodechange: true,
23680             /**
23681              * @event editorevent
23682              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23683              * @param {HtmlEditor} this
23684              */
23685             editorevent: true,
23686             /**
23687              * @event firstfocus
23688              * Fires when on first focus - needed by toolbars..
23689              * @param {HtmlEditor} this
23690              */
23691             firstfocus: true,
23692             /**
23693              * @event autosave
23694              * Auto save the htmlEditor value as a file into Events
23695              * @param {HtmlEditor} this
23696              */
23697             autosave: true,
23698             /**
23699              * @event savedpreview
23700              * preview the saved version of htmlEditor
23701              * @param {HtmlEditor} this
23702              */
23703             savedpreview: true
23704         });
23705 };
23706
23707
23708 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23709     
23710     
23711       /**
23712      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23713      */
23714     toolbars : false,
23715     
23716      /**
23717     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23718     */
23719     btns : [],
23720    
23721      /**
23722      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23723      *                        Roo.resizable.
23724      */
23725     resizable : false,
23726      /**
23727      * @cfg {Number} height (in pixels)
23728      */   
23729     height: 300,
23730    /**
23731      * @cfg {Number} width (in pixels)
23732      */   
23733     width: false,
23734     
23735     /**
23736      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23737      * 
23738      */
23739     stylesheets: false,
23740     
23741     // id of frame..
23742     frameId: false,
23743     
23744     // private properties
23745     validationEvent : false,
23746     deferHeight: true,
23747     initialized : false,
23748     activated : false,
23749     
23750     onFocus : Roo.emptyFn,
23751     iframePad:3,
23752     hideMode:'offsets',
23753     
23754     tbContainer : false,
23755     
23756     bodyCls : '',
23757     
23758     toolbarContainer :function() {
23759         return this.wrap.select('.x-html-editor-tb',true).first();
23760     },
23761
23762     /**
23763      * Protected method that will not generally be called directly. It
23764      * is called when the editor creates its toolbar. Override this method if you need to
23765      * add custom toolbar buttons.
23766      * @param {HtmlEditor} editor
23767      */
23768     createToolbar : function(){
23769         Roo.log('renewing');
23770         Roo.log("create toolbars");
23771         
23772         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23773         this.toolbars[0].render(this.toolbarContainer());
23774         
23775         return;
23776         
23777 //        if (!editor.toolbars || !editor.toolbars.length) {
23778 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23779 //        }
23780 //        
23781 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23782 //            editor.toolbars[i] = Roo.factory(
23783 //                    typeof(editor.toolbars[i]) == 'string' ?
23784 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23785 //                Roo.bootstrap.HtmlEditor);
23786 //            editor.toolbars[i].init(editor);
23787 //        }
23788     },
23789
23790      
23791     // private
23792     onRender : function(ct, position)
23793     {
23794        // Roo.log("Call onRender: " + this.xtype);
23795         var _t = this;
23796         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23797       
23798         this.wrap = this.inputEl().wrap({
23799             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23800         });
23801         
23802         this.editorcore.onRender(ct, position);
23803          
23804         if (this.resizable) {
23805             this.resizeEl = new Roo.Resizable(this.wrap, {
23806                 pinned : true,
23807                 wrap: true,
23808                 dynamic : true,
23809                 minHeight : this.height,
23810                 height: this.height,
23811                 handles : this.resizable,
23812                 width: this.width,
23813                 listeners : {
23814                     resize : function(r, w, h) {
23815                         _t.onResize(w,h); // -something
23816                     }
23817                 }
23818             });
23819             
23820         }
23821         this.createToolbar(this);
23822        
23823         
23824         if(!this.width && this.resizable){
23825             this.setSize(this.wrap.getSize());
23826         }
23827         if (this.resizeEl) {
23828             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23829             // should trigger onReize..
23830         }
23831         
23832     },
23833
23834     // private
23835     onResize : function(w, h)
23836     {
23837         Roo.log('resize: ' +w + ',' + h );
23838         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23839         var ew = false;
23840         var eh = false;
23841         
23842         if(this.inputEl() ){
23843             if(typeof w == 'number'){
23844                 var aw = w - this.wrap.getFrameWidth('lr');
23845                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23846                 ew = aw;
23847             }
23848             if(typeof h == 'number'){
23849                  var tbh = -11;  // fixme it needs to tool bar size!
23850                 for (var i =0; i < this.toolbars.length;i++) {
23851                     // fixme - ask toolbars for heights?
23852                     tbh += this.toolbars[i].el.getHeight();
23853                     //if (this.toolbars[i].footer) {
23854                     //    tbh += this.toolbars[i].footer.el.getHeight();
23855                     //}
23856                 }
23857               
23858                 
23859                 
23860                 
23861                 
23862                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23863                 ah -= 5; // knock a few pixes off for look..
23864                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23865                 var eh = ah;
23866             }
23867         }
23868         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23869         this.editorcore.onResize(ew,eh);
23870         
23871     },
23872
23873     /**
23874      * Toggles the editor between standard and source edit mode.
23875      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23876      */
23877     toggleSourceEdit : function(sourceEditMode)
23878     {
23879         this.editorcore.toggleSourceEdit(sourceEditMode);
23880         
23881         if(this.editorcore.sourceEditMode){
23882             Roo.log('editor - showing textarea');
23883             
23884 //            Roo.log('in');
23885 //            Roo.log(this.syncValue());
23886             this.syncValue();
23887             this.inputEl().removeClass(['hide', 'x-hidden']);
23888             this.inputEl().dom.removeAttribute('tabIndex');
23889             this.inputEl().focus();
23890         }else{
23891             Roo.log('editor - hiding textarea');
23892 //            Roo.log('out')
23893 //            Roo.log(this.pushValue()); 
23894             this.pushValue();
23895             
23896             this.inputEl().addClass(['hide', 'x-hidden']);
23897             this.inputEl().dom.setAttribute('tabIndex', -1);
23898             //this.deferFocus();
23899         }
23900          
23901         if(this.resizable){
23902             this.setSize(this.wrap.getSize());
23903         }
23904         
23905         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23906     },
23907  
23908     // private (for BoxComponent)
23909     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23910
23911     // private (for BoxComponent)
23912     getResizeEl : function(){
23913         return this.wrap;
23914     },
23915
23916     // private (for BoxComponent)
23917     getPositionEl : function(){
23918         return this.wrap;
23919     },
23920
23921     // private
23922     initEvents : function(){
23923         this.originalValue = this.getValue();
23924     },
23925
23926 //    /**
23927 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23928 //     * @method
23929 //     */
23930 //    markInvalid : Roo.emptyFn,
23931 //    /**
23932 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23933 //     * @method
23934 //     */
23935 //    clearInvalid : Roo.emptyFn,
23936
23937     setValue : function(v){
23938         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23939         this.editorcore.pushValue();
23940     },
23941
23942      
23943     // private
23944     deferFocus : function(){
23945         this.focus.defer(10, this);
23946     },
23947
23948     // doc'ed in Field
23949     focus : function(){
23950         this.editorcore.focus();
23951         
23952     },
23953       
23954
23955     // private
23956     onDestroy : function(){
23957         
23958         
23959         
23960         if(this.rendered){
23961             
23962             for (var i =0; i < this.toolbars.length;i++) {
23963                 // fixme - ask toolbars for heights?
23964                 this.toolbars[i].onDestroy();
23965             }
23966             
23967             this.wrap.dom.innerHTML = '';
23968             this.wrap.remove();
23969         }
23970     },
23971
23972     // private
23973     onFirstFocus : function(){
23974         //Roo.log("onFirstFocus");
23975         this.editorcore.onFirstFocus();
23976          for (var i =0; i < this.toolbars.length;i++) {
23977             this.toolbars[i].onFirstFocus();
23978         }
23979         
23980     },
23981     
23982     // private
23983     syncValue : function()
23984     {   
23985         this.editorcore.syncValue();
23986     },
23987     
23988     pushValue : function()
23989     {   
23990         this.editorcore.pushValue();
23991     }
23992      
23993     
23994     // hide stuff that is not compatible
23995     /**
23996      * @event blur
23997      * @hide
23998      */
23999     /**
24000      * @event change
24001      * @hide
24002      */
24003     /**
24004      * @event focus
24005      * @hide
24006      */
24007     /**
24008      * @event specialkey
24009      * @hide
24010      */
24011     /**
24012      * @cfg {String} fieldClass @hide
24013      */
24014     /**
24015      * @cfg {String} focusClass @hide
24016      */
24017     /**
24018      * @cfg {String} autoCreate @hide
24019      */
24020     /**
24021      * @cfg {String} inputType @hide
24022      */
24023      
24024     /**
24025      * @cfg {String} invalidText @hide
24026      */
24027     /**
24028      * @cfg {String} msgFx @hide
24029      */
24030     /**
24031      * @cfg {String} validateOnBlur @hide
24032      */
24033 });
24034  
24035     
24036    
24037    
24038    
24039       
24040 Roo.namespace('Roo.bootstrap.htmleditor');
24041 /**
24042  * @class Roo.bootstrap.HtmlEditorToolbar1
24043  * Basic Toolbar
24044  * 
24045  * Usage:
24046  *
24047  new Roo.bootstrap.HtmlEditor({
24048     ....
24049     toolbars : [
24050         new Roo.bootstrap.HtmlEditorToolbar1({
24051             disable : { fonts: 1 , format: 1, ..., ... , ...],
24052             btns : [ .... ]
24053         })
24054     }
24055      
24056  * 
24057  * @cfg {Object} disable List of elements to disable..
24058  * @cfg {Array} btns List of additional buttons.
24059  * 
24060  * 
24061  * NEEDS Extra CSS? 
24062  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24063  */
24064  
24065 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24066 {
24067     
24068     Roo.apply(this, config);
24069     
24070     // default disabled, based on 'good practice'..
24071     this.disable = this.disable || {};
24072     Roo.applyIf(this.disable, {
24073         fontSize : true,
24074         colors : true,
24075         specialElements : true
24076     });
24077     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24078     
24079     this.editor = config.editor;
24080     this.editorcore = config.editor.editorcore;
24081     
24082     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24083     
24084     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24085     // dont call parent... till later.
24086 }
24087 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
24088      
24089     bar : true,
24090     
24091     editor : false,
24092     editorcore : false,
24093     
24094     
24095     formats : [
24096         "p" ,  
24097         "h1","h2","h3","h4","h5","h6", 
24098         "pre", "code", 
24099         "abbr", "acronym", "address", "cite", "samp", "var",
24100         'div','span'
24101     ],
24102     
24103     onRender : function(ct, position)
24104     {
24105        // Roo.log("Call onRender: " + this.xtype);
24106         
24107        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24108        Roo.log(this.el);
24109        this.el.dom.style.marginBottom = '0';
24110        var _this = this;
24111        var editorcore = this.editorcore;
24112        var editor= this.editor;
24113        
24114        var children = [];
24115        var btn = function(id,cmd , toggle, handler, html){
24116        
24117             var  event = toggle ? 'toggle' : 'click';
24118        
24119             var a = {
24120                 size : 'sm',
24121                 xtype: 'Button',
24122                 xns: Roo.bootstrap,
24123                 //glyphicon : id,
24124                 fa: id,
24125                 cmd : id || cmd,
24126                 enableToggle:toggle !== false,
24127                 html : html || '',
24128                 pressed : toggle ? false : null,
24129                 listeners : {}
24130             };
24131             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24132                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24133             };
24134             children.push(a);
24135             return a;
24136        }
24137        
24138     //    var cb_box = function...
24139         
24140         var style = {
24141                 xtype: 'Button',
24142                 size : 'sm',
24143                 xns: Roo.bootstrap,
24144                 fa : 'font',
24145                 //html : 'submit'
24146                 menu : {
24147                     xtype: 'Menu',
24148                     xns: Roo.bootstrap,
24149                     items:  []
24150                 }
24151         };
24152         Roo.each(this.formats, function(f) {
24153             style.menu.items.push({
24154                 xtype :'MenuItem',
24155                 xns: Roo.bootstrap,
24156                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24157                 tagname : f,
24158                 listeners : {
24159                     click : function()
24160                     {
24161                         editorcore.insertTag(this.tagname);
24162                         editor.focus();
24163                     }
24164                 }
24165                 
24166             });
24167         });
24168         children.push(style);   
24169         
24170         btn('bold',false,true);
24171         btn('italic',false,true);
24172         btn('align-left', 'justifyleft',true);
24173         btn('align-center', 'justifycenter',true);
24174         btn('align-right' , 'justifyright',true);
24175         btn('link', false, false, function(btn) {
24176             //Roo.log("create link?");
24177             var url = prompt(this.createLinkText, this.defaultLinkValue);
24178             if(url && url != 'http:/'+'/'){
24179                 this.editorcore.relayCmd('createlink', url);
24180             }
24181         }),
24182         btn('list','insertunorderedlist',true);
24183         btn('pencil', false,true, function(btn){
24184                 Roo.log(this);
24185                 this.toggleSourceEdit(btn.pressed);
24186         });
24187         
24188         if (this.editor.btns.length > 0) {
24189             for (var i = 0; i<this.editor.btns.length; i++) {
24190                 children.push(this.editor.btns[i]);
24191             }
24192         }
24193         
24194         /*
24195         var cog = {
24196                 xtype: 'Button',
24197                 size : 'sm',
24198                 xns: Roo.bootstrap,
24199                 glyphicon : 'cog',
24200                 //html : 'submit'
24201                 menu : {
24202                     xtype: 'Menu',
24203                     xns: Roo.bootstrap,
24204                     items:  []
24205                 }
24206         };
24207         
24208         cog.menu.items.push({
24209             xtype :'MenuItem',
24210             xns: Roo.bootstrap,
24211             html : Clean styles,
24212             tagname : f,
24213             listeners : {
24214                 click : function()
24215                 {
24216                     editorcore.insertTag(this.tagname);
24217                     editor.focus();
24218                 }
24219             }
24220             
24221         });
24222        */
24223         
24224          
24225        this.xtype = 'NavSimplebar';
24226         
24227         for(var i=0;i< children.length;i++) {
24228             
24229             this.buttons.add(this.addxtypeChild(children[i]));
24230             
24231         }
24232         
24233         editor.on('editorevent', this.updateToolbar, this);
24234     },
24235     onBtnClick : function(id)
24236     {
24237        this.editorcore.relayCmd(id);
24238        this.editorcore.focus();
24239     },
24240     
24241     /**
24242      * Protected method that will not generally be called directly. It triggers
24243      * a toolbar update by reading the markup state of the current selection in the editor.
24244      */
24245     updateToolbar: function(){
24246
24247         if(!this.editorcore.activated){
24248             this.editor.onFirstFocus(); // is this neeed?
24249             return;
24250         }
24251
24252         var btns = this.buttons; 
24253         var doc = this.editorcore.doc;
24254         btns.get('bold').setActive(doc.queryCommandState('bold'));
24255         btns.get('italic').setActive(doc.queryCommandState('italic'));
24256         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24257         
24258         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24259         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24260         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24261         
24262         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24263         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24264          /*
24265         
24266         var ans = this.editorcore.getAllAncestors();
24267         if (this.formatCombo) {
24268             
24269             
24270             var store = this.formatCombo.store;
24271             this.formatCombo.setValue("");
24272             for (var i =0; i < ans.length;i++) {
24273                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24274                     // select it..
24275                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24276                     break;
24277                 }
24278             }
24279         }
24280         
24281         
24282         
24283         // hides menus... - so this cant be on a menu...
24284         Roo.bootstrap.MenuMgr.hideAll();
24285         */
24286         Roo.bootstrap.MenuMgr.hideAll();
24287         //this.editorsyncValue();
24288     },
24289     onFirstFocus: function() {
24290         this.buttons.each(function(item){
24291            item.enable();
24292         });
24293     },
24294     toggleSourceEdit : function(sourceEditMode){
24295         
24296           
24297         if(sourceEditMode){
24298             Roo.log("disabling buttons");
24299            this.buttons.each( function(item){
24300                 if(item.cmd != 'pencil'){
24301                     item.disable();
24302                 }
24303             });
24304           
24305         }else{
24306             Roo.log("enabling buttons");
24307             if(this.editorcore.initialized){
24308                 this.buttons.each( function(item){
24309                     item.enable();
24310                 });
24311             }
24312             
24313         }
24314         Roo.log("calling toggole on editor");
24315         // tell the editor that it's been pressed..
24316         this.editor.toggleSourceEdit(sourceEditMode);
24317        
24318     }
24319 });
24320
24321
24322
24323
24324
24325 /**
24326  * @class Roo.bootstrap.Table.AbstractSelectionModel
24327  * @extends Roo.util.Observable
24328  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24329  * implemented by descendant classes.  This class should not be directly instantiated.
24330  * @constructor
24331  */
24332 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24333     this.locked = false;
24334     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24335 };
24336
24337
24338 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24339     /** @ignore Called by the grid automatically. Do not call directly. */
24340     init : function(grid){
24341         this.grid = grid;
24342         this.initEvents();
24343     },
24344
24345     /**
24346      * Locks the selections.
24347      */
24348     lock : function(){
24349         this.locked = true;
24350     },
24351
24352     /**
24353      * Unlocks the selections.
24354      */
24355     unlock : function(){
24356         this.locked = false;
24357     },
24358
24359     /**
24360      * Returns true if the selections are locked.
24361      * @return {Boolean}
24362      */
24363     isLocked : function(){
24364         return this.locked;
24365     }
24366 });
24367 /**
24368  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24369  * @class Roo.bootstrap.Table.RowSelectionModel
24370  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24371  * It supports multiple selections and keyboard selection/navigation. 
24372  * @constructor
24373  * @param {Object} config
24374  */
24375
24376 Roo.bootstrap.Table.RowSelectionModel = function(config){
24377     Roo.apply(this, config);
24378     this.selections = new Roo.util.MixedCollection(false, function(o){
24379         return o.id;
24380     });
24381
24382     this.last = false;
24383     this.lastActive = false;
24384
24385     this.addEvents({
24386         /**
24387              * @event selectionchange
24388              * Fires when the selection changes
24389              * @param {SelectionModel} this
24390              */
24391             "selectionchange" : true,
24392         /**
24393              * @event afterselectionchange
24394              * Fires after the selection changes (eg. by key press or clicking)
24395              * @param {SelectionModel} this
24396              */
24397             "afterselectionchange" : true,
24398         /**
24399              * @event beforerowselect
24400              * Fires when a row is selected being selected, return false to cancel.
24401              * @param {SelectionModel} this
24402              * @param {Number} rowIndex The selected index
24403              * @param {Boolean} keepExisting False if other selections will be cleared
24404              */
24405             "beforerowselect" : true,
24406         /**
24407              * @event rowselect
24408              * Fires when a row is selected.
24409              * @param {SelectionModel} this
24410              * @param {Number} rowIndex The selected index
24411              * @param {Roo.data.Record} r The record
24412              */
24413             "rowselect" : true,
24414         /**
24415              * @event rowdeselect
24416              * Fires when a row is deselected.
24417              * @param {SelectionModel} this
24418              * @param {Number} rowIndex The selected index
24419              */
24420         "rowdeselect" : true
24421     });
24422     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24423     this.locked = false;
24424  };
24425
24426 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24427     /**
24428      * @cfg {Boolean} singleSelect
24429      * True to allow selection of only one row at a time (defaults to false)
24430      */
24431     singleSelect : false,
24432
24433     // private
24434     initEvents : function()
24435     {
24436
24437         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24438         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24439         //}else{ // allow click to work like normal
24440          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24441         //}
24442         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24443         this.grid.on("rowclick", this.handleMouseDown, this);
24444         
24445         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24446             "up" : function(e){
24447                 if(!e.shiftKey){
24448                     this.selectPrevious(e.shiftKey);
24449                 }else if(this.last !== false && this.lastActive !== false){
24450                     var last = this.last;
24451                     this.selectRange(this.last,  this.lastActive-1);
24452                     this.grid.getView().focusRow(this.lastActive);
24453                     if(last !== false){
24454                         this.last = last;
24455                     }
24456                 }else{
24457                     this.selectFirstRow();
24458                 }
24459                 this.fireEvent("afterselectionchange", this);
24460             },
24461             "down" : function(e){
24462                 if(!e.shiftKey){
24463                     this.selectNext(e.shiftKey);
24464                 }else if(this.last !== false && this.lastActive !== false){
24465                     var last = this.last;
24466                     this.selectRange(this.last,  this.lastActive+1);
24467                     this.grid.getView().focusRow(this.lastActive);
24468                     if(last !== false){
24469                         this.last = last;
24470                     }
24471                 }else{
24472                     this.selectFirstRow();
24473                 }
24474                 this.fireEvent("afterselectionchange", this);
24475             },
24476             scope: this
24477         });
24478         this.grid.store.on('load', function(){
24479             this.selections.clear();
24480         },this);
24481         /*
24482         var view = this.grid.view;
24483         view.on("refresh", this.onRefresh, this);
24484         view.on("rowupdated", this.onRowUpdated, this);
24485         view.on("rowremoved", this.onRemove, this);
24486         */
24487     },
24488
24489     // private
24490     onRefresh : function()
24491     {
24492         var ds = this.grid.store, i, v = this.grid.view;
24493         var s = this.selections;
24494         s.each(function(r){
24495             if((i = ds.indexOfId(r.id)) != -1){
24496                 v.onRowSelect(i);
24497             }else{
24498                 s.remove(r);
24499             }
24500         });
24501     },
24502
24503     // private
24504     onRemove : function(v, index, r){
24505         this.selections.remove(r);
24506     },
24507
24508     // private
24509     onRowUpdated : function(v, index, r){
24510         if(this.isSelected(r)){
24511             v.onRowSelect(index);
24512         }
24513     },
24514
24515     /**
24516      * Select records.
24517      * @param {Array} records The records to select
24518      * @param {Boolean} keepExisting (optional) True to keep existing selections
24519      */
24520     selectRecords : function(records, keepExisting)
24521     {
24522         if(!keepExisting){
24523             this.clearSelections();
24524         }
24525             var ds = this.grid.store;
24526         for(var i = 0, len = records.length; i < len; i++){
24527             this.selectRow(ds.indexOf(records[i]), true);
24528         }
24529     },
24530
24531     /**
24532      * Gets the number of selected rows.
24533      * @return {Number}
24534      */
24535     getCount : function(){
24536         return this.selections.length;
24537     },
24538
24539     /**
24540      * Selects the first row in the grid.
24541      */
24542     selectFirstRow : function(){
24543         this.selectRow(0);
24544     },
24545
24546     /**
24547      * Select the last row.
24548      * @param {Boolean} keepExisting (optional) True to keep existing selections
24549      */
24550     selectLastRow : function(keepExisting){
24551         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24552         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24553     },
24554
24555     /**
24556      * Selects the row immediately following the last selected row.
24557      * @param {Boolean} keepExisting (optional) True to keep existing selections
24558      */
24559     selectNext : function(keepExisting)
24560     {
24561             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24562             this.selectRow(this.last+1, keepExisting);
24563             this.grid.getView().focusRow(this.last);
24564         }
24565     },
24566
24567     /**
24568      * Selects the row that precedes the last selected row.
24569      * @param {Boolean} keepExisting (optional) True to keep existing selections
24570      */
24571     selectPrevious : function(keepExisting){
24572         if(this.last){
24573             this.selectRow(this.last-1, keepExisting);
24574             this.grid.getView().focusRow(this.last);
24575         }
24576     },
24577
24578     /**
24579      * Returns the selected records
24580      * @return {Array} Array of selected records
24581      */
24582     getSelections : function(){
24583         return [].concat(this.selections.items);
24584     },
24585
24586     /**
24587      * Returns the first selected record.
24588      * @return {Record}
24589      */
24590     getSelected : function(){
24591         return this.selections.itemAt(0);
24592     },
24593
24594
24595     /**
24596      * Clears all selections.
24597      */
24598     clearSelections : function(fast)
24599     {
24600         if(this.locked) {
24601             return;
24602         }
24603         if(fast !== true){
24604                 var ds = this.grid.store;
24605             var s = this.selections;
24606             s.each(function(r){
24607                 this.deselectRow(ds.indexOfId(r.id));
24608             }, this);
24609             s.clear();
24610         }else{
24611             this.selections.clear();
24612         }
24613         this.last = false;
24614     },
24615
24616
24617     /**
24618      * Selects all rows.
24619      */
24620     selectAll : function(){
24621         if(this.locked) {
24622             return;
24623         }
24624         this.selections.clear();
24625         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24626             this.selectRow(i, true);
24627         }
24628     },
24629
24630     /**
24631      * Returns True if there is a selection.
24632      * @return {Boolean}
24633      */
24634     hasSelection : function(){
24635         return this.selections.length > 0;
24636     },
24637
24638     /**
24639      * Returns True if the specified row is selected.
24640      * @param {Number/Record} record The record or index of the record to check
24641      * @return {Boolean}
24642      */
24643     isSelected : function(index){
24644             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24645         return (r && this.selections.key(r.id) ? true : false);
24646     },
24647
24648     /**
24649      * Returns True if the specified record id is selected.
24650      * @param {String} id The id of record to check
24651      * @return {Boolean}
24652      */
24653     isIdSelected : function(id){
24654         return (this.selections.key(id) ? true : false);
24655     },
24656
24657
24658     // private
24659     handleMouseDBClick : function(e, t){
24660         
24661     },
24662     // private
24663     handleMouseDown : function(e, t)
24664     {
24665             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24666         if(this.isLocked() || rowIndex < 0 ){
24667             return;
24668         };
24669         if(e.shiftKey && this.last !== false){
24670             var last = this.last;
24671             this.selectRange(last, rowIndex, e.ctrlKey);
24672             this.last = last; // reset the last
24673             t.focus();
24674     
24675         }else{
24676             var isSelected = this.isSelected(rowIndex);
24677             //Roo.log("select row:" + rowIndex);
24678             if(isSelected){
24679                 this.deselectRow(rowIndex);
24680             } else {
24681                         this.selectRow(rowIndex, true);
24682             }
24683     
24684             /*
24685                 if(e.button !== 0 && isSelected){
24686                 alert('rowIndex 2: ' + rowIndex);
24687                     view.focusRow(rowIndex);
24688                 }else if(e.ctrlKey && isSelected){
24689                     this.deselectRow(rowIndex);
24690                 }else if(!isSelected){
24691                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24692                     view.focusRow(rowIndex);
24693                 }
24694             */
24695         }
24696         this.fireEvent("afterselectionchange", this);
24697     },
24698     // private
24699     handleDragableRowClick :  function(grid, rowIndex, e) 
24700     {
24701         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24702             this.selectRow(rowIndex, false);
24703             grid.view.focusRow(rowIndex);
24704              this.fireEvent("afterselectionchange", this);
24705         }
24706     },
24707     
24708     /**
24709      * Selects multiple rows.
24710      * @param {Array} rows Array of the indexes of the row to select
24711      * @param {Boolean} keepExisting (optional) True to keep existing selections
24712      */
24713     selectRows : function(rows, keepExisting){
24714         if(!keepExisting){
24715             this.clearSelections();
24716         }
24717         for(var i = 0, len = rows.length; i < len; i++){
24718             this.selectRow(rows[i], true);
24719         }
24720     },
24721
24722     /**
24723      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24724      * @param {Number} startRow The index of the first row in the range
24725      * @param {Number} endRow The index of the last row in the range
24726      * @param {Boolean} keepExisting (optional) True to retain existing selections
24727      */
24728     selectRange : function(startRow, endRow, keepExisting){
24729         if(this.locked) {
24730             return;
24731         }
24732         if(!keepExisting){
24733             this.clearSelections();
24734         }
24735         if(startRow <= endRow){
24736             for(var i = startRow; i <= endRow; i++){
24737                 this.selectRow(i, true);
24738             }
24739         }else{
24740             for(var i = startRow; i >= endRow; i--){
24741                 this.selectRow(i, true);
24742             }
24743         }
24744     },
24745
24746     /**
24747      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24748      * @param {Number} startRow The index of the first row in the range
24749      * @param {Number} endRow The index of the last row in the range
24750      */
24751     deselectRange : function(startRow, endRow, preventViewNotify){
24752         if(this.locked) {
24753             return;
24754         }
24755         for(var i = startRow; i <= endRow; i++){
24756             this.deselectRow(i, preventViewNotify);
24757         }
24758     },
24759
24760     /**
24761      * Selects a row.
24762      * @param {Number} row The index of the row to select
24763      * @param {Boolean} keepExisting (optional) True to keep existing selections
24764      */
24765     selectRow : function(index, keepExisting, preventViewNotify)
24766     {
24767             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24768             return;
24769         }
24770         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24771             if(!keepExisting || this.singleSelect){
24772                 this.clearSelections();
24773             }
24774             
24775             var r = this.grid.store.getAt(index);
24776             //console.log('selectRow - record id :' + r.id);
24777             
24778             this.selections.add(r);
24779             this.last = this.lastActive = index;
24780             if(!preventViewNotify){
24781                 var proxy = new Roo.Element(
24782                                 this.grid.getRowDom(index)
24783                 );
24784                 proxy.addClass('bg-info info');
24785             }
24786             this.fireEvent("rowselect", this, index, r);
24787             this.fireEvent("selectionchange", this);
24788         }
24789     },
24790
24791     /**
24792      * Deselects a row.
24793      * @param {Number} row The index of the row to deselect
24794      */
24795     deselectRow : function(index, preventViewNotify)
24796     {
24797         if(this.locked) {
24798             return;
24799         }
24800         if(this.last == index){
24801             this.last = false;
24802         }
24803         if(this.lastActive == index){
24804             this.lastActive = false;
24805         }
24806         
24807         var r = this.grid.store.getAt(index);
24808         if (!r) {
24809             return;
24810         }
24811         
24812         this.selections.remove(r);
24813         //.console.log('deselectRow - record id :' + r.id);
24814         if(!preventViewNotify){
24815         
24816             var proxy = new Roo.Element(
24817                 this.grid.getRowDom(index)
24818             );
24819             proxy.removeClass('bg-info info');
24820         }
24821         this.fireEvent("rowdeselect", this, index);
24822         this.fireEvent("selectionchange", this);
24823     },
24824
24825     // private
24826     restoreLast : function(){
24827         if(this._last){
24828             this.last = this._last;
24829         }
24830     },
24831
24832     // private
24833     acceptsNav : function(row, col, cm){
24834         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24835     },
24836
24837     // private
24838     onEditorKey : function(field, e){
24839         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24840         if(k == e.TAB){
24841             e.stopEvent();
24842             ed.completeEdit();
24843             if(e.shiftKey){
24844                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24845             }else{
24846                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24847             }
24848         }else if(k == e.ENTER && !e.ctrlKey){
24849             e.stopEvent();
24850             ed.completeEdit();
24851             if(e.shiftKey){
24852                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24853             }else{
24854                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24855             }
24856         }else if(k == e.ESC){
24857             ed.cancelEdit();
24858         }
24859         if(newCell){
24860             g.startEditing(newCell[0], newCell[1]);
24861         }
24862     }
24863 });
24864 /*
24865  * Based on:
24866  * Ext JS Library 1.1.1
24867  * Copyright(c) 2006-2007, Ext JS, LLC.
24868  *
24869  * Originally Released Under LGPL - original licence link has changed is not relivant.
24870  *
24871  * Fork - LGPL
24872  * <script type="text/javascript">
24873  */
24874  
24875 /**
24876  * @class Roo.bootstrap.PagingToolbar
24877  * @extends Roo.bootstrap.NavSimplebar
24878  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24879  * @constructor
24880  * Create a new PagingToolbar
24881  * @param {Object} config The config object
24882  * @param {Roo.data.Store} store
24883  */
24884 Roo.bootstrap.PagingToolbar = function(config)
24885 {
24886     // old args format still supported... - xtype is prefered..
24887         // created from xtype...
24888     
24889     this.ds = config.dataSource;
24890     
24891     if (config.store && !this.ds) {
24892         this.store= Roo.factory(config.store, Roo.data);
24893         this.ds = this.store;
24894         this.ds.xmodule = this.xmodule || false;
24895     }
24896     
24897     this.toolbarItems = [];
24898     if (config.items) {
24899         this.toolbarItems = config.items;
24900     }
24901     
24902     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24903     
24904     this.cursor = 0;
24905     
24906     if (this.ds) { 
24907         this.bind(this.ds);
24908     }
24909     
24910     if (Roo.bootstrap.version == 4) {
24911         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24912     } else {
24913         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24914     }
24915     
24916 };
24917
24918 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24919     /**
24920      * @cfg {Roo.data.Store} dataSource
24921      * The underlying data store providing the paged data
24922      */
24923     /**
24924      * @cfg {String/HTMLElement/Element} container
24925      * container The id or element that will contain the toolbar
24926      */
24927     /**
24928      * @cfg {Boolean} displayInfo
24929      * True to display the displayMsg (defaults to false)
24930      */
24931     /**
24932      * @cfg {Number} pageSize
24933      * The number of records to display per page (defaults to 20)
24934      */
24935     pageSize: 20,
24936     /**
24937      * @cfg {String} displayMsg
24938      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24939      */
24940     displayMsg : 'Displaying {0} - {1} of {2}',
24941     /**
24942      * @cfg {String} emptyMsg
24943      * The message to display when no records are found (defaults to "No data to display")
24944      */
24945     emptyMsg : 'No data to display',
24946     /**
24947      * Customizable piece of the default paging text (defaults to "Page")
24948      * @type String
24949      */
24950     beforePageText : "Page",
24951     /**
24952      * Customizable piece of the default paging text (defaults to "of %0")
24953      * @type String
24954      */
24955     afterPageText : "of {0}",
24956     /**
24957      * Customizable piece of the default paging text (defaults to "First Page")
24958      * @type String
24959      */
24960     firstText : "First Page",
24961     /**
24962      * Customizable piece of the default paging text (defaults to "Previous Page")
24963      * @type String
24964      */
24965     prevText : "Previous Page",
24966     /**
24967      * Customizable piece of the default paging text (defaults to "Next Page")
24968      * @type String
24969      */
24970     nextText : "Next Page",
24971     /**
24972      * Customizable piece of the default paging text (defaults to "Last Page")
24973      * @type String
24974      */
24975     lastText : "Last Page",
24976     /**
24977      * Customizable piece of the default paging text (defaults to "Refresh")
24978      * @type String
24979      */
24980     refreshText : "Refresh",
24981
24982     buttons : false,
24983     // private
24984     onRender : function(ct, position) 
24985     {
24986         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24987         this.navgroup.parentId = this.id;
24988         this.navgroup.onRender(this.el, null);
24989         // add the buttons to the navgroup
24990         
24991         if(this.displayInfo){
24992             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24993             this.displayEl = this.el.select('.x-paging-info', true).first();
24994 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24995 //            this.displayEl = navel.el.select('span',true).first();
24996         }
24997         
24998         var _this = this;
24999         
25000         if(this.buttons){
25001             Roo.each(_this.buttons, function(e){ // this might need to use render????
25002                Roo.factory(e).render(_this.el);
25003             });
25004         }
25005             
25006         Roo.each(_this.toolbarItems, function(e) {
25007             _this.navgroup.addItem(e);
25008         });
25009         
25010         
25011         this.first = this.navgroup.addItem({
25012             tooltip: this.firstText,
25013             cls: "prev btn-outline-secondary",
25014             html : ' <i class="fa fa-step-backward"></i>',
25015             disabled: true,
25016             preventDefault: true,
25017             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25018         });
25019         
25020         this.prev =  this.navgroup.addItem({
25021             tooltip: this.prevText,
25022             cls: "prev btn-outline-secondary",
25023             html : ' <i class="fa fa-backward"></i>',
25024             disabled: true,
25025             preventDefault: true,
25026             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
25027         });
25028     //this.addSeparator();
25029         
25030         
25031         var field = this.navgroup.addItem( {
25032             tagtype : 'span',
25033             cls : 'x-paging-position  btn-outline-secondary',
25034              disabled: true,
25035             html : this.beforePageText  +
25036                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25037                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
25038          } ); //?? escaped?
25039         
25040         this.field = field.el.select('input', true).first();
25041         this.field.on("keydown", this.onPagingKeydown, this);
25042         this.field.on("focus", function(){this.dom.select();});
25043     
25044     
25045         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
25046         //this.field.setHeight(18);
25047         //this.addSeparator();
25048         this.next = this.navgroup.addItem({
25049             tooltip: this.nextText,
25050             cls: "next btn-outline-secondary",
25051             html : ' <i class="fa fa-forward"></i>',
25052             disabled: true,
25053             preventDefault: true,
25054             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25055         });
25056         this.last = this.navgroup.addItem({
25057             tooltip: this.lastText,
25058             html : ' <i class="fa fa-step-forward"></i>',
25059             cls: "next btn-outline-secondary",
25060             disabled: true,
25061             preventDefault: true,
25062             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25063         });
25064     //this.addSeparator();
25065         this.loading = this.navgroup.addItem({
25066             tooltip: this.refreshText,
25067             cls: "btn-outline-secondary",
25068             html : ' <i class="fa fa-refresh"></i>',
25069             preventDefault: true,
25070             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25071         });
25072         
25073     },
25074
25075     // private
25076     updateInfo : function(){
25077         if(this.displayEl){
25078             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25079             var msg = count == 0 ?
25080                 this.emptyMsg :
25081                 String.format(
25082                     this.displayMsg,
25083                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25084                 );
25085             this.displayEl.update(msg);
25086         }
25087     },
25088
25089     // private
25090     onLoad : function(ds, r, o)
25091     {
25092         this.cursor = o.params.start ? o.params.start : 0;
25093         
25094         var d = this.getPageData(),
25095             ap = d.activePage,
25096             ps = d.pages;
25097         
25098         
25099         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25100         this.field.dom.value = ap;
25101         this.first.setDisabled(ap == 1);
25102         this.prev.setDisabled(ap == 1);
25103         this.next.setDisabled(ap == ps);
25104         this.last.setDisabled(ap == ps);
25105         this.loading.enable();
25106         this.updateInfo();
25107     },
25108
25109     // private
25110     getPageData : function(){
25111         var total = this.ds.getTotalCount();
25112         return {
25113             total : total,
25114             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25115             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25116         };
25117     },
25118
25119     // private
25120     onLoadError : function(){
25121         this.loading.enable();
25122     },
25123
25124     // private
25125     onPagingKeydown : function(e){
25126         var k = e.getKey();
25127         var d = this.getPageData();
25128         if(k == e.RETURN){
25129             var v = this.field.dom.value, pageNum;
25130             if(!v || isNaN(pageNum = parseInt(v, 10))){
25131                 this.field.dom.value = d.activePage;
25132                 return;
25133             }
25134             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25135             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25136             e.stopEvent();
25137         }
25138         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))
25139         {
25140           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25141           this.field.dom.value = pageNum;
25142           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25143           e.stopEvent();
25144         }
25145         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25146         {
25147           var v = this.field.dom.value, pageNum; 
25148           var increment = (e.shiftKey) ? 10 : 1;
25149           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25150                 increment *= -1;
25151           }
25152           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25153             this.field.dom.value = d.activePage;
25154             return;
25155           }
25156           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25157           {
25158             this.field.dom.value = parseInt(v, 10) + increment;
25159             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25160             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25161           }
25162           e.stopEvent();
25163         }
25164     },
25165
25166     // private
25167     beforeLoad : function(){
25168         if(this.loading){
25169             this.loading.disable();
25170         }
25171     },
25172
25173     // private
25174     onClick : function(which){
25175         
25176         var ds = this.ds;
25177         if (!ds) {
25178             return;
25179         }
25180         
25181         switch(which){
25182             case "first":
25183                 ds.load({params:{start: 0, limit: this.pageSize}});
25184             break;
25185             case "prev":
25186                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25187             break;
25188             case "next":
25189                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25190             break;
25191             case "last":
25192                 var total = ds.getTotalCount();
25193                 var extra = total % this.pageSize;
25194                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25195                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25196             break;
25197             case "refresh":
25198                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25199             break;
25200         }
25201     },
25202
25203     /**
25204      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25205      * @param {Roo.data.Store} store The data store to unbind
25206      */
25207     unbind : function(ds){
25208         ds.un("beforeload", this.beforeLoad, this);
25209         ds.un("load", this.onLoad, this);
25210         ds.un("loadexception", this.onLoadError, this);
25211         ds.un("remove", this.updateInfo, this);
25212         ds.un("add", this.updateInfo, this);
25213         this.ds = undefined;
25214     },
25215
25216     /**
25217      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25218      * @param {Roo.data.Store} store The data store to bind
25219      */
25220     bind : function(ds){
25221         ds.on("beforeload", this.beforeLoad, this);
25222         ds.on("load", this.onLoad, this);
25223         ds.on("loadexception", this.onLoadError, this);
25224         ds.on("remove", this.updateInfo, this);
25225         ds.on("add", this.updateInfo, this);
25226         this.ds = ds;
25227     }
25228 });/*
25229  * - LGPL
25230  *
25231  * element
25232  * 
25233  */
25234
25235 /**
25236  * @class Roo.bootstrap.MessageBar
25237  * @extends Roo.bootstrap.Component
25238  * Bootstrap MessageBar class
25239  * @cfg {String} html contents of the MessageBar
25240  * @cfg {String} weight (info | success | warning | danger) default info
25241  * @cfg {String} beforeClass insert the bar before the given class
25242  * @cfg {Boolean} closable (true | false) default false
25243  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25244  * 
25245  * @constructor
25246  * Create a new Element
25247  * @param {Object} config The config object
25248  */
25249
25250 Roo.bootstrap.MessageBar = function(config){
25251     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25252 };
25253
25254 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25255     
25256     html: '',
25257     weight: 'info',
25258     closable: false,
25259     fixed: false,
25260     beforeClass: 'bootstrap-sticky-wrap',
25261     
25262     getAutoCreate : function(){
25263         
25264         var cfg = {
25265             tag: 'div',
25266             cls: 'alert alert-dismissable alert-' + this.weight,
25267             cn: [
25268                 {
25269                     tag: 'span',
25270                     cls: 'message',
25271                     html: this.html || ''
25272                 }
25273             ]
25274         };
25275         
25276         if(this.fixed){
25277             cfg.cls += ' alert-messages-fixed';
25278         }
25279         
25280         if(this.closable){
25281             cfg.cn.push({
25282                 tag: 'button',
25283                 cls: 'close',
25284                 html: 'x'
25285             });
25286         }
25287         
25288         return cfg;
25289     },
25290     
25291     onRender : function(ct, position)
25292     {
25293         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25294         
25295         if(!this.el){
25296             var cfg = Roo.apply({},  this.getAutoCreate());
25297             cfg.id = Roo.id();
25298             
25299             if (this.cls) {
25300                 cfg.cls += ' ' + this.cls;
25301             }
25302             if (this.style) {
25303                 cfg.style = this.style;
25304             }
25305             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25306             
25307             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25308         }
25309         
25310         this.el.select('>button.close').on('click', this.hide, this);
25311         
25312     },
25313     
25314     show : function()
25315     {
25316         if (!this.rendered) {
25317             this.render();
25318         }
25319         
25320         this.el.show();
25321         
25322         this.fireEvent('show', this);
25323         
25324     },
25325     
25326     hide : function()
25327     {
25328         if (!this.rendered) {
25329             this.render();
25330         }
25331         
25332         this.el.hide();
25333         
25334         this.fireEvent('hide', this);
25335     },
25336     
25337     update : function()
25338     {
25339 //        var e = this.el.dom.firstChild;
25340 //        
25341 //        if(this.closable){
25342 //            e = e.nextSibling;
25343 //        }
25344 //        
25345 //        e.data = this.html || '';
25346
25347         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25348     }
25349    
25350 });
25351
25352  
25353
25354      /*
25355  * - LGPL
25356  *
25357  * Graph
25358  * 
25359  */
25360
25361
25362 /**
25363  * @class Roo.bootstrap.Graph
25364  * @extends Roo.bootstrap.Component
25365  * Bootstrap Graph class
25366 > Prameters
25367  -sm {number} sm 4
25368  -md {number} md 5
25369  @cfg {String} graphtype  bar | vbar | pie
25370  @cfg {number} g_x coodinator | centre x (pie)
25371  @cfg {number} g_y coodinator | centre y (pie)
25372  @cfg {number} g_r radius (pie)
25373  @cfg {number} g_height height of the chart (respected by all elements in the set)
25374  @cfg {number} g_width width of the chart (respected by all elements in the set)
25375  @cfg {Object} title The title of the chart
25376     
25377  -{Array}  values
25378  -opts (object) options for the chart 
25379      o {
25380      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25381      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25382      o vgutter (number)
25383      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.
25384      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25385      o to
25386      o stretch (boolean)
25387      o }
25388  -opts (object) options for the pie
25389      o{
25390      o cut
25391      o startAngle (number)
25392      o endAngle (number)
25393      } 
25394  *
25395  * @constructor
25396  * Create a new Input
25397  * @param {Object} config The config object
25398  */
25399
25400 Roo.bootstrap.Graph = function(config){
25401     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25402     
25403     this.addEvents({
25404         // img events
25405         /**
25406          * @event click
25407          * The img click event for the img.
25408          * @param {Roo.EventObject} e
25409          */
25410         "click" : true
25411     });
25412 };
25413
25414 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25415     
25416     sm: 4,
25417     md: 5,
25418     graphtype: 'bar',
25419     g_height: 250,
25420     g_width: 400,
25421     g_x: 50,
25422     g_y: 50,
25423     g_r: 30,
25424     opts:{
25425         //g_colors: this.colors,
25426         g_type: 'soft',
25427         g_gutter: '20%'
25428
25429     },
25430     title : false,
25431
25432     getAutoCreate : function(){
25433         
25434         var cfg = {
25435             tag: 'div',
25436             html : null
25437         };
25438         
25439         
25440         return  cfg;
25441     },
25442
25443     onRender : function(ct,position){
25444         
25445         
25446         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25447         
25448         if (typeof(Raphael) == 'undefined') {
25449             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25450             return;
25451         }
25452         
25453         this.raphael = Raphael(this.el.dom);
25454         
25455                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25456                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25457                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25458                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25459                 /*
25460                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25461                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25462                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25463                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25464                 
25465                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25466                 r.barchart(330, 10, 300, 220, data1);
25467                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25468                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25469                 */
25470                 
25471                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25472                 // r.barchart(30, 30, 560, 250,  xdata, {
25473                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25474                 //     axis : "0 0 1 1",
25475                 //     axisxlabels :  xdata
25476                 //     //yvalues : cols,
25477                    
25478                 // });
25479 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25480 //        
25481 //        this.load(null,xdata,{
25482 //                axis : "0 0 1 1",
25483 //                axisxlabels :  xdata
25484 //                });
25485
25486     },
25487
25488     load : function(graphtype,xdata,opts)
25489     {
25490         this.raphael.clear();
25491         if(!graphtype) {
25492             graphtype = this.graphtype;
25493         }
25494         if(!opts){
25495             opts = this.opts;
25496         }
25497         var r = this.raphael,
25498             fin = function () {
25499                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25500             },
25501             fout = function () {
25502                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25503             },
25504             pfin = function() {
25505                 this.sector.stop();
25506                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25507
25508                 if (this.label) {
25509                     this.label[0].stop();
25510                     this.label[0].attr({ r: 7.5 });
25511                     this.label[1].attr({ "font-weight": 800 });
25512                 }
25513             },
25514             pfout = function() {
25515                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25516
25517                 if (this.label) {
25518                     this.label[0].animate({ r: 5 }, 500, "bounce");
25519                     this.label[1].attr({ "font-weight": 400 });
25520                 }
25521             };
25522
25523         switch(graphtype){
25524             case 'bar':
25525                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25526                 break;
25527             case 'hbar':
25528                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25529                 break;
25530             case 'pie':
25531 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25532 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25533 //            
25534                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25535                 
25536                 break;
25537
25538         }
25539         
25540         if(this.title){
25541             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25542         }
25543         
25544     },
25545     
25546     setTitle: function(o)
25547     {
25548         this.title = o;
25549     },
25550     
25551     initEvents: function() {
25552         
25553         if(!this.href){
25554             this.el.on('click', this.onClick, this);
25555         }
25556     },
25557     
25558     onClick : function(e)
25559     {
25560         Roo.log('img onclick');
25561         this.fireEvent('click', this, e);
25562     }
25563    
25564 });
25565
25566  
25567 /*
25568  * - LGPL
25569  *
25570  * numberBox
25571  * 
25572  */
25573 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25574
25575 /**
25576  * @class Roo.bootstrap.dash.NumberBox
25577  * @extends Roo.bootstrap.Component
25578  * Bootstrap NumberBox class
25579  * @cfg {String} headline Box headline
25580  * @cfg {String} content Box content
25581  * @cfg {String} icon Box icon
25582  * @cfg {String} footer Footer text
25583  * @cfg {String} fhref Footer href
25584  * 
25585  * @constructor
25586  * Create a new NumberBox
25587  * @param {Object} config The config object
25588  */
25589
25590
25591 Roo.bootstrap.dash.NumberBox = function(config){
25592     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25593     
25594 };
25595
25596 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25597     
25598     headline : '',
25599     content : '',
25600     icon : '',
25601     footer : '',
25602     fhref : '',
25603     ficon : '',
25604     
25605     getAutoCreate : function(){
25606         
25607         var cfg = {
25608             tag : 'div',
25609             cls : 'small-box ',
25610             cn : [
25611                 {
25612                     tag : 'div',
25613                     cls : 'inner',
25614                     cn :[
25615                         {
25616                             tag : 'h3',
25617                             cls : 'roo-headline',
25618                             html : this.headline
25619                         },
25620                         {
25621                             tag : 'p',
25622                             cls : 'roo-content',
25623                             html : this.content
25624                         }
25625                     ]
25626                 }
25627             ]
25628         };
25629         
25630         if(this.icon){
25631             cfg.cn.push({
25632                 tag : 'div',
25633                 cls : 'icon',
25634                 cn :[
25635                     {
25636                         tag : 'i',
25637                         cls : 'ion ' + this.icon
25638                     }
25639                 ]
25640             });
25641         }
25642         
25643         if(this.footer){
25644             var footer = {
25645                 tag : 'a',
25646                 cls : 'small-box-footer',
25647                 href : this.fhref || '#',
25648                 html : this.footer
25649             };
25650             
25651             cfg.cn.push(footer);
25652             
25653         }
25654         
25655         return  cfg;
25656     },
25657
25658     onRender : function(ct,position){
25659         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25660
25661
25662        
25663                 
25664     },
25665
25666     setHeadline: function (value)
25667     {
25668         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25669     },
25670     
25671     setFooter: function (value, href)
25672     {
25673         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25674         
25675         if(href){
25676             this.el.select('a.small-box-footer',true).first().attr('href', href);
25677         }
25678         
25679     },
25680
25681     setContent: function (value)
25682     {
25683         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25684     },
25685
25686     initEvents: function() 
25687     {   
25688         
25689     }
25690     
25691 });
25692
25693  
25694 /*
25695  * - LGPL
25696  *
25697  * TabBox
25698  * 
25699  */
25700 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25701
25702 /**
25703  * @class Roo.bootstrap.dash.TabBox
25704  * @extends Roo.bootstrap.Component
25705  * Bootstrap TabBox class
25706  * @cfg {String} title Title of the TabBox
25707  * @cfg {String} icon Icon of the TabBox
25708  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25709  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25710  * 
25711  * @constructor
25712  * Create a new TabBox
25713  * @param {Object} config The config object
25714  */
25715
25716
25717 Roo.bootstrap.dash.TabBox = function(config){
25718     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25719     this.addEvents({
25720         // raw events
25721         /**
25722          * @event addpane
25723          * When a pane is added
25724          * @param {Roo.bootstrap.dash.TabPane} pane
25725          */
25726         "addpane" : true,
25727         /**
25728          * @event activatepane
25729          * When a pane is activated
25730          * @param {Roo.bootstrap.dash.TabPane} pane
25731          */
25732         "activatepane" : true
25733         
25734          
25735     });
25736     
25737     this.panes = [];
25738 };
25739
25740 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25741
25742     title : '',
25743     icon : false,
25744     showtabs : true,
25745     tabScrollable : false,
25746     
25747     getChildContainer : function()
25748     {
25749         return this.el.select('.tab-content', true).first();
25750     },
25751     
25752     getAutoCreate : function(){
25753         
25754         var header = {
25755             tag: 'li',
25756             cls: 'pull-left header',
25757             html: this.title,
25758             cn : []
25759         };
25760         
25761         if(this.icon){
25762             header.cn.push({
25763                 tag: 'i',
25764                 cls: 'fa ' + this.icon
25765             });
25766         }
25767         
25768         var h = {
25769             tag: 'ul',
25770             cls: 'nav nav-tabs pull-right',
25771             cn: [
25772                 header
25773             ]
25774         };
25775         
25776         if(this.tabScrollable){
25777             h = {
25778                 tag: 'div',
25779                 cls: 'tab-header',
25780                 cn: [
25781                     {
25782                         tag: 'ul',
25783                         cls: 'nav nav-tabs pull-right',
25784                         cn: [
25785                             header
25786                         ]
25787                     }
25788                 ]
25789             };
25790         }
25791         
25792         var cfg = {
25793             tag: 'div',
25794             cls: 'nav-tabs-custom',
25795             cn: [
25796                 h,
25797                 {
25798                     tag: 'div',
25799                     cls: 'tab-content no-padding',
25800                     cn: []
25801                 }
25802             ]
25803         };
25804
25805         return  cfg;
25806     },
25807     initEvents : function()
25808     {
25809         //Roo.log('add add pane handler');
25810         this.on('addpane', this.onAddPane, this);
25811     },
25812      /**
25813      * Updates the box title
25814      * @param {String} html to set the title to.
25815      */
25816     setTitle : function(value)
25817     {
25818         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25819     },
25820     onAddPane : function(pane)
25821     {
25822         this.panes.push(pane);
25823         //Roo.log('addpane');
25824         //Roo.log(pane);
25825         // tabs are rendere left to right..
25826         if(!this.showtabs){
25827             return;
25828         }
25829         
25830         var ctr = this.el.select('.nav-tabs', true).first();
25831          
25832          
25833         var existing = ctr.select('.nav-tab',true);
25834         var qty = existing.getCount();;
25835         
25836         
25837         var tab = ctr.createChild({
25838             tag : 'li',
25839             cls : 'nav-tab' + (qty ? '' : ' active'),
25840             cn : [
25841                 {
25842                     tag : 'a',
25843                     href:'#',
25844                     html : pane.title
25845                 }
25846             ]
25847         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25848         pane.tab = tab;
25849         
25850         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25851         if (!qty) {
25852             pane.el.addClass('active');
25853         }
25854         
25855                 
25856     },
25857     onTabClick : function(ev,un,ob,pane)
25858     {
25859         //Roo.log('tab - prev default');
25860         ev.preventDefault();
25861         
25862         
25863         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25864         pane.tab.addClass('active');
25865         //Roo.log(pane.title);
25866         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25867         // technically we should have a deactivate event.. but maybe add later.
25868         // and it should not de-activate the selected tab...
25869         this.fireEvent('activatepane', pane);
25870         pane.el.addClass('active');
25871         pane.fireEvent('activate');
25872         
25873         
25874     },
25875     
25876     getActivePane : function()
25877     {
25878         var r = false;
25879         Roo.each(this.panes, function(p) {
25880             if(p.el.hasClass('active')){
25881                 r = p;
25882                 return false;
25883             }
25884             
25885             return;
25886         });
25887         
25888         return r;
25889     }
25890     
25891     
25892 });
25893
25894  
25895 /*
25896  * - LGPL
25897  *
25898  * Tab pane
25899  * 
25900  */
25901 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25902 /**
25903  * @class Roo.bootstrap.TabPane
25904  * @extends Roo.bootstrap.Component
25905  * Bootstrap TabPane class
25906  * @cfg {Boolean} active (false | true) Default false
25907  * @cfg {String} title title of panel
25908
25909  * 
25910  * @constructor
25911  * Create a new TabPane
25912  * @param {Object} config The config object
25913  */
25914
25915 Roo.bootstrap.dash.TabPane = function(config){
25916     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25917     
25918     this.addEvents({
25919         // raw events
25920         /**
25921          * @event activate
25922          * When a pane is activated
25923          * @param {Roo.bootstrap.dash.TabPane} pane
25924          */
25925         "activate" : true
25926          
25927     });
25928 };
25929
25930 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25931     
25932     active : false,
25933     title : '',
25934     
25935     // the tabBox that this is attached to.
25936     tab : false,
25937      
25938     getAutoCreate : function() 
25939     {
25940         var cfg = {
25941             tag: 'div',
25942             cls: 'tab-pane'
25943         };
25944         
25945         if(this.active){
25946             cfg.cls += ' active';
25947         }
25948         
25949         return cfg;
25950     },
25951     initEvents  : function()
25952     {
25953         //Roo.log('trigger add pane handler');
25954         this.parent().fireEvent('addpane', this)
25955     },
25956     
25957      /**
25958      * Updates the tab title 
25959      * @param {String} html to set the title to.
25960      */
25961     setTitle: function(str)
25962     {
25963         if (!this.tab) {
25964             return;
25965         }
25966         this.title = str;
25967         this.tab.select('a', true).first().dom.innerHTML = str;
25968         
25969     }
25970     
25971     
25972     
25973 });
25974
25975  
25976
25977
25978  /*
25979  * - LGPL
25980  *
25981  * menu
25982  * 
25983  */
25984 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25985
25986 /**
25987  * @class Roo.bootstrap.menu.Menu
25988  * @extends Roo.bootstrap.Component
25989  * Bootstrap Menu class - container for Menu
25990  * @cfg {String} html Text of the menu
25991  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25992  * @cfg {String} icon Font awesome icon
25993  * @cfg {String} pos Menu align to (top | bottom) default bottom
25994  * 
25995  * 
25996  * @constructor
25997  * Create a new Menu
25998  * @param {Object} config The config object
25999  */
26000
26001
26002 Roo.bootstrap.menu.Menu = function(config){
26003     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26004     
26005     this.addEvents({
26006         /**
26007          * @event beforeshow
26008          * Fires before this menu is displayed
26009          * @param {Roo.bootstrap.menu.Menu} this
26010          */
26011         beforeshow : true,
26012         /**
26013          * @event beforehide
26014          * Fires before this menu is hidden
26015          * @param {Roo.bootstrap.menu.Menu} this
26016          */
26017         beforehide : true,
26018         /**
26019          * @event show
26020          * Fires after this menu is displayed
26021          * @param {Roo.bootstrap.menu.Menu} this
26022          */
26023         show : true,
26024         /**
26025          * @event hide
26026          * Fires after this menu is hidden
26027          * @param {Roo.bootstrap.menu.Menu} this
26028          */
26029         hide : true,
26030         /**
26031          * @event click
26032          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26033          * @param {Roo.bootstrap.menu.Menu} this
26034          * @param {Roo.EventObject} e
26035          */
26036         click : true
26037     });
26038     
26039 };
26040
26041 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
26042     
26043     submenu : false,
26044     html : '',
26045     weight : 'default',
26046     icon : false,
26047     pos : 'bottom',
26048     
26049     
26050     getChildContainer : function() {
26051         if(this.isSubMenu){
26052             return this.el;
26053         }
26054         
26055         return this.el.select('ul.dropdown-menu', true).first();  
26056     },
26057     
26058     getAutoCreate : function()
26059     {
26060         var text = [
26061             {
26062                 tag : 'span',
26063                 cls : 'roo-menu-text',
26064                 html : this.html
26065             }
26066         ];
26067         
26068         if(this.icon){
26069             text.unshift({
26070                 tag : 'i',
26071                 cls : 'fa ' + this.icon
26072             })
26073         }
26074         
26075         
26076         var cfg = {
26077             tag : 'div',
26078             cls : 'btn-group',
26079             cn : [
26080                 {
26081                     tag : 'button',
26082                     cls : 'dropdown-button btn btn-' + this.weight,
26083                     cn : text
26084                 },
26085                 {
26086                     tag : 'button',
26087                     cls : 'dropdown-toggle btn btn-' + this.weight,
26088                     cn : [
26089                         {
26090                             tag : 'span',
26091                             cls : 'caret'
26092                         }
26093                     ]
26094                 },
26095                 {
26096                     tag : 'ul',
26097                     cls : 'dropdown-menu'
26098                 }
26099             ]
26100             
26101         };
26102         
26103         if(this.pos == 'top'){
26104             cfg.cls += ' dropup';
26105         }
26106         
26107         if(this.isSubMenu){
26108             cfg = {
26109                 tag : 'ul',
26110                 cls : 'dropdown-menu'
26111             }
26112         }
26113         
26114         return cfg;
26115     },
26116     
26117     onRender : function(ct, position)
26118     {
26119         this.isSubMenu = ct.hasClass('dropdown-submenu');
26120         
26121         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26122     },
26123     
26124     initEvents : function() 
26125     {
26126         if(this.isSubMenu){
26127             return;
26128         }
26129         
26130         this.hidden = true;
26131         
26132         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26133         this.triggerEl.on('click', this.onTriggerPress, this);
26134         
26135         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26136         this.buttonEl.on('click', this.onClick, this);
26137         
26138     },
26139     
26140     list : function()
26141     {
26142         if(this.isSubMenu){
26143             return this.el;
26144         }
26145         
26146         return this.el.select('ul.dropdown-menu', true).first();
26147     },
26148     
26149     onClick : function(e)
26150     {
26151         this.fireEvent("click", this, e);
26152     },
26153     
26154     onTriggerPress  : function(e)
26155     {   
26156         if (this.isVisible()) {
26157             this.hide();
26158         } else {
26159             this.show();
26160         }
26161     },
26162     
26163     isVisible : function(){
26164         return !this.hidden;
26165     },
26166     
26167     show : function()
26168     {
26169         this.fireEvent("beforeshow", this);
26170         
26171         this.hidden = false;
26172         this.el.addClass('open');
26173         
26174         Roo.get(document).on("mouseup", this.onMouseUp, this);
26175         
26176         this.fireEvent("show", this);
26177         
26178         
26179     },
26180     
26181     hide : function()
26182     {
26183         this.fireEvent("beforehide", this);
26184         
26185         this.hidden = true;
26186         this.el.removeClass('open');
26187         
26188         Roo.get(document).un("mouseup", this.onMouseUp);
26189         
26190         this.fireEvent("hide", this);
26191     },
26192     
26193     onMouseUp : function()
26194     {
26195         this.hide();
26196     }
26197     
26198 });
26199
26200  
26201  /*
26202  * - LGPL
26203  *
26204  * menu item
26205  * 
26206  */
26207 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26208
26209 /**
26210  * @class Roo.bootstrap.menu.Item
26211  * @extends Roo.bootstrap.Component
26212  * Bootstrap MenuItem class
26213  * @cfg {Boolean} submenu (true | false) default false
26214  * @cfg {String} html text of the item
26215  * @cfg {String} href the link
26216  * @cfg {Boolean} disable (true | false) default false
26217  * @cfg {Boolean} preventDefault (true | false) default true
26218  * @cfg {String} icon Font awesome icon
26219  * @cfg {String} pos Submenu align to (left | right) default right 
26220  * 
26221  * 
26222  * @constructor
26223  * Create a new Item
26224  * @param {Object} config The config object
26225  */
26226
26227
26228 Roo.bootstrap.menu.Item = function(config){
26229     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26230     this.addEvents({
26231         /**
26232          * @event mouseover
26233          * Fires when the mouse is hovering over this menu
26234          * @param {Roo.bootstrap.menu.Item} this
26235          * @param {Roo.EventObject} e
26236          */
26237         mouseover : true,
26238         /**
26239          * @event mouseout
26240          * Fires when the mouse exits this menu
26241          * @param {Roo.bootstrap.menu.Item} this
26242          * @param {Roo.EventObject} e
26243          */
26244         mouseout : true,
26245         // raw events
26246         /**
26247          * @event click
26248          * The raw click event for the entire grid.
26249          * @param {Roo.EventObject} e
26250          */
26251         click : true
26252     });
26253 };
26254
26255 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26256     
26257     submenu : false,
26258     href : '',
26259     html : '',
26260     preventDefault: true,
26261     disable : false,
26262     icon : false,
26263     pos : 'right',
26264     
26265     getAutoCreate : function()
26266     {
26267         var text = [
26268             {
26269                 tag : 'span',
26270                 cls : 'roo-menu-item-text',
26271                 html : this.html
26272             }
26273         ];
26274         
26275         if(this.icon){
26276             text.unshift({
26277                 tag : 'i',
26278                 cls : 'fa ' + this.icon
26279             })
26280         }
26281         
26282         var cfg = {
26283             tag : 'li',
26284             cn : [
26285                 {
26286                     tag : 'a',
26287                     href : this.href || '#',
26288                     cn : text
26289                 }
26290             ]
26291         };
26292         
26293         if(this.disable){
26294             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26295         }
26296         
26297         if(this.submenu){
26298             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26299             
26300             if(this.pos == 'left'){
26301                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26302             }
26303         }
26304         
26305         return cfg;
26306     },
26307     
26308     initEvents : function() 
26309     {
26310         this.el.on('mouseover', this.onMouseOver, this);
26311         this.el.on('mouseout', this.onMouseOut, this);
26312         
26313         this.el.select('a', true).first().on('click', this.onClick, this);
26314         
26315     },
26316     
26317     onClick : function(e)
26318     {
26319         if(this.preventDefault){
26320             e.preventDefault();
26321         }
26322         
26323         this.fireEvent("click", this, e);
26324     },
26325     
26326     onMouseOver : function(e)
26327     {
26328         if(this.submenu && this.pos == 'left'){
26329             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26330         }
26331         
26332         this.fireEvent("mouseover", this, e);
26333     },
26334     
26335     onMouseOut : function(e)
26336     {
26337         this.fireEvent("mouseout", this, e);
26338     }
26339 });
26340
26341  
26342
26343  /*
26344  * - LGPL
26345  *
26346  * menu separator
26347  * 
26348  */
26349 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26350
26351 /**
26352  * @class Roo.bootstrap.menu.Separator
26353  * @extends Roo.bootstrap.Component
26354  * Bootstrap Separator class
26355  * 
26356  * @constructor
26357  * Create a new Separator
26358  * @param {Object} config The config object
26359  */
26360
26361
26362 Roo.bootstrap.menu.Separator = function(config){
26363     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26364 };
26365
26366 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26367     
26368     getAutoCreate : function(){
26369         var cfg = {
26370             tag : 'li',
26371             cls: 'divider'
26372         };
26373         
26374         return cfg;
26375     }
26376    
26377 });
26378
26379  
26380
26381  /*
26382  * - LGPL
26383  *
26384  * Tooltip
26385  * 
26386  */
26387
26388 /**
26389  * @class Roo.bootstrap.Tooltip
26390  * Bootstrap Tooltip class
26391  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26392  * to determine which dom element triggers the tooltip.
26393  * 
26394  * It needs to add support for additional attributes like tooltip-position
26395  * 
26396  * @constructor
26397  * Create a new Toolti
26398  * @param {Object} config The config object
26399  */
26400
26401 Roo.bootstrap.Tooltip = function(config){
26402     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26403     
26404     this.alignment = Roo.bootstrap.Tooltip.alignment;
26405     
26406     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26407         this.alignment = config.alignment;
26408     }
26409     
26410 };
26411
26412 Roo.apply(Roo.bootstrap.Tooltip, {
26413     /**
26414      * @function init initialize tooltip monitoring.
26415      * @static
26416      */
26417     currentEl : false,
26418     currentTip : false,
26419     currentRegion : false,
26420     
26421     //  init : delay?
26422     
26423     init : function()
26424     {
26425         Roo.get(document).on('mouseover', this.enter ,this);
26426         Roo.get(document).on('mouseout', this.leave, this);
26427          
26428         
26429         this.currentTip = new Roo.bootstrap.Tooltip();
26430     },
26431     
26432     enter : function(ev)
26433     {
26434         var dom = ev.getTarget();
26435         
26436         //Roo.log(['enter',dom]);
26437         var el = Roo.fly(dom);
26438         if (this.currentEl) {
26439             //Roo.log(dom);
26440             //Roo.log(this.currentEl);
26441             //Roo.log(this.currentEl.contains(dom));
26442             if (this.currentEl == el) {
26443                 return;
26444             }
26445             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26446                 return;
26447             }
26448
26449         }
26450         
26451         if (this.currentTip.el) {
26452             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26453         }    
26454         //Roo.log(ev);
26455         
26456         if(!el || el.dom == document){
26457             return;
26458         }
26459         
26460         var bindEl = el;
26461         
26462         // you can not look for children, as if el is the body.. then everythign is the child..
26463         if (!el.attr('tooltip')) { //
26464             if (!el.select("[tooltip]").elements.length) {
26465                 return;
26466             }
26467             // is the mouse over this child...?
26468             bindEl = el.select("[tooltip]").first();
26469             var xy = ev.getXY();
26470             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26471                 //Roo.log("not in region.");
26472                 return;
26473             }
26474             //Roo.log("child element over..");
26475             
26476         }
26477         this.currentEl = bindEl;
26478         this.currentTip.bind(bindEl);
26479         this.currentRegion = Roo.lib.Region.getRegion(dom);
26480         this.currentTip.enter();
26481         
26482     },
26483     leave : function(ev)
26484     {
26485         var dom = ev.getTarget();
26486         //Roo.log(['leave',dom]);
26487         if (!this.currentEl) {
26488             return;
26489         }
26490         
26491         
26492         if (dom != this.currentEl.dom) {
26493             return;
26494         }
26495         var xy = ev.getXY();
26496         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26497             return;
26498         }
26499         // only activate leave if mouse cursor is outside... bounding box..
26500         
26501         
26502         
26503         
26504         if (this.currentTip) {
26505             this.currentTip.leave();
26506         }
26507         //Roo.log('clear currentEl');
26508         this.currentEl = false;
26509         
26510         
26511     },
26512     alignment : {
26513         'left' : ['r-l', [-2,0], 'right'],
26514         'right' : ['l-r', [2,0], 'left'],
26515         'bottom' : ['t-b', [0,2], 'top'],
26516         'top' : [ 'b-t', [0,-2], 'bottom']
26517     }
26518     
26519 });
26520
26521
26522 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26523     
26524     
26525     bindEl : false,
26526     
26527     delay : null, // can be { show : 300 , hide: 500}
26528     
26529     timeout : null,
26530     
26531     hoverState : null, //???
26532     
26533     placement : 'bottom', 
26534     
26535     alignment : false,
26536     
26537     getAutoCreate : function(){
26538     
26539         var cfg = {
26540            cls : 'tooltip',
26541            role : 'tooltip',
26542            cn : [
26543                 {
26544                     cls : 'tooltip-arrow'
26545                 },
26546                 {
26547                     cls : 'tooltip-inner'
26548                 }
26549            ]
26550         };
26551         
26552         return cfg;
26553     },
26554     bind : function(el)
26555     {
26556         this.bindEl = el;
26557     },
26558       
26559     
26560     enter : function () {
26561        
26562         if (this.timeout != null) {
26563             clearTimeout(this.timeout);
26564         }
26565         
26566         this.hoverState = 'in';
26567          //Roo.log("enter - show");
26568         if (!this.delay || !this.delay.show) {
26569             this.show();
26570             return;
26571         }
26572         var _t = this;
26573         this.timeout = setTimeout(function () {
26574             if (_t.hoverState == 'in') {
26575                 _t.show();
26576             }
26577         }, this.delay.show);
26578     },
26579     leave : function()
26580     {
26581         clearTimeout(this.timeout);
26582     
26583         this.hoverState = 'out';
26584          if (!this.delay || !this.delay.hide) {
26585             this.hide();
26586             return;
26587         }
26588        
26589         var _t = this;
26590         this.timeout = setTimeout(function () {
26591             //Roo.log("leave - timeout");
26592             
26593             if (_t.hoverState == 'out') {
26594                 _t.hide();
26595                 Roo.bootstrap.Tooltip.currentEl = false;
26596             }
26597         }, delay);
26598     },
26599     
26600     show : function (msg)
26601     {
26602         if (!this.el) {
26603             this.render(document.body);
26604         }
26605         // set content.
26606         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26607         
26608         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26609         
26610         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26611         
26612         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26613         
26614         var placement = typeof this.placement == 'function' ?
26615             this.placement.call(this, this.el, on_el) :
26616             this.placement;
26617             
26618         var autoToken = /\s?auto?\s?/i;
26619         var autoPlace = autoToken.test(placement);
26620         if (autoPlace) {
26621             placement = placement.replace(autoToken, '') || 'top';
26622         }
26623         
26624         //this.el.detach()
26625         //this.el.setXY([0,0]);
26626         this.el.show();
26627         //this.el.dom.style.display='block';
26628         
26629         //this.el.appendTo(on_el);
26630         
26631         var p = this.getPosition();
26632         var box = this.el.getBox();
26633         
26634         if (autoPlace) {
26635             // fixme..
26636         }
26637         
26638         var align = this.alignment[placement];
26639         
26640         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26641         
26642         if(placement == 'top' || placement == 'bottom'){
26643             if(xy[0] < 0){
26644                 placement = 'right';
26645             }
26646             
26647             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26648                 placement = 'left';
26649             }
26650             
26651             var scroll = Roo.select('body', true).first().getScroll();
26652             
26653             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26654                 placement = 'top';
26655             }
26656             
26657             align = this.alignment[placement];
26658         }
26659         
26660         this.el.alignTo(this.bindEl, align[0],align[1]);
26661         //var arrow = this.el.select('.arrow',true).first();
26662         //arrow.set(align[2], 
26663         
26664         this.el.addClass(placement);
26665         
26666         this.el.addClass('in fade');
26667         
26668         this.hoverState = null;
26669         
26670         if (this.el.hasClass('fade')) {
26671             // fade it?
26672         }
26673         
26674     },
26675     hide : function()
26676     {
26677          
26678         if (!this.el) {
26679             return;
26680         }
26681         //this.el.setXY([0,0]);
26682         this.el.removeClass('in');
26683         //this.el.hide();
26684         
26685     }
26686     
26687 });
26688  
26689
26690  /*
26691  * - LGPL
26692  *
26693  * Location Picker
26694  * 
26695  */
26696
26697 /**
26698  * @class Roo.bootstrap.LocationPicker
26699  * @extends Roo.bootstrap.Component
26700  * Bootstrap LocationPicker class
26701  * @cfg {Number} latitude Position when init default 0
26702  * @cfg {Number} longitude Position when init default 0
26703  * @cfg {Number} zoom default 15
26704  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26705  * @cfg {Boolean} mapTypeControl default false
26706  * @cfg {Boolean} disableDoubleClickZoom default false
26707  * @cfg {Boolean} scrollwheel default true
26708  * @cfg {Boolean} streetViewControl default false
26709  * @cfg {Number} radius default 0
26710  * @cfg {String} locationName
26711  * @cfg {Boolean} draggable default true
26712  * @cfg {Boolean} enableAutocomplete default false
26713  * @cfg {Boolean} enableReverseGeocode default true
26714  * @cfg {String} markerTitle
26715  * 
26716  * @constructor
26717  * Create a new LocationPicker
26718  * @param {Object} config The config object
26719  */
26720
26721
26722 Roo.bootstrap.LocationPicker = function(config){
26723     
26724     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26725     
26726     this.addEvents({
26727         /**
26728          * @event initial
26729          * Fires when the picker initialized.
26730          * @param {Roo.bootstrap.LocationPicker} this
26731          * @param {Google Location} location
26732          */
26733         initial : true,
26734         /**
26735          * @event positionchanged
26736          * Fires when the picker position changed.
26737          * @param {Roo.bootstrap.LocationPicker} this
26738          * @param {Google Location} location
26739          */
26740         positionchanged : true,
26741         /**
26742          * @event resize
26743          * Fires when the map resize.
26744          * @param {Roo.bootstrap.LocationPicker} this
26745          */
26746         resize : true,
26747         /**
26748          * @event show
26749          * Fires when the map show.
26750          * @param {Roo.bootstrap.LocationPicker} this
26751          */
26752         show : true,
26753         /**
26754          * @event hide
26755          * Fires when the map hide.
26756          * @param {Roo.bootstrap.LocationPicker} this
26757          */
26758         hide : true,
26759         /**
26760          * @event mapClick
26761          * Fires when click the map.
26762          * @param {Roo.bootstrap.LocationPicker} this
26763          * @param {Map event} e
26764          */
26765         mapClick : true,
26766         /**
26767          * @event mapRightClick
26768          * Fires when right click the map.
26769          * @param {Roo.bootstrap.LocationPicker} this
26770          * @param {Map event} e
26771          */
26772         mapRightClick : true,
26773         /**
26774          * @event markerClick
26775          * Fires when click the marker.
26776          * @param {Roo.bootstrap.LocationPicker} this
26777          * @param {Map event} e
26778          */
26779         markerClick : true,
26780         /**
26781          * @event markerRightClick
26782          * Fires when right click the marker.
26783          * @param {Roo.bootstrap.LocationPicker} this
26784          * @param {Map event} e
26785          */
26786         markerRightClick : true,
26787         /**
26788          * @event OverlayViewDraw
26789          * Fires when OverlayView Draw
26790          * @param {Roo.bootstrap.LocationPicker} this
26791          */
26792         OverlayViewDraw : true,
26793         /**
26794          * @event OverlayViewOnAdd
26795          * Fires when OverlayView Draw
26796          * @param {Roo.bootstrap.LocationPicker} this
26797          */
26798         OverlayViewOnAdd : true,
26799         /**
26800          * @event OverlayViewOnRemove
26801          * Fires when OverlayView Draw
26802          * @param {Roo.bootstrap.LocationPicker} this
26803          */
26804         OverlayViewOnRemove : true,
26805         /**
26806          * @event OverlayViewShow
26807          * Fires when OverlayView Draw
26808          * @param {Roo.bootstrap.LocationPicker} this
26809          * @param {Pixel} cpx
26810          */
26811         OverlayViewShow : true,
26812         /**
26813          * @event OverlayViewHide
26814          * Fires when OverlayView Draw
26815          * @param {Roo.bootstrap.LocationPicker} this
26816          */
26817         OverlayViewHide : true,
26818         /**
26819          * @event loadexception
26820          * Fires when load google lib failed.
26821          * @param {Roo.bootstrap.LocationPicker} this
26822          */
26823         loadexception : true
26824     });
26825         
26826 };
26827
26828 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26829     
26830     gMapContext: false,
26831     
26832     latitude: 0,
26833     longitude: 0,
26834     zoom: 15,
26835     mapTypeId: false,
26836     mapTypeControl: false,
26837     disableDoubleClickZoom: false,
26838     scrollwheel: true,
26839     streetViewControl: false,
26840     radius: 0,
26841     locationName: '',
26842     draggable: true,
26843     enableAutocomplete: false,
26844     enableReverseGeocode: true,
26845     markerTitle: '',
26846     
26847     getAutoCreate: function()
26848     {
26849
26850         var cfg = {
26851             tag: 'div',
26852             cls: 'roo-location-picker'
26853         };
26854         
26855         return cfg
26856     },
26857     
26858     initEvents: function(ct, position)
26859     {       
26860         if(!this.el.getWidth() || this.isApplied()){
26861             return;
26862         }
26863         
26864         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26865         
26866         this.initial();
26867     },
26868     
26869     initial: function()
26870     {
26871         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26872             this.fireEvent('loadexception', this);
26873             return;
26874         }
26875         
26876         if(!this.mapTypeId){
26877             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26878         }
26879         
26880         this.gMapContext = this.GMapContext();
26881         
26882         this.initOverlayView();
26883         
26884         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26885         
26886         var _this = this;
26887                 
26888         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26889             _this.setPosition(_this.gMapContext.marker.position);
26890         });
26891         
26892         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26893             _this.fireEvent('mapClick', this, event);
26894             
26895         });
26896
26897         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26898             _this.fireEvent('mapRightClick', this, event);
26899             
26900         });
26901         
26902         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26903             _this.fireEvent('markerClick', this, event);
26904             
26905         });
26906
26907         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26908             _this.fireEvent('markerRightClick', this, event);
26909             
26910         });
26911         
26912         this.setPosition(this.gMapContext.location);
26913         
26914         this.fireEvent('initial', this, this.gMapContext.location);
26915     },
26916     
26917     initOverlayView: function()
26918     {
26919         var _this = this;
26920         
26921         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26922             
26923             draw: function()
26924             {
26925                 _this.fireEvent('OverlayViewDraw', _this);
26926             },
26927             
26928             onAdd: function()
26929             {
26930                 _this.fireEvent('OverlayViewOnAdd', _this);
26931             },
26932             
26933             onRemove: function()
26934             {
26935                 _this.fireEvent('OverlayViewOnRemove', _this);
26936             },
26937             
26938             show: function(cpx)
26939             {
26940                 _this.fireEvent('OverlayViewShow', _this, cpx);
26941             },
26942             
26943             hide: function()
26944             {
26945                 _this.fireEvent('OverlayViewHide', _this);
26946             }
26947             
26948         });
26949     },
26950     
26951     fromLatLngToContainerPixel: function(event)
26952     {
26953         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26954     },
26955     
26956     isApplied: function() 
26957     {
26958         return this.getGmapContext() == false ? false : true;
26959     },
26960     
26961     getGmapContext: function() 
26962     {
26963         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26964     },
26965     
26966     GMapContext: function() 
26967     {
26968         var position = new google.maps.LatLng(this.latitude, this.longitude);
26969         
26970         var _map = new google.maps.Map(this.el.dom, {
26971             center: position,
26972             zoom: this.zoom,
26973             mapTypeId: this.mapTypeId,
26974             mapTypeControl: this.mapTypeControl,
26975             disableDoubleClickZoom: this.disableDoubleClickZoom,
26976             scrollwheel: this.scrollwheel,
26977             streetViewControl: this.streetViewControl,
26978             locationName: this.locationName,
26979             draggable: this.draggable,
26980             enableAutocomplete: this.enableAutocomplete,
26981             enableReverseGeocode: this.enableReverseGeocode
26982         });
26983         
26984         var _marker = new google.maps.Marker({
26985             position: position,
26986             map: _map,
26987             title: this.markerTitle,
26988             draggable: this.draggable
26989         });
26990         
26991         return {
26992             map: _map,
26993             marker: _marker,
26994             circle: null,
26995             location: position,
26996             radius: this.radius,
26997             locationName: this.locationName,
26998             addressComponents: {
26999                 formatted_address: null,
27000                 addressLine1: null,
27001                 addressLine2: null,
27002                 streetName: null,
27003                 streetNumber: null,
27004                 city: null,
27005                 district: null,
27006                 state: null,
27007                 stateOrProvince: null
27008             },
27009             settings: this,
27010             domContainer: this.el.dom,
27011             geodecoder: new google.maps.Geocoder()
27012         };
27013     },
27014     
27015     drawCircle: function(center, radius, options) 
27016     {
27017         if (this.gMapContext.circle != null) {
27018             this.gMapContext.circle.setMap(null);
27019         }
27020         if (radius > 0) {
27021             radius *= 1;
27022             options = Roo.apply({}, options, {
27023                 strokeColor: "#0000FF",
27024                 strokeOpacity: .35,
27025                 strokeWeight: 2,
27026                 fillColor: "#0000FF",
27027                 fillOpacity: .2
27028             });
27029             
27030             options.map = this.gMapContext.map;
27031             options.radius = radius;
27032             options.center = center;
27033             this.gMapContext.circle = new google.maps.Circle(options);
27034             return this.gMapContext.circle;
27035         }
27036         
27037         return null;
27038     },
27039     
27040     setPosition: function(location) 
27041     {
27042         this.gMapContext.location = location;
27043         this.gMapContext.marker.setPosition(location);
27044         this.gMapContext.map.panTo(location);
27045         this.drawCircle(location, this.gMapContext.radius, {});
27046         
27047         var _this = this;
27048         
27049         if (this.gMapContext.settings.enableReverseGeocode) {
27050             this.gMapContext.geodecoder.geocode({
27051                 latLng: this.gMapContext.location
27052             }, function(results, status) {
27053                 
27054                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27055                     _this.gMapContext.locationName = results[0].formatted_address;
27056                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27057                     
27058                     _this.fireEvent('positionchanged', this, location);
27059                 }
27060             });
27061             
27062             return;
27063         }
27064         
27065         this.fireEvent('positionchanged', this, location);
27066     },
27067     
27068     resize: function()
27069     {
27070         google.maps.event.trigger(this.gMapContext.map, "resize");
27071         
27072         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27073         
27074         this.fireEvent('resize', this);
27075     },
27076     
27077     setPositionByLatLng: function(latitude, longitude)
27078     {
27079         this.setPosition(new google.maps.LatLng(latitude, longitude));
27080     },
27081     
27082     getCurrentPosition: function() 
27083     {
27084         return {
27085             latitude: this.gMapContext.location.lat(),
27086             longitude: this.gMapContext.location.lng()
27087         };
27088     },
27089     
27090     getAddressName: function() 
27091     {
27092         return this.gMapContext.locationName;
27093     },
27094     
27095     getAddressComponents: function() 
27096     {
27097         return this.gMapContext.addressComponents;
27098     },
27099     
27100     address_component_from_google_geocode: function(address_components) 
27101     {
27102         var result = {};
27103         
27104         for (var i = 0; i < address_components.length; i++) {
27105             var component = address_components[i];
27106             if (component.types.indexOf("postal_code") >= 0) {
27107                 result.postalCode = component.short_name;
27108             } else if (component.types.indexOf("street_number") >= 0) {
27109                 result.streetNumber = component.short_name;
27110             } else if (component.types.indexOf("route") >= 0) {
27111                 result.streetName = component.short_name;
27112             } else if (component.types.indexOf("neighborhood") >= 0) {
27113                 result.city = component.short_name;
27114             } else if (component.types.indexOf("locality") >= 0) {
27115                 result.city = component.short_name;
27116             } else if (component.types.indexOf("sublocality") >= 0) {
27117                 result.district = component.short_name;
27118             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27119                 result.stateOrProvince = component.short_name;
27120             } else if (component.types.indexOf("country") >= 0) {
27121                 result.country = component.short_name;
27122             }
27123         }
27124         
27125         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27126         result.addressLine2 = "";
27127         return result;
27128     },
27129     
27130     setZoomLevel: function(zoom)
27131     {
27132         this.gMapContext.map.setZoom(zoom);
27133     },
27134     
27135     show: function()
27136     {
27137         if(!this.el){
27138             return;
27139         }
27140         
27141         this.el.show();
27142         
27143         this.resize();
27144         
27145         this.fireEvent('show', this);
27146     },
27147     
27148     hide: function()
27149     {
27150         if(!this.el){
27151             return;
27152         }
27153         
27154         this.el.hide();
27155         
27156         this.fireEvent('hide', this);
27157     }
27158     
27159 });
27160
27161 Roo.apply(Roo.bootstrap.LocationPicker, {
27162     
27163     OverlayView : function(map, options)
27164     {
27165         options = options || {};
27166         
27167         this.setMap(map);
27168     }
27169     
27170     
27171 });/*
27172  * - LGPL
27173  *
27174  * Alert
27175  * 
27176  */
27177
27178 /**
27179  * @class Roo.bootstrap.Alert
27180  * @extends Roo.bootstrap.Component
27181  * Bootstrap Alert class
27182  * @cfg {String} title The title of alert
27183  * @cfg {String} html The content of alert
27184  * @cfg {String} weight (  success | info | warning | danger )
27185  * @cfg {String} faicon font-awesomeicon
27186  * 
27187  * @constructor
27188  * Create a new alert
27189  * @param {Object} config The config object
27190  */
27191
27192
27193 Roo.bootstrap.Alert = function(config){
27194     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27195     
27196 };
27197
27198 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27199     
27200     title: '',
27201     html: '',
27202     weight: false,
27203     faicon: false,
27204     
27205     getAutoCreate : function()
27206     {
27207         
27208         var cfg = {
27209             tag : 'div',
27210             cls : 'alert',
27211             cn : [
27212                 {
27213                     tag : 'i',
27214                     cls : 'roo-alert-icon'
27215                     
27216                 },
27217                 {
27218                     tag : 'b',
27219                     cls : 'roo-alert-title',
27220                     html : this.title
27221                 },
27222                 {
27223                     tag : 'span',
27224                     cls : 'roo-alert-text',
27225                     html : this.html
27226                 }
27227             ]
27228         };
27229         
27230         if(this.faicon){
27231             cfg.cn[0].cls += ' fa ' + this.faicon;
27232         }
27233         
27234         if(this.weight){
27235             cfg.cls += ' alert-' + this.weight;
27236         }
27237         
27238         return cfg;
27239     },
27240     
27241     initEvents: function() 
27242     {
27243         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27244     },
27245     
27246     setTitle : function(str)
27247     {
27248         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27249     },
27250     
27251     setText : function(str)
27252     {
27253         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27254     },
27255     
27256     setWeight : function(weight)
27257     {
27258         if(this.weight){
27259             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27260         }
27261         
27262         this.weight = weight;
27263         
27264         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27265     },
27266     
27267     setIcon : function(icon)
27268     {
27269         if(this.faicon){
27270             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27271         }
27272         
27273         this.faicon = icon;
27274         
27275         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27276     },
27277     
27278     hide: function() 
27279     {
27280         this.el.hide();   
27281     },
27282     
27283     show: function() 
27284     {  
27285         this.el.show();   
27286     }
27287     
27288 });
27289
27290  
27291 /*
27292 * Licence: LGPL
27293 */
27294
27295 /**
27296  * @class Roo.bootstrap.UploadCropbox
27297  * @extends Roo.bootstrap.Component
27298  * Bootstrap UploadCropbox class
27299  * @cfg {String} emptyText show when image has been loaded
27300  * @cfg {String} rotateNotify show when image too small to rotate
27301  * @cfg {Number} errorTimeout default 3000
27302  * @cfg {Number} minWidth default 300
27303  * @cfg {Number} minHeight default 300
27304  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27305  * @cfg {Boolean} isDocument (true|false) default false
27306  * @cfg {String} url action url
27307  * @cfg {String} paramName default 'imageUpload'
27308  * @cfg {String} method default POST
27309  * @cfg {Boolean} loadMask (true|false) default true
27310  * @cfg {Boolean} loadingText default 'Loading...'
27311  * 
27312  * @constructor
27313  * Create a new UploadCropbox
27314  * @param {Object} config The config object
27315  */
27316
27317 Roo.bootstrap.UploadCropbox = function(config){
27318     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27319     
27320     this.addEvents({
27321         /**
27322          * @event beforeselectfile
27323          * Fire before select file
27324          * @param {Roo.bootstrap.UploadCropbox} this
27325          */
27326         "beforeselectfile" : true,
27327         /**
27328          * @event initial
27329          * Fire after initEvent
27330          * @param {Roo.bootstrap.UploadCropbox} this
27331          */
27332         "initial" : true,
27333         /**
27334          * @event crop
27335          * Fire after initEvent
27336          * @param {Roo.bootstrap.UploadCropbox} this
27337          * @param {String} data
27338          */
27339         "crop" : true,
27340         /**
27341          * @event prepare
27342          * Fire when preparing the file data
27343          * @param {Roo.bootstrap.UploadCropbox} this
27344          * @param {Object} file
27345          */
27346         "prepare" : true,
27347         /**
27348          * @event exception
27349          * Fire when get exception
27350          * @param {Roo.bootstrap.UploadCropbox} this
27351          * @param {XMLHttpRequest} xhr
27352          */
27353         "exception" : true,
27354         /**
27355          * @event beforeloadcanvas
27356          * Fire before load the canvas
27357          * @param {Roo.bootstrap.UploadCropbox} this
27358          * @param {String} src
27359          */
27360         "beforeloadcanvas" : true,
27361         /**
27362          * @event trash
27363          * Fire when trash image
27364          * @param {Roo.bootstrap.UploadCropbox} this
27365          */
27366         "trash" : true,
27367         /**
27368          * @event download
27369          * Fire when download the image
27370          * @param {Roo.bootstrap.UploadCropbox} this
27371          */
27372         "download" : true,
27373         /**
27374          * @event footerbuttonclick
27375          * Fire when footerbuttonclick
27376          * @param {Roo.bootstrap.UploadCropbox} this
27377          * @param {String} type
27378          */
27379         "footerbuttonclick" : true,
27380         /**
27381          * @event resize
27382          * Fire when resize
27383          * @param {Roo.bootstrap.UploadCropbox} this
27384          */
27385         "resize" : true,
27386         /**
27387          * @event rotate
27388          * Fire when rotate the image
27389          * @param {Roo.bootstrap.UploadCropbox} this
27390          * @param {String} pos
27391          */
27392         "rotate" : true,
27393         /**
27394          * @event inspect
27395          * Fire when inspect the file
27396          * @param {Roo.bootstrap.UploadCropbox} this
27397          * @param {Object} file
27398          */
27399         "inspect" : true,
27400         /**
27401          * @event upload
27402          * Fire when xhr upload the file
27403          * @param {Roo.bootstrap.UploadCropbox} this
27404          * @param {Object} data
27405          */
27406         "upload" : true,
27407         /**
27408          * @event arrange
27409          * Fire when arrange the file data
27410          * @param {Roo.bootstrap.UploadCropbox} this
27411          * @param {Object} formData
27412          */
27413         "arrange" : true
27414     });
27415     
27416     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27417 };
27418
27419 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27420     
27421     emptyText : 'Click to upload image',
27422     rotateNotify : 'Image is too small to rotate',
27423     errorTimeout : 3000,
27424     scale : 0,
27425     baseScale : 1,
27426     rotate : 0,
27427     dragable : false,
27428     pinching : false,
27429     mouseX : 0,
27430     mouseY : 0,
27431     cropData : false,
27432     minWidth : 300,
27433     minHeight : 300,
27434     file : false,
27435     exif : {},
27436     baseRotate : 1,
27437     cropType : 'image/jpeg',
27438     buttons : false,
27439     canvasLoaded : false,
27440     isDocument : false,
27441     method : 'POST',
27442     paramName : 'imageUpload',
27443     loadMask : true,
27444     loadingText : 'Loading...',
27445     maskEl : false,
27446     
27447     getAutoCreate : function()
27448     {
27449         var cfg = {
27450             tag : 'div',
27451             cls : 'roo-upload-cropbox',
27452             cn : [
27453                 {
27454                     tag : 'input',
27455                     cls : 'roo-upload-cropbox-selector',
27456                     type : 'file'
27457                 },
27458                 {
27459                     tag : 'div',
27460                     cls : 'roo-upload-cropbox-body',
27461                     style : 'cursor:pointer',
27462                     cn : [
27463                         {
27464                             tag : 'div',
27465                             cls : 'roo-upload-cropbox-preview'
27466                         },
27467                         {
27468                             tag : 'div',
27469                             cls : 'roo-upload-cropbox-thumb'
27470                         },
27471                         {
27472                             tag : 'div',
27473                             cls : 'roo-upload-cropbox-empty-notify',
27474                             html : this.emptyText
27475                         },
27476                         {
27477                             tag : 'div',
27478                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27479                             html : this.rotateNotify
27480                         }
27481                     ]
27482                 },
27483                 {
27484                     tag : 'div',
27485                     cls : 'roo-upload-cropbox-footer',
27486                     cn : {
27487                         tag : 'div',
27488                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27489                         cn : []
27490                     }
27491                 }
27492             ]
27493         };
27494         
27495         return cfg;
27496     },
27497     
27498     onRender : function(ct, position)
27499     {
27500         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27501         
27502         if (this.buttons.length) {
27503             
27504             Roo.each(this.buttons, function(bb) {
27505                 
27506                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27507                 
27508                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27509                 
27510             }, this);
27511         }
27512         
27513         if(this.loadMask){
27514             this.maskEl = this.el;
27515         }
27516     },
27517     
27518     initEvents : function()
27519     {
27520         this.urlAPI = (window.createObjectURL && window) || 
27521                                 (window.URL && URL.revokeObjectURL && URL) || 
27522                                 (window.webkitURL && webkitURL);
27523                         
27524         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27525         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27526         
27527         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27528         this.selectorEl.hide();
27529         
27530         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27531         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27532         
27533         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27534         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27535         this.thumbEl.hide();
27536         
27537         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27538         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27539         
27540         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27541         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27542         this.errorEl.hide();
27543         
27544         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27545         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27546         this.footerEl.hide();
27547         
27548         this.setThumbBoxSize();
27549         
27550         this.bind();
27551         
27552         this.resize();
27553         
27554         this.fireEvent('initial', this);
27555     },
27556
27557     bind : function()
27558     {
27559         var _this = this;
27560         
27561         window.addEventListener("resize", function() { _this.resize(); } );
27562         
27563         this.bodyEl.on('click', this.beforeSelectFile, this);
27564         
27565         if(Roo.isTouch){
27566             this.bodyEl.on('touchstart', this.onTouchStart, this);
27567             this.bodyEl.on('touchmove', this.onTouchMove, this);
27568             this.bodyEl.on('touchend', this.onTouchEnd, this);
27569         }
27570         
27571         if(!Roo.isTouch){
27572             this.bodyEl.on('mousedown', this.onMouseDown, this);
27573             this.bodyEl.on('mousemove', this.onMouseMove, this);
27574             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27575             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27576             Roo.get(document).on('mouseup', this.onMouseUp, this);
27577         }
27578         
27579         this.selectorEl.on('change', this.onFileSelected, this);
27580     },
27581     
27582     reset : function()
27583     {    
27584         this.scale = 0;
27585         this.baseScale = 1;
27586         this.rotate = 0;
27587         this.baseRotate = 1;
27588         this.dragable = false;
27589         this.pinching = false;
27590         this.mouseX = 0;
27591         this.mouseY = 0;
27592         this.cropData = false;
27593         this.notifyEl.dom.innerHTML = this.emptyText;
27594         
27595         this.selectorEl.dom.value = '';
27596         
27597     },
27598     
27599     resize : function()
27600     {
27601         if(this.fireEvent('resize', this) != false){
27602             this.setThumbBoxPosition();
27603             this.setCanvasPosition();
27604         }
27605     },
27606     
27607     onFooterButtonClick : function(e, el, o, type)
27608     {
27609         switch (type) {
27610             case 'rotate-left' :
27611                 this.onRotateLeft(e);
27612                 break;
27613             case 'rotate-right' :
27614                 this.onRotateRight(e);
27615                 break;
27616             case 'picture' :
27617                 this.beforeSelectFile(e);
27618                 break;
27619             case 'trash' :
27620                 this.trash(e);
27621                 break;
27622             case 'crop' :
27623                 this.crop(e);
27624                 break;
27625             case 'download' :
27626                 this.download(e);
27627                 break;
27628             default :
27629                 break;
27630         }
27631         
27632         this.fireEvent('footerbuttonclick', this, type);
27633     },
27634     
27635     beforeSelectFile : function(e)
27636     {
27637         e.preventDefault();
27638         
27639         if(this.fireEvent('beforeselectfile', this) != false){
27640             this.selectorEl.dom.click();
27641         }
27642     },
27643     
27644     onFileSelected : function(e)
27645     {
27646         e.preventDefault();
27647         
27648         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27649             return;
27650         }
27651         
27652         var file = this.selectorEl.dom.files[0];
27653         
27654         if(this.fireEvent('inspect', this, file) != false){
27655             this.prepare(file);
27656         }
27657         
27658     },
27659     
27660     trash : function(e)
27661     {
27662         this.fireEvent('trash', this);
27663     },
27664     
27665     download : function(e)
27666     {
27667         this.fireEvent('download', this);
27668     },
27669     
27670     loadCanvas : function(src)
27671     {   
27672         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27673             
27674             this.reset();
27675             
27676             this.imageEl = document.createElement('img');
27677             
27678             var _this = this;
27679             
27680             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27681             
27682             this.imageEl.src = src;
27683         }
27684     },
27685     
27686     onLoadCanvas : function()
27687     {   
27688         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27689         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27690         
27691         this.bodyEl.un('click', this.beforeSelectFile, this);
27692         
27693         this.notifyEl.hide();
27694         this.thumbEl.show();
27695         this.footerEl.show();
27696         
27697         this.baseRotateLevel();
27698         
27699         if(this.isDocument){
27700             this.setThumbBoxSize();
27701         }
27702         
27703         this.setThumbBoxPosition();
27704         
27705         this.baseScaleLevel();
27706         
27707         this.draw();
27708         
27709         this.resize();
27710         
27711         this.canvasLoaded = true;
27712         
27713         if(this.loadMask){
27714             this.maskEl.unmask();
27715         }
27716         
27717     },
27718     
27719     setCanvasPosition : function()
27720     {   
27721         if(!this.canvasEl){
27722             return;
27723         }
27724         
27725         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27726         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27727         
27728         this.previewEl.setLeft(pw);
27729         this.previewEl.setTop(ph);
27730         
27731     },
27732     
27733     onMouseDown : function(e)
27734     {   
27735         e.stopEvent();
27736         
27737         this.dragable = true;
27738         this.pinching = false;
27739         
27740         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27741             this.dragable = false;
27742             return;
27743         }
27744         
27745         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27746         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27747         
27748     },
27749     
27750     onMouseMove : function(e)
27751     {   
27752         e.stopEvent();
27753         
27754         if(!this.canvasLoaded){
27755             return;
27756         }
27757         
27758         if (!this.dragable){
27759             return;
27760         }
27761         
27762         var minX = Math.ceil(this.thumbEl.getLeft(true));
27763         var minY = Math.ceil(this.thumbEl.getTop(true));
27764         
27765         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27766         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27767         
27768         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27769         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27770         
27771         x = x - this.mouseX;
27772         y = y - this.mouseY;
27773         
27774         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27775         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27776         
27777         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27778         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27779         
27780         this.previewEl.setLeft(bgX);
27781         this.previewEl.setTop(bgY);
27782         
27783         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27784         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27785     },
27786     
27787     onMouseUp : function(e)
27788     {   
27789         e.stopEvent();
27790         
27791         this.dragable = false;
27792     },
27793     
27794     onMouseWheel : function(e)
27795     {   
27796         e.stopEvent();
27797         
27798         this.startScale = this.scale;
27799         
27800         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27801         
27802         if(!this.zoomable()){
27803             this.scale = this.startScale;
27804             return;
27805         }
27806         
27807         this.draw();
27808         
27809         return;
27810     },
27811     
27812     zoomable : function()
27813     {
27814         var minScale = this.thumbEl.getWidth() / this.minWidth;
27815         
27816         if(this.minWidth < this.minHeight){
27817             minScale = this.thumbEl.getHeight() / this.minHeight;
27818         }
27819         
27820         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27821         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27822         
27823         if(
27824                 this.isDocument &&
27825                 (this.rotate == 0 || this.rotate == 180) && 
27826                 (
27827                     width > this.imageEl.OriginWidth || 
27828                     height > this.imageEl.OriginHeight ||
27829                     (width < this.minWidth && height < this.minHeight)
27830                 )
27831         ){
27832             return false;
27833         }
27834         
27835         if(
27836                 this.isDocument &&
27837                 (this.rotate == 90 || this.rotate == 270) && 
27838                 (
27839                     width > this.imageEl.OriginWidth || 
27840                     height > this.imageEl.OriginHeight ||
27841                     (width < this.minHeight && height < this.minWidth)
27842                 )
27843         ){
27844             return false;
27845         }
27846         
27847         if(
27848                 !this.isDocument &&
27849                 (this.rotate == 0 || this.rotate == 180) && 
27850                 (
27851                     width < this.minWidth || 
27852                     width > this.imageEl.OriginWidth || 
27853                     height < this.minHeight || 
27854                     height > this.imageEl.OriginHeight
27855                 )
27856         ){
27857             return false;
27858         }
27859         
27860         if(
27861                 !this.isDocument &&
27862                 (this.rotate == 90 || this.rotate == 270) && 
27863                 (
27864                     width < this.minHeight || 
27865                     width > this.imageEl.OriginWidth || 
27866                     height < this.minWidth || 
27867                     height > this.imageEl.OriginHeight
27868                 )
27869         ){
27870             return false;
27871         }
27872         
27873         return true;
27874         
27875     },
27876     
27877     onRotateLeft : function(e)
27878     {   
27879         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27880             
27881             var minScale = this.thumbEl.getWidth() / this.minWidth;
27882             
27883             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27884             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27885             
27886             this.startScale = this.scale;
27887             
27888             while (this.getScaleLevel() < minScale){
27889             
27890                 this.scale = this.scale + 1;
27891                 
27892                 if(!this.zoomable()){
27893                     break;
27894                 }
27895                 
27896                 if(
27897                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27898                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27899                 ){
27900                     continue;
27901                 }
27902                 
27903                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27904
27905                 this.draw();
27906                 
27907                 return;
27908             }
27909             
27910             this.scale = this.startScale;
27911             
27912             this.onRotateFail();
27913             
27914             return false;
27915         }
27916         
27917         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27918
27919         if(this.isDocument){
27920             this.setThumbBoxSize();
27921             this.setThumbBoxPosition();
27922             this.setCanvasPosition();
27923         }
27924         
27925         this.draw();
27926         
27927         this.fireEvent('rotate', this, 'left');
27928         
27929     },
27930     
27931     onRotateRight : function(e)
27932     {
27933         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27934             
27935             var minScale = this.thumbEl.getWidth() / this.minWidth;
27936         
27937             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27938             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27939             
27940             this.startScale = this.scale;
27941             
27942             while (this.getScaleLevel() < minScale){
27943             
27944                 this.scale = this.scale + 1;
27945                 
27946                 if(!this.zoomable()){
27947                     break;
27948                 }
27949                 
27950                 if(
27951                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27952                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27953                 ){
27954                     continue;
27955                 }
27956                 
27957                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27958
27959                 this.draw();
27960                 
27961                 return;
27962             }
27963             
27964             this.scale = this.startScale;
27965             
27966             this.onRotateFail();
27967             
27968             return false;
27969         }
27970         
27971         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27972
27973         if(this.isDocument){
27974             this.setThumbBoxSize();
27975             this.setThumbBoxPosition();
27976             this.setCanvasPosition();
27977         }
27978         
27979         this.draw();
27980         
27981         this.fireEvent('rotate', this, 'right');
27982     },
27983     
27984     onRotateFail : function()
27985     {
27986         this.errorEl.show(true);
27987         
27988         var _this = this;
27989         
27990         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27991     },
27992     
27993     draw : function()
27994     {
27995         this.previewEl.dom.innerHTML = '';
27996         
27997         var canvasEl = document.createElement("canvas");
27998         
27999         var contextEl = canvasEl.getContext("2d");
28000         
28001         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28002         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28003         var center = this.imageEl.OriginWidth / 2;
28004         
28005         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28006             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28007             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28008             center = this.imageEl.OriginHeight / 2;
28009         }
28010         
28011         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28012         
28013         contextEl.translate(center, center);
28014         contextEl.rotate(this.rotate * Math.PI / 180);
28015
28016         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28017         
28018         this.canvasEl = document.createElement("canvas");
28019         
28020         this.contextEl = this.canvasEl.getContext("2d");
28021         
28022         switch (this.rotate) {
28023             case 0 :
28024                 
28025                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28026                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28027                 
28028                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28029                 
28030                 break;
28031             case 90 : 
28032                 
28033                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28034                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28035                 
28036                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28037                     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);
28038                     break;
28039                 }
28040                 
28041                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28042                 
28043                 break;
28044             case 180 :
28045                 
28046                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28047                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28048                 
28049                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28050                     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);
28051                     break;
28052                 }
28053                 
28054                 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);
28055                 
28056                 break;
28057             case 270 :
28058                 
28059                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28060                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28061         
28062                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28063                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28064                     break;
28065                 }
28066                 
28067                 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);
28068                 
28069                 break;
28070             default : 
28071                 break;
28072         }
28073         
28074         this.previewEl.appendChild(this.canvasEl);
28075         
28076         this.setCanvasPosition();
28077     },
28078     
28079     crop : function()
28080     {
28081         if(!this.canvasLoaded){
28082             return;
28083         }
28084         
28085         var imageCanvas = document.createElement("canvas");
28086         
28087         var imageContext = imageCanvas.getContext("2d");
28088         
28089         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28090         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28091         
28092         var center = imageCanvas.width / 2;
28093         
28094         imageContext.translate(center, center);
28095         
28096         imageContext.rotate(this.rotate * Math.PI / 180);
28097         
28098         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28099         
28100         var canvas = document.createElement("canvas");
28101         
28102         var context = canvas.getContext("2d");
28103                 
28104         canvas.width = this.minWidth;
28105         canvas.height = this.minHeight;
28106
28107         switch (this.rotate) {
28108             case 0 :
28109                 
28110                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28111                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28112                 
28113                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28114                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28115                 
28116                 var targetWidth = this.minWidth - 2 * x;
28117                 var targetHeight = this.minHeight - 2 * y;
28118                 
28119                 var scale = 1;
28120                 
28121                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28122                     scale = targetWidth / width;
28123                 }
28124                 
28125                 if(x > 0 && y == 0){
28126                     scale = targetHeight / height;
28127                 }
28128                 
28129                 if(x > 0 && y > 0){
28130                     scale = targetWidth / width;
28131                     
28132                     if(width < height){
28133                         scale = targetHeight / height;
28134                     }
28135                 }
28136                 
28137                 context.scale(scale, scale);
28138                 
28139                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28140                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28141
28142                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28143                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28144
28145                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28146                 
28147                 break;
28148             case 90 : 
28149                 
28150                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28151                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28152                 
28153                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28154                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28155                 
28156                 var targetWidth = this.minWidth - 2 * x;
28157                 var targetHeight = this.minHeight - 2 * y;
28158                 
28159                 var scale = 1;
28160                 
28161                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28162                     scale = targetWidth / width;
28163                 }
28164                 
28165                 if(x > 0 && y == 0){
28166                     scale = targetHeight / height;
28167                 }
28168                 
28169                 if(x > 0 && y > 0){
28170                     scale = targetWidth / width;
28171                     
28172                     if(width < height){
28173                         scale = targetHeight / height;
28174                     }
28175                 }
28176                 
28177                 context.scale(scale, scale);
28178                 
28179                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28180                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28181
28182                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28183                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28184                 
28185                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28186                 
28187                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28188                 
28189                 break;
28190             case 180 :
28191                 
28192                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28193                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28194                 
28195                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28196                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28197                 
28198                 var targetWidth = this.minWidth - 2 * x;
28199                 var targetHeight = this.minHeight - 2 * y;
28200                 
28201                 var scale = 1;
28202                 
28203                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28204                     scale = targetWidth / width;
28205                 }
28206                 
28207                 if(x > 0 && y == 0){
28208                     scale = targetHeight / height;
28209                 }
28210                 
28211                 if(x > 0 && y > 0){
28212                     scale = targetWidth / width;
28213                     
28214                     if(width < height){
28215                         scale = targetHeight / height;
28216                     }
28217                 }
28218                 
28219                 context.scale(scale, scale);
28220                 
28221                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28222                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28223
28224                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28225                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28226
28227                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28228                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28229                 
28230                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28231                 
28232                 break;
28233             case 270 :
28234                 
28235                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28236                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28237                 
28238                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28239                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28240                 
28241                 var targetWidth = this.minWidth - 2 * x;
28242                 var targetHeight = this.minHeight - 2 * y;
28243                 
28244                 var scale = 1;
28245                 
28246                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28247                     scale = targetWidth / width;
28248                 }
28249                 
28250                 if(x > 0 && y == 0){
28251                     scale = targetHeight / height;
28252                 }
28253                 
28254                 if(x > 0 && y > 0){
28255                     scale = targetWidth / width;
28256                     
28257                     if(width < height){
28258                         scale = targetHeight / height;
28259                     }
28260                 }
28261                 
28262                 context.scale(scale, scale);
28263                 
28264                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28265                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28266
28267                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28268                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28269                 
28270                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28271                 
28272                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28273                 
28274                 break;
28275             default : 
28276                 break;
28277         }
28278         
28279         this.cropData = canvas.toDataURL(this.cropType);
28280         
28281         if(this.fireEvent('crop', this, this.cropData) !== false){
28282             this.process(this.file, this.cropData);
28283         }
28284         
28285         return;
28286         
28287     },
28288     
28289     setThumbBoxSize : function()
28290     {
28291         var width, height;
28292         
28293         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28294             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28295             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28296             
28297             this.minWidth = width;
28298             this.minHeight = height;
28299             
28300             if(this.rotate == 90 || this.rotate == 270){
28301                 this.minWidth = height;
28302                 this.minHeight = width;
28303             }
28304         }
28305         
28306         height = 300;
28307         width = Math.ceil(this.minWidth * height / this.minHeight);
28308         
28309         if(this.minWidth > this.minHeight){
28310             width = 300;
28311             height = Math.ceil(this.minHeight * width / this.minWidth);
28312         }
28313         
28314         this.thumbEl.setStyle({
28315             width : width + 'px',
28316             height : height + 'px'
28317         });
28318
28319         return;
28320             
28321     },
28322     
28323     setThumbBoxPosition : function()
28324     {
28325         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28326         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28327         
28328         this.thumbEl.setLeft(x);
28329         this.thumbEl.setTop(y);
28330         
28331     },
28332     
28333     baseRotateLevel : function()
28334     {
28335         this.baseRotate = 1;
28336         
28337         if(
28338                 typeof(this.exif) != 'undefined' &&
28339                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28340                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28341         ){
28342             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28343         }
28344         
28345         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28346         
28347     },
28348     
28349     baseScaleLevel : function()
28350     {
28351         var width, height;
28352         
28353         if(this.isDocument){
28354             
28355             if(this.baseRotate == 6 || this.baseRotate == 8){
28356             
28357                 height = this.thumbEl.getHeight();
28358                 this.baseScale = height / this.imageEl.OriginWidth;
28359
28360                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28361                     width = this.thumbEl.getWidth();
28362                     this.baseScale = width / this.imageEl.OriginHeight;
28363                 }
28364
28365                 return;
28366             }
28367
28368             height = this.thumbEl.getHeight();
28369             this.baseScale = height / this.imageEl.OriginHeight;
28370
28371             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28372                 width = this.thumbEl.getWidth();
28373                 this.baseScale = width / this.imageEl.OriginWidth;
28374             }
28375
28376             return;
28377         }
28378         
28379         if(this.baseRotate == 6 || this.baseRotate == 8){
28380             
28381             width = this.thumbEl.getHeight();
28382             this.baseScale = width / this.imageEl.OriginHeight;
28383             
28384             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28385                 height = this.thumbEl.getWidth();
28386                 this.baseScale = height / this.imageEl.OriginHeight;
28387             }
28388             
28389             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28390                 height = this.thumbEl.getWidth();
28391                 this.baseScale = height / this.imageEl.OriginHeight;
28392                 
28393                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28394                     width = this.thumbEl.getHeight();
28395                     this.baseScale = width / this.imageEl.OriginWidth;
28396                 }
28397             }
28398             
28399             return;
28400         }
28401         
28402         width = this.thumbEl.getWidth();
28403         this.baseScale = width / this.imageEl.OriginWidth;
28404         
28405         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28406             height = this.thumbEl.getHeight();
28407             this.baseScale = height / this.imageEl.OriginHeight;
28408         }
28409         
28410         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28411             
28412             height = this.thumbEl.getHeight();
28413             this.baseScale = height / this.imageEl.OriginHeight;
28414             
28415             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28416                 width = this.thumbEl.getWidth();
28417                 this.baseScale = width / this.imageEl.OriginWidth;
28418             }
28419             
28420         }
28421         
28422         return;
28423     },
28424     
28425     getScaleLevel : function()
28426     {
28427         return this.baseScale * Math.pow(1.1, this.scale);
28428     },
28429     
28430     onTouchStart : function(e)
28431     {
28432         if(!this.canvasLoaded){
28433             this.beforeSelectFile(e);
28434             return;
28435         }
28436         
28437         var touches = e.browserEvent.touches;
28438         
28439         if(!touches){
28440             return;
28441         }
28442         
28443         if(touches.length == 1){
28444             this.onMouseDown(e);
28445             return;
28446         }
28447         
28448         if(touches.length != 2){
28449             return;
28450         }
28451         
28452         var coords = [];
28453         
28454         for(var i = 0, finger; finger = touches[i]; i++){
28455             coords.push(finger.pageX, finger.pageY);
28456         }
28457         
28458         var x = Math.pow(coords[0] - coords[2], 2);
28459         var y = Math.pow(coords[1] - coords[3], 2);
28460         
28461         this.startDistance = Math.sqrt(x + y);
28462         
28463         this.startScale = this.scale;
28464         
28465         this.pinching = true;
28466         this.dragable = false;
28467         
28468     },
28469     
28470     onTouchMove : function(e)
28471     {
28472         if(!this.pinching && !this.dragable){
28473             return;
28474         }
28475         
28476         var touches = e.browserEvent.touches;
28477         
28478         if(!touches){
28479             return;
28480         }
28481         
28482         if(this.dragable){
28483             this.onMouseMove(e);
28484             return;
28485         }
28486         
28487         var coords = [];
28488         
28489         for(var i = 0, finger; finger = touches[i]; i++){
28490             coords.push(finger.pageX, finger.pageY);
28491         }
28492         
28493         var x = Math.pow(coords[0] - coords[2], 2);
28494         var y = Math.pow(coords[1] - coords[3], 2);
28495         
28496         this.endDistance = Math.sqrt(x + y);
28497         
28498         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28499         
28500         if(!this.zoomable()){
28501             this.scale = this.startScale;
28502             return;
28503         }
28504         
28505         this.draw();
28506         
28507     },
28508     
28509     onTouchEnd : function(e)
28510     {
28511         this.pinching = false;
28512         this.dragable = false;
28513         
28514     },
28515     
28516     process : function(file, crop)
28517     {
28518         if(this.loadMask){
28519             this.maskEl.mask(this.loadingText);
28520         }
28521         
28522         this.xhr = new XMLHttpRequest();
28523         
28524         file.xhr = this.xhr;
28525
28526         this.xhr.open(this.method, this.url, true);
28527         
28528         var headers = {
28529             "Accept": "application/json",
28530             "Cache-Control": "no-cache",
28531             "X-Requested-With": "XMLHttpRequest"
28532         };
28533         
28534         for (var headerName in headers) {
28535             var headerValue = headers[headerName];
28536             if (headerValue) {
28537                 this.xhr.setRequestHeader(headerName, headerValue);
28538             }
28539         }
28540         
28541         var _this = this;
28542         
28543         this.xhr.onload = function()
28544         {
28545             _this.xhrOnLoad(_this.xhr);
28546         }
28547         
28548         this.xhr.onerror = function()
28549         {
28550             _this.xhrOnError(_this.xhr);
28551         }
28552         
28553         var formData = new FormData();
28554
28555         formData.append('returnHTML', 'NO');
28556         
28557         if(crop){
28558             formData.append('crop', crop);
28559         }
28560         
28561         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28562             formData.append(this.paramName, file, file.name);
28563         }
28564         
28565         if(typeof(file.filename) != 'undefined'){
28566             formData.append('filename', file.filename);
28567         }
28568         
28569         if(typeof(file.mimetype) != 'undefined'){
28570             formData.append('mimetype', file.mimetype);
28571         }
28572         
28573         if(this.fireEvent('arrange', this, formData) != false){
28574             this.xhr.send(formData);
28575         };
28576     },
28577     
28578     xhrOnLoad : function(xhr)
28579     {
28580         if(this.loadMask){
28581             this.maskEl.unmask();
28582         }
28583         
28584         if (xhr.readyState !== 4) {
28585             this.fireEvent('exception', this, xhr);
28586             return;
28587         }
28588
28589         var response = Roo.decode(xhr.responseText);
28590         
28591         if(!response.success){
28592             this.fireEvent('exception', this, xhr);
28593             return;
28594         }
28595         
28596         var response = Roo.decode(xhr.responseText);
28597         
28598         this.fireEvent('upload', this, response);
28599         
28600     },
28601     
28602     xhrOnError : function()
28603     {
28604         if(this.loadMask){
28605             this.maskEl.unmask();
28606         }
28607         
28608         Roo.log('xhr on error');
28609         
28610         var response = Roo.decode(xhr.responseText);
28611           
28612         Roo.log(response);
28613         
28614     },
28615     
28616     prepare : function(file)
28617     {   
28618         if(this.loadMask){
28619             this.maskEl.mask(this.loadingText);
28620         }
28621         
28622         this.file = false;
28623         this.exif = {};
28624         
28625         if(typeof(file) === 'string'){
28626             this.loadCanvas(file);
28627             return;
28628         }
28629         
28630         if(!file || !this.urlAPI){
28631             return;
28632         }
28633         
28634         this.file = file;
28635         this.cropType = file.type;
28636         
28637         var _this = this;
28638         
28639         if(this.fireEvent('prepare', this, this.file) != false){
28640             
28641             var reader = new FileReader();
28642             
28643             reader.onload = function (e) {
28644                 if (e.target.error) {
28645                     Roo.log(e.target.error);
28646                     return;
28647                 }
28648                 
28649                 var buffer = e.target.result,
28650                     dataView = new DataView(buffer),
28651                     offset = 2,
28652                     maxOffset = dataView.byteLength - 4,
28653                     markerBytes,
28654                     markerLength;
28655                 
28656                 if (dataView.getUint16(0) === 0xffd8) {
28657                     while (offset < maxOffset) {
28658                         markerBytes = dataView.getUint16(offset);
28659                         
28660                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28661                             markerLength = dataView.getUint16(offset + 2) + 2;
28662                             if (offset + markerLength > dataView.byteLength) {
28663                                 Roo.log('Invalid meta data: Invalid segment size.');
28664                                 break;
28665                             }
28666                             
28667                             if(markerBytes == 0xffe1){
28668                                 _this.parseExifData(
28669                                     dataView,
28670                                     offset,
28671                                     markerLength
28672                                 );
28673                             }
28674                             
28675                             offset += markerLength;
28676                             
28677                             continue;
28678                         }
28679                         
28680                         break;
28681                     }
28682                     
28683                 }
28684                 
28685                 var url = _this.urlAPI.createObjectURL(_this.file);
28686                 
28687                 _this.loadCanvas(url);
28688                 
28689                 return;
28690             }
28691             
28692             reader.readAsArrayBuffer(this.file);
28693             
28694         }
28695         
28696     },
28697     
28698     parseExifData : function(dataView, offset, length)
28699     {
28700         var tiffOffset = offset + 10,
28701             littleEndian,
28702             dirOffset;
28703     
28704         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28705             // No Exif data, might be XMP data instead
28706             return;
28707         }
28708         
28709         // Check for the ASCII code for "Exif" (0x45786966):
28710         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28711             // No Exif data, might be XMP data instead
28712             return;
28713         }
28714         if (tiffOffset + 8 > dataView.byteLength) {
28715             Roo.log('Invalid Exif data: Invalid segment size.');
28716             return;
28717         }
28718         // Check for the two null bytes:
28719         if (dataView.getUint16(offset + 8) !== 0x0000) {
28720             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28721             return;
28722         }
28723         // Check the byte alignment:
28724         switch (dataView.getUint16(tiffOffset)) {
28725         case 0x4949:
28726             littleEndian = true;
28727             break;
28728         case 0x4D4D:
28729             littleEndian = false;
28730             break;
28731         default:
28732             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28733             return;
28734         }
28735         // Check for the TIFF tag marker (0x002A):
28736         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28737             Roo.log('Invalid Exif data: Missing TIFF marker.');
28738             return;
28739         }
28740         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28741         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28742         
28743         this.parseExifTags(
28744             dataView,
28745             tiffOffset,
28746             tiffOffset + dirOffset,
28747             littleEndian
28748         );
28749     },
28750     
28751     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28752     {
28753         var tagsNumber,
28754             dirEndOffset,
28755             i;
28756         if (dirOffset + 6 > dataView.byteLength) {
28757             Roo.log('Invalid Exif data: Invalid directory offset.');
28758             return;
28759         }
28760         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28761         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28762         if (dirEndOffset + 4 > dataView.byteLength) {
28763             Roo.log('Invalid Exif data: Invalid directory size.');
28764             return;
28765         }
28766         for (i = 0; i < tagsNumber; i += 1) {
28767             this.parseExifTag(
28768                 dataView,
28769                 tiffOffset,
28770                 dirOffset + 2 + 12 * i, // tag offset
28771                 littleEndian
28772             );
28773         }
28774         // Return the offset to the next directory:
28775         return dataView.getUint32(dirEndOffset, littleEndian);
28776     },
28777     
28778     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28779     {
28780         var tag = dataView.getUint16(offset, littleEndian);
28781         
28782         this.exif[tag] = this.getExifValue(
28783             dataView,
28784             tiffOffset,
28785             offset,
28786             dataView.getUint16(offset + 2, littleEndian), // tag type
28787             dataView.getUint32(offset + 4, littleEndian), // tag length
28788             littleEndian
28789         );
28790     },
28791     
28792     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28793     {
28794         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28795             tagSize,
28796             dataOffset,
28797             values,
28798             i,
28799             str,
28800             c;
28801     
28802         if (!tagType) {
28803             Roo.log('Invalid Exif data: Invalid tag type.');
28804             return;
28805         }
28806         
28807         tagSize = tagType.size * length;
28808         // Determine if the value is contained in the dataOffset bytes,
28809         // or if the value at the dataOffset is a pointer to the actual data:
28810         dataOffset = tagSize > 4 ?
28811                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28812         if (dataOffset + tagSize > dataView.byteLength) {
28813             Roo.log('Invalid Exif data: Invalid data offset.');
28814             return;
28815         }
28816         if (length === 1) {
28817             return tagType.getValue(dataView, dataOffset, littleEndian);
28818         }
28819         values = [];
28820         for (i = 0; i < length; i += 1) {
28821             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28822         }
28823         
28824         if (tagType.ascii) {
28825             str = '';
28826             // Concatenate the chars:
28827             for (i = 0; i < values.length; i += 1) {
28828                 c = values[i];
28829                 // Ignore the terminating NULL byte(s):
28830                 if (c === '\u0000') {
28831                     break;
28832                 }
28833                 str += c;
28834             }
28835             return str;
28836         }
28837         return values;
28838     }
28839     
28840 });
28841
28842 Roo.apply(Roo.bootstrap.UploadCropbox, {
28843     tags : {
28844         'Orientation': 0x0112
28845     },
28846     
28847     Orientation: {
28848             1: 0, //'top-left',
28849 //            2: 'top-right',
28850             3: 180, //'bottom-right',
28851 //            4: 'bottom-left',
28852 //            5: 'left-top',
28853             6: 90, //'right-top',
28854 //            7: 'right-bottom',
28855             8: 270 //'left-bottom'
28856     },
28857     
28858     exifTagTypes : {
28859         // byte, 8-bit unsigned int:
28860         1: {
28861             getValue: function (dataView, dataOffset) {
28862                 return dataView.getUint8(dataOffset);
28863             },
28864             size: 1
28865         },
28866         // ascii, 8-bit byte:
28867         2: {
28868             getValue: function (dataView, dataOffset) {
28869                 return String.fromCharCode(dataView.getUint8(dataOffset));
28870             },
28871             size: 1,
28872             ascii: true
28873         },
28874         // short, 16 bit int:
28875         3: {
28876             getValue: function (dataView, dataOffset, littleEndian) {
28877                 return dataView.getUint16(dataOffset, littleEndian);
28878             },
28879             size: 2
28880         },
28881         // long, 32 bit int:
28882         4: {
28883             getValue: function (dataView, dataOffset, littleEndian) {
28884                 return dataView.getUint32(dataOffset, littleEndian);
28885             },
28886             size: 4
28887         },
28888         // rational = two long values, first is numerator, second is denominator:
28889         5: {
28890             getValue: function (dataView, dataOffset, littleEndian) {
28891                 return dataView.getUint32(dataOffset, littleEndian) /
28892                     dataView.getUint32(dataOffset + 4, littleEndian);
28893             },
28894             size: 8
28895         },
28896         // slong, 32 bit signed int:
28897         9: {
28898             getValue: function (dataView, dataOffset, littleEndian) {
28899                 return dataView.getInt32(dataOffset, littleEndian);
28900             },
28901             size: 4
28902         },
28903         // srational, two slongs, first is numerator, second is denominator:
28904         10: {
28905             getValue: function (dataView, dataOffset, littleEndian) {
28906                 return dataView.getInt32(dataOffset, littleEndian) /
28907                     dataView.getInt32(dataOffset + 4, littleEndian);
28908             },
28909             size: 8
28910         }
28911     },
28912     
28913     footer : {
28914         STANDARD : [
28915             {
28916                 tag : 'div',
28917                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28918                 action : 'rotate-left',
28919                 cn : [
28920                     {
28921                         tag : 'button',
28922                         cls : 'btn btn-default',
28923                         html : '<i class="fa fa-undo"></i>'
28924                     }
28925                 ]
28926             },
28927             {
28928                 tag : 'div',
28929                 cls : 'btn-group roo-upload-cropbox-picture',
28930                 action : 'picture',
28931                 cn : [
28932                     {
28933                         tag : 'button',
28934                         cls : 'btn btn-default',
28935                         html : '<i class="fa fa-picture-o"></i>'
28936                     }
28937                 ]
28938             },
28939             {
28940                 tag : 'div',
28941                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28942                 action : 'rotate-right',
28943                 cn : [
28944                     {
28945                         tag : 'button',
28946                         cls : 'btn btn-default',
28947                         html : '<i class="fa fa-repeat"></i>'
28948                     }
28949                 ]
28950             }
28951         ],
28952         DOCUMENT : [
28953             {
28954                 tag : 'div',
28955                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28956                 action : 'rotate-left',
28957                 cn : [
28958                     {
28959                         tag : 'button',
28960                         cls : 'btn btn-default',
28961                         html : '<i class="fa fa-undo"></i>'
28962                     }
28963                 ]
28964             },
28965             {
28966                 tag : 'div',
28967                 cls : 'btn-group roo-upload-cropbox-download',
28968                 action : 'download',
28969                 cn : [
28970                     {
28971                         tag : 'button',
28972                         cls : 'btn btn-default',
28973                         html : '<i class="fa fa-download"></i>'
28974                     }
28975                 ]
28976             },
28977             {
28978                 tag : 'div',
28979                 cls : 'btn-group roo-upload-cropbox-crop',
28980                 action : 'crop',
28981                 cn : [
28982                     {
28983                         tag : 'button',
28984                         cls : 'btn btn-default',
28985                         html : '<i class="fa fa-crop"></i>'
28986                     }
28987                 ]
28988             },
28989             {
28990                 tag : 'div',
28991                 cls : 'btn-group roo-upload-cropbox-trash',
28992                 action : 'trash',
28993                 cn : [
28994                     {
28995                         tag : 'button',
28996                         cls : 'btn btn-default',
28997                         html : '<i class="fa fa-trash"></i>'
28998                     }
28999                 ]
29000             },
29001             {
29002                 tag : 'div',
29003                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29004                 action : 'rotate-right',
29005                 cn : [
29006                     {
29007                         tag : 'button',
29008                         cls : 'btn btn-default',
29009                         html : '<i class="fa fa-repeat"></i>'
29010                     }
29011                 ]
29012             }
29013         ],
29014         ROTATOR : [
29015             {
29016                 tag : 'div',
29017                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29018                 action : 'rotate-left',
29019                 cn : [
29020                     {
29021                         tag : 'button',
29022                         cls : 'btn btn-default',
29023                         html : '<i class="fa fa-undo"></i>'
29024                     }
29025                 ]
29026             },
29027             {
29028                 tag : 'div',
29029                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29030                 action : 'rotate-right',
29031                 cn : [
29032                     {
29033                         tag : 'button',
29034                         cls : 'btn btn-default',
29035                         html : '<i class="fa fa-repeat"></i>'
29036                     }
29037                 ]
29038             }
29039         ]
29040     }
29041 });
29042
29043 /*
29044 * Licence: LGPL
29045 */
29046
29047 /**
29048  * @class Roo.bootstrap.DocumentManager
29049  * @extends Roo.bootstrap.Component
29050  * Bootstrap DocumentManager class
29051  * @cfg {String} paramName default 'imageUpload'
29052  * @cfg {String} toolTipName default 'filename'
29053  * @cfg {String} method default POST
29054  * @cfg {String} url action url
29055  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29056  * @cfg {Boolean} multiple multiple upload default true
29057  * @cfg {Number} thumbSize default 300
29058  * @cfg {String} fieldLabel
29059  * @cfg {Number} labelWidth default 4
29060  * @cfg {String} labelAlign (left|top) default left
29061  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29062 * @cfg {Number} labellg set the width of label (1-12)
29063  * @cfg {Number} labelmd set the width of label (1-12)
29064  * @cfg {Number} labelsm set the width of label (1-12)
29065  * @cfg {Number} labelxs set the width of label (1-12)
29066  * 
29067  * @constructor
29068  * Create a new DocumentManager
29069  * @param {Object} config The config object
29070  */
29071
29072 Roo.bootstrap.DocumentManager = function(config){
29073     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29074     
29075     this.files = [];
29076     this.delegates = [];
29077     
29078     this.addEvents({
29079         /**
29080          * @event initial
29081          * Fire when initial the DocumentManager
29082          * @param {Roo.bootstrap.DocumentManager} this
29083          */
29084         "initial" : true,
29085         /**
29086          * @event inspect
29087          * inspect selected file
29088          * @param {Roo.bootstrap.DocumentManager} this
29089          * @param {File} file
29090          */
29091         "inspect" : true,
29092         /**
29093          * @event exception
29094          * Fire when xhr load exception
29095          * @param {Roo.bootstrap.DocumentManager} this
29096          * @param {XMLHttpRequest} xhr
29097          */
29098         "exception" : true,
29099         /**
29100          * @event afterupload
29101          * Fire when xhr load exception
29102          * @param {Roo.bootstrap.DocumentManager} this
29103          * @param {XMLHttpRequest} xhr
29104          */
29105         "afterupload" : true,
29106         /**
29107          * @event prepare
29108          * prepare the form data
29109          * @param {Roo.bootstrap.DocumentManager} this
29110          * @param {Object} formData
29111          */
29112         "prepare" : true,
29113         /**
29114          * @event remove
29115          * Fire when remove the file
29116          * @param {Roo.bootstrap.DocumentManager} this
29117          * @param {Object} file
29118          */
29119         "remove" : true,
29120         /**
29121          * @event refresh
29122          * Fire after refresh the file
29123          * @param {Roo.bootstrap.DocumentManager} this
29124          */
29125         "refresh" : true,
29126         /**
29127          * @event click
29128          * Fire after click the image
29129          * @param {Roo.bootstrap.DocumentManager} this
29130          * @param {Object} file
29131          */
29132         "click" : true,
29133         /**
29134          * @event edit
29135          * Fire when upload a image and editable set to true
29136          * @param {Roo.bootstrap.DocumentManager} this
29137          * @param {Object} file
29138          */
29139         "edit" : true,
29140         /**
29141          * @event beforeselectfile
29142          * Fire before select file
29143          * @param {Roo.bootstrap.DocumentManager} this
29144          */
29145         "beforeselectfile" : true,
29146         /**
29147          * @event process
29148          * Fire before process file
29149          * @param {Roo.bootstrap.DocumentManager} this
29150          * @param {Object} file
29151          */
29152         "process" : true,
29153         /**
29154          * @event previewrendered
29155          * Fire when preview rendered
29156          * @param {Roo.bootstrap.DocumentManager} this
29157          * @param {Object} file
29158          */
29159         "previewrendered" : true,
29160         /**
29161          */
29162         "previewResize" : true
29163         
29164     });
29165 };
29166
29167 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29168     
29169     boxes : 0,
29170     inputName : '',
29171     thumbSize : 300,
29172     multiple : true,
29173     files : false,
29174     method : 'POST',
29175     url : '',
29176     paramName : 'imageUpload',
29177     toolTipName : 'filename',
29178     fieldLabel : '',
29179     labelWidth : 4,
29180     labelAlign : 'left',
29181     editable : true,
29182     delegates : false,
29183     xhr : false, 
29184     
29185     labellg : 0,
29186     labelmd : 0,
29187     labelsm : 0,
29188     labelxs : 0,
29189     
29190     getAutoCreate : function()
29191     {   
29192         var managerWidget = {
29193             tag : 'div',
29194             cls : 'roo-document-manager',
29195             cn : [
29196                 {
29197                     tag : 'input',
29198                     cls : 'roo-document-manager-selector',
29199                     type : 'file'
29200                 },
29201                 {
29202                     tag : 'div',
29203                     cls : 'roo-document-manager-uploader',
29204                     cn : [
29205                         {
29206                             tag : 'div',
29207                             cls : 'roo-document-manager-upload-btn',
29208                             html : '<i class="fa fa-plus"></i>'
29209                         }
29210                     ]
29211                     
29212                 }
29213             ]
29214         };
29215         
29216         var content = [
29217             {
29218                 tag : 'div',
29219                 cls : 'column col-md-12',
29220                 cn : managerWidget
29221             }
29222         ];
29223         
29224         if(this.fieldLabel.length){
29225             
29226             content = [
29227                 {
29228                     tag : 'div',
29229                     cls : 'column col-md-12',
29230                     html : this.fieldLabel
29231                 },
29232                 {
29233                     tag : 'div',
29234                     cls : 'column col-md-12',
29235                     cn : managerWidget
29236                 }
29237             ];
29238
29239             if(this.labelAlign == 'left'){
29240                 content = [
29241                     {
29242                         tag : 'div',
29243                         cls : 'column',
29244                         html : this.fieldLabel
29245                     },
29246                     {
29247                         tag : 'div',
29248                         cls : 'column',
29249                         cn : managerWidget
29250                     }
29251                 ];
29252                 
29253                 if(this.labelWidth > 12){
29254                     content[0].style = "width: " + this.labelWidth + 'px';
29255                 }
29256
29257                 if(this.labelWidth < 13 && this.labelmd == 0){
29258                     this.labelmd = this.labelWidth;
29259                 }
29260
29261                 if(this.labellg > 0){
29262                     content[0].cls += ' col-lg-' + this.labellg;
29263                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29264                 }
29265
29266                 if(this.labelmd > 0){
29267                     content[0].cls += ' col-md-' + this.labelmd;
29268                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29269                 }
29270
29271                 if(this.labelsm > 0){
29272                     content[0].cls += ' col-sm-' + this.labelsm;
29273                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29274                 }
29275
29276                 if(this.labelxs > 0){
29277                     content[0].cls += ' col-xs-' + this.labelxs;
29278                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29279                 }
29280                 
29281             }
29282         }
29283         
29284         var cfg = {
29285             tag : 'div',
29286             cls : 'row clearfix',
29287             cn : content
29288         };
29289         
29290         return cfg;
29291         
29292     },
29293     
29294     initEvents : function()
29295     {
29296         this.managerEl = this.el.select('.roo-document-manager', true).first();
29297         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29298         
29299         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29300         this.selectorEl.hide();
29301         
29302         if(this.multiple){
29303             this.selectorEl.attr('multiple', 'multiple');
29304         }
29305         
29306         this.selectorEl.on('change', this.onFileSelected, this);
29307         
29308         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29309         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29310         
29311         this.uploader.on('click', this.onUploaderClick, this);
29312         
29313         this.renderProgressDialog();
29314         
29315         var _this = this;
29316         
29317         window.addEventListener("resize", function() { _this.refresh(); } );
29318         
29319         this.fireEvent('initial', this);
29320     },
29321     
29322     renderProgressDialog : function()
29323     {
29324         var _this = this;
29325         
29326         this.progressDialog = new Roo.bootstrap.Modal({
29327             cls : 'roo-document-manager-progress-dialog',
29328             allow_close : false,
29329             animate : false,
29330             title : '',
29331             buttons : [
29332                 {
29333                     name  :'cancel',
29334                     weight : 'danger',
29335                     html : 'Cancel'
29336                 }
29337             ], 
29338             listeners : { 
29339                 btnclick : function() {
29340                     _this.uploadCancel();
29341                     this.hide();
29342                 }
29343             }
29344         });
29345          
29346         this.progressDialog.render(Roo.get(document.body));
29347          
29348         this.progress = new Roo.bootstrap.Progress({
29349             cls : 'roo-document-manager-progress',
29350             active : true,
29351             striped : true
29352         });
29353         
29354         this.progress.render(this.progressDialog.getChildContainer());
29355         
29356         this.progressBar = new Roo.bootstrap.ProgressBar({
29357             cls : 'roo-document-manager-progress-bar',
29358             aria_valuenow : 0,
29359             aria_valuemin : 0,
29360             aria_valuemax : 12,
29361             panel : 'success'
29362         });
29363         
29364         this.progressBar.render(this.progress.getChildContainer());
29365     },
29366     
29367     onUploaderClick : function(e)
29368     {
29369         e.preventDefault();
29370      
29371         if(this.fireEvent('beforeselectfile', this) != false){
29372             this.selectorEl.dom.click();
29373         }
29374         
29375     },
29376     
29377     onFileSelected : function(e)
29378     {
29379         e.preventDefault();
29380         
29381         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29382             return;
29383         }
29384         
29385         Roo.each(this.selectorEl.dom.files, function(file){
29386             if(this.fireEvent('inspect', this, file) != false){
29387                 this.files.push(file);
29388             }
29389         }, this);
29390         
29391         this.queue();
29392         
29393     },
29394     
29395     queue : function()
29396     {
29397         this.selectorEl.dom.value = '';
29398         
29399         if(!this.files || !this.files.length){
29400             return;
29401         }
29402         
29403         if(this.boxes > 0 && this.files.length > this.boxes){
29404             this.files = this.files.slice(0, this.boxes);
29405         }
29406         
29407         this.uploader.show();
29408         
29409         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29410             this.uploader.hide();
29411         }
29412         
29413         var _this = this;
29414         
29415         var files = [];
29416         
29417         var docs = [];
29418         
29419         Roo.each(this.files, function(file){
29420             
29421             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29422                 var f = this.renderPreview(file);
29423                 files.push(f);
29424                 return;
29425             }
29426             
29427             if(file.type.indexOf('image') != -1){
29428                 this.delegates.push(
29429                     (function(){
29430                         _this.process(file);
29431                     }).createDelegate(this)
29432                 );
29433         
29434                 return;
29435             }
29436             
29437             docs.push(
29438                 (function(){
29439                     _this.process(file);
29440                 }).createDelegate(this)
29441             );
29442             
29443         }, this);
29444         
29445         this.files = files;
29446         
29447         this.delegates = this.delegates.concat(docs);
29448         
29449         if(!this.delegates.length){
29450             this.refresh();
29451             return;
29452         }
29453         
29454         this.progressBar.aria_valuemax = this.delegates.length;
29455         
29456         this.arrange();
29457         
29458         return;
29459     },
29460     
29461     arrange : function()
29462     {
29463         if(!this.delegates.length){
29464             this.progressDialog.hide();
29465             this.refresh();
29466             return;
29467         }
29468         
29469         var delegate = this.delegates.shift();
29470         
29471         this.progressDialog.show();
29472         
29473         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29474         
29475         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29476         
29477         delegate();
29478     },
29479     
29480     refresh : function()
29481     {
29482         this.uploader.show();
29483         
29484         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29485             this.uploader.hide();
29486         }
29487         
29488         Roo.isTouch ? this.closable(false) : this.closable(true);
29489         
29490         this.fireEvent('refresh', this);
29491     },
29492     
29493     onRemove : function(e, el, o)
29494     {
29495         e.preventDefault();
29496         
29497         this.fireEvent('remove', this, o);
29498         
29499     },
29500     
29501     remove : function(o)
29502     {
29503         var files = [];
29504         
29505         Roo.each(this.files, function(file){
29506             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29507                 files.push(file);
29508                 return;
29509             }
29510
29511             o.target.remove();
29512
29513         }, this);
29514         
29515         this.files = files;
29516         
29517         this.refresh();
29518     },
29519     
29520     clear : function()
29521     {
29522         Roo.each(this.files, function(file){
29523             if(!file.target){
29524                 return;
29525             }
29526             
29527             file.target.remove();
29528
29529         }, this);
29530         
29531         this.files = [];
29532         
29533         this.refresh();
29534     },
29535     
29536     onClick : function(e, el, o)
29537     {
29538         e.preventDefault();
29539         
29540         this.fireEvent('click', this, o);
29541         
29542     },
29543     
29544     closable : function(closable)
29545     {
29546         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29547             
29548             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29549             
29550             if(closable){
29551                 el.show();
29552                 return;
29553             }
29554             
29555             el.hide();
29556             
29557         }, this);
29558     },
29559     
29560     xhrOnLoad : function(xhr)
29561     {
29562         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29563             el.remove();
29564         }, this);
29565         
29566         if (xhr.readyState !== 4) {
29567             this.arrange();
29568             this.fireEvent('exception', this, xhr);
29569             return;
29570         }
29571
29572         var response = Roo.decode(xhr.responseText);
29573         
29574         if(!response.success){
29575             this.arrange();
29576             this.fireEvent('exception', this, xhr);
29577             return;
29578         }
29579         
29580         var file = this.renderPreview(response.data);
29581         
29582         this.files.push(file);
29583         
29584         this.arrange();
29585         
29586         this.fireEvent('afterupload', this, xhr);
29587         
29588     },
29589     
29590     xhrOnError : function(xhr)
29591     {
29592         Roo.log('xhr on error');
29593         
29594         var response = Roo.decode(xhr.responseText);
29595           
29596         Roo.log(response);
29597         
29598         this.arrange();
29599     },
29600     
29601     process : function(file)
29602     {
29603         if(this.fireEvent('process', this, file) !== false){
29604             if(this.editable && file.type.indexOf('image') != -1){
29605                 this.fireEvent('edit', this, file);
29606                 return;
29607             }
29608
29609             this.uploadStart(file, false);
29610
29611             return;
29612         }
29613         
29614     },
29615     
29616     uploadStart : function(file, crop)
29617     {
29618         this.xhr = new XMLHttpRequest();
29619         
29620         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29621             this.arrange();
29622             return;
29623         }
29624         
29625         file.xhr = this.xhr;
29626             
29627         this.managerEl.createChild({
29628             tag : 'div',
29629             cls : 'roo-document-manager-loading',
29630             cn : [
29631                 {
29632                     tag : 'div',
29633                     tooltip : file.name,
29634                     cls : 'roo-document-manager-thumb',
29635                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29636                 }
29637             ]
29638
29639         });
29640
29641         this.xhr.open(this.method, this.url, true);
29642         
29643         var headers = {
29644             "Accept": "application/json",
29645             "Cache-Control": "no-cache",
29646             "X-Requested-With": "XMLHttpRequest"
29647         };
29648         
29649         for (var headerName in headers) {
29650             var headerValue = headers[headerName];
29651             if (headerValue) {
29652                 this.xhr.setRequestHeader(headerName, headerValue);
29653             }
29654         }
29655         
29656         var _this = this;
29657         
29658         this.xhr.onload = function()
29659         {
29660             _this.xhrOnLoad(_this.xhr);
29661         }
29662         
29663         this.xhr.onerror = function()
29664         {
29665             _this.xhrOnError(_this.xhr);
29666         }
29667         
29668         var formData = new FormData();
29669
29670         formData.append('returnHTML', 'NO');
29671         
29672         if(crop){
29673             formData.append('crop', crop);
29674         }
29675         
29676         formData.append(this.paramName, file, file.name);
29677         
29678         var options = {
29679             file : file, 
29680             manually : false
29681         };
29682         
29683         if(this.fireEvent('prepare', this, formData, options) != false){
29684             
29685             if(options.manually){
29686                 return;
29687             }
29688             
29689             this.xhr.send(formData);
29690             return;
29691         };
29692         
29693         this.uploadCancel();
29694     },
29695     
29696     uploadCancel : function()
29697     {
29698         if (this.xhr) {
29699             this.xhr.abort();
29700         }
29701         
29702         this.delegates = [];
29703         
29704         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29705             el.remove();
29706         }, this);
29707         
29708         this.arrange();
29709     },
29710     
29711     renderPreview : function(file)
29712     {
29713         if(typeof(file.target) != 'undefined' && file.target){
29714             return file;
29715         }
29716         
29717         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29718         
29719         var previewEl = this.managerEl.createChild({
29720             tag : 'div',
29721             cls : 'roo-document-manager-preview',
29722             cn : [
29723                 {
29724                     tag : 'div',
29725                     tooltip : file[this.toolTipName],
29726                     cls : 'roo-document-manager-thumb',
29727                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29728                 },
29729                 {
29730                     tag : 'button',
29731                     cls : 'close',
29732                     html : '<i class="fa fa-times-circle"></i>'
29733                 }
29734             ]
29735         });
29736
29737         var close = previewEl.select('button.close', true).first();
29738
29739         close.on('click', this.onRemove, this, file);
29740
29741         file.target = previewEl;
29742
29743         var image = previewEl.select('img', true).first();
29744         
29745         var _this = this;
29746         
29747         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29748         
29749         image.on('click', this.onClick, this, file);
29750         
29751         this.fireEvent('previewrendered', this, file);
29752         
29753         return file;
29754         
29755     },
29756     
29757     onPreviewLoad : function(file, image)
29758     {
29759         if(typeof(file.target) == 'undefined' || !file.target){
29760             return;
29761         }
29762         
29763         var width = image.dom.naturalWidth || image.dom.width;
29764         var height = image.dom.naturalHeight || image.dom.height;
29765         
29766         if(!this.previewResize) {
29767             return;
29768         }
29769         
29770         if(width > height){
29771             file.target.addClass('wide');
29772             return;
29773         }
29774         
29775         file.target.addClass('tall');
29776         return;
29777         
29778     },
29779     
29780     uploadFromSource : function(file, crop)
29781     {
29782         this.xhr = new XMLHttpRequest();
29783         
29784         this.managerEl.createChild({
29785             tag : 'div',
29786             cls : 'roo-document-manager-loading',
29787             cn : [
29788                 {
29789                     tag : 'div',
29790                     tooltip : file.name,
29791                     cls : 'roo-document-manager-thumb',
29792                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29793                 }
29794             ]
29795
29796         });
29797
29798         this.xhr.open(this.method, this.url, true);
29799         
29800         var headers = {
29801             "Accept": "application/json",
29802             "Cache-Control": "no-cache",
29803             "X-Requested-With": "XMLHttpRequest"
29804         };
29805         
29806         for (var headerName in headers) {
29807             var headerValue = headers[headerName];
29808             if (headerValue) {
29809                 this.xhr.setRequestHeader(headerName, headerValue);
29810             }
29811         }
29812         
29813         var _this = this;
29814         
29815         this.xhr.onload = function()
29816         {
29817             _this.xhrOnLoad(_this.xhr);
29818         }
29819         
29820         this.xhr.onerror = function()
29821         {
29822             _this.xhrOnError(_this.xhr);
29823         }
29824         
29825         var formData = new FormData();
29826
29827         formData.append('returnHTML', 'NO');
29828         
29829         formData.append('crop', crop);
29830         
29831         if(typeof(file.filename) != 'undefined'){
29832             formData.append('filename', file.filename);
29833         }
29834         
29835         if(typeof(file.mimetype) != 'undefined'){
29836             formData.append('mimetype', file.mimetype);
29837         }
29838         
29839         Roo.log(formData);
29840         
29841         if(this.fireEvent('prepare', this, formData) != false){
29842             this.xhr.send(formData);
29843         };
29844     }
29845 });
29846
29847 /*
29848 * Licence: LGPL
29849 */
29850
29851 /**
29852  * @class Roo.bootstrap.DocumentViewer
29853  * @extends Roo.bootstrap.Component
29854  * Bootstrap DocumentViewer class
29855  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29856  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29857  * 
29858  * @constructor
29859  * Create a new DocumentViewer
29860  * @param {Object} config The config object
29861  */
29862
29863 Roo.bootstrap.DocumentViewer = function(config){
29864     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29865     
29866     this.addEvents({
29867         /**
29868          * @event initial
29869          * Fire after initEvent
29870          * @param {Roo.bootstrap.DocumentViewer} this
29871          */
29872         "initial" : true,
29873         /**
29874          * @event click
29875          * Fire after click
29876          * @param {Roo.bootstrap.DocumentViewer} this
29877          */
29878         "click" : true,
29879         /**
29880          * @event download
29881          * Fire after download button
29882          * @param {Roo.bootstrap.DocumentViewer} this
29883          */
29884         "download" : true,
29885         /**
29886          * @event trash
29887          * Fire after trash button
29888          * @param {Roo.bootstrap.DocumentViewer} this
29889          */
29890         "trash" : true
29891         
29892     });
29893 };
29894
29895 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29896     
29897     showDownload : true,
29898     
29899     showTrash : true,
29900     
29901     getAutoCreate : function()
29902     {
29903         var cfg = {
29904             tag : 'div',
29905             cls : 'roo-document-viewer',
29906             cn : [
29907                 {
29908                     tag : 'div',
29909                     cls : 'roo-document-viewer-body',
29910                     cn : [
29911                         {
29912                             tag : 'div',
29913                             cls : 'roo-document-viewer-thumb',
29914                             cn : [
29915                                 {
29916                                     tag : 'img',
29917                                     cls : 'roo-document-viewer-image'
29918                                 }
29919                             ]
29920                         }
29921                     ]
29922                 },
29923                 {
29924                     tag : 'div',
29925                     cls : 'roo-document-viewer-footer',
29926                     cn : {
29927                         tag : 'div',
29928                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29929                         cn : [
29930                             {
29931                                 tag : 'div',
29932                                 cls : 'btn-group roo-document-viewer-download',
29933                                 cn : [
29934                                     {
29935                                         tag : 'button',
29936                                         cls : 'btn btn-default',
29937                                         html : '<i class="fa fa-download"></i>'
29938                                     }
29939                                 ]
29940                             },
29941                             {
29942                                 tag : 'div',
29943                                 cls : 'btn-group roo-document-viewer-trash',
29944                                 cn : [
29945                                     {
29946                                         tag : 'button',
29947                                         cls : 'btn btn-default',
29948                                         html : '<i class="fa fa-trash"></i>'
29949                                     }
29950                                 ]
29951                             }
29952                         ]
29953                     }
29954                 }
29955             ]
29956         };
29957         
29958         return cfg;
29959     },
29960     
29961     initEvents : function()
29962     {
29963         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29964         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29965         
29966         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29967         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29968         
29969         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29970         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29971         
29972         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29973         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29974         
29975         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29976         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29977         
29978         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29979         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29980         
29981         this.bodyEl.on('click', this.onClick, this);
29982         this.downloadBtn.on('click', this.onDownload, this);
29983         this.trashBtn.on('click', this.onTrash, this);
29984         
29985         this.downloadBtn.hide();
29986         this.trashBtn.hide();
29987         
29988         if(this.showDownload){
29989             this.downloadBtn.show();
29990         }
29991         
29992         if(this.showTrash){
29993             this.trashBtn.show();
29994         }
29995         
29996         if(!this.showDownload && !this.showTrash) {
29997             this.footerEl.hide();
29998         }
29999         
30000     },
30001     
30002     initial : function()
30003     {
30004         this.fireEvent('initial', this);
30005         
30006     },
30007     
30008     onClick : function(e)
30009     {
30010         e.preventDefault();
30011         
30012         this.fireEvent('click', this);
30013     },
30014     
30015     onDownload : function(e)
30016     {
30017         e.preventDefault();
30018         
30019         this.fireEvent('download', this);
30020     },
30021     
30022     onTrash : function(e)
30023     {
30024         e.preventDefault();
30025         
30026         this.fireEvent('trash', this);
30027     }
30028     
30029 });
30030 /*
30031  * - LGPL
30032  *
30033  * nav progress bar
30034  * 
30035  */
30036
30037 /**
30038  * @class Roo.bootstrap.NavProgressBar
30039  * @extends Roo.bootstrap.Component
30040  * Bootstrap NavProgressBar class
30041  * 
30042  * @constructor
30043  * Create a new nav progress bar
30044  * @param {Object} config The config object
30045  */
30046
30047 Roo.bootstrap.NavProgressBar = function(config){
30048     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30049
30050     this.bullets = this.bullets || [];
30051    
30052 //    Roo.bootstrap.NavProgressBar.register(this);
30053      this.addEvents({
30054         /**
30055              * @event changed
30056              * Fires when the active item changes
30057              * @param {Roo.bootstrap.NavProgressBar} this
30058              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30059              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30060          */
30061         'changed': true
30062      });
30063     
30064 };
30065
30066 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30067     
30068     bullets : [],
30069     barItems : [],
30070     
30071     getAutoCreate : function()
30072     {
30073         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30074         
30075         cfg = {
30076             tag : 'div',
30077             cls : 'roo-navigation-bar-group',
30078             cn : [
30079                 {
30080                     tag : 'div',
30081                     cls : 'roo-navigation-top-bar'
30082                 },
30083                 {
30084                     tag : 'div',
30085                     cls : 'roo-navigation-bullets-bar',
30086                     cn : [
30087                         {
30088                             tag : 'ul',
30089                             cls : 'roo-navigation-bar'
30090                         }
30091                     ]
30092                 },
30093                 
30094                 {
30095                     tag : 'div',
30096                     cls : 'roo-navigation-bottom-bar'
30097                 }
30098             ]
30099             
30100         };
30101         
30102         return cfg;
30103         
30104     },
30105     
30106     initEvents: function() 
30107     {
30108         
30109     },
30110     
30111     onRender : function(ct, position) 
30112     {
30113         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30114         
30115         if(this.bullets.length){
30116             Roo.each(this.bullets, function(b){
30117                this.addItem(b);
30118             }, this);
30119         }
30120         
30121         this.format();
30122         
30123     },
30124     
30125     addItem : function(cfg)
30126     {
30127         var item = new Roo.bootstrap.NavProgressItem(cfg);
30128         
30129         item.parentId = this.id;
30130         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30131         
30132         if(cfg.html){
30133             var top = new Roo.bootstrap.Element({
30134                 tag : 'div',
30135                 cls : 'roo-navigation-bar-text'
30136             });
30137             
30138             var bottom = new Roo.bootstrap.Element({
30139                 tag : 'div',
30140                 cls : 'roo-navigation-bar-text'
30141             });
30142             
30143             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30144             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30145             
30146             var topText = new Roo.bootstrap.Element({
30147                 tag : 'span',
30148                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30149             });
30150             
30151             var bottomText = new Roo.bootstrap.Element({
30152                 tag : 'span',
30153                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30154             });
30155             
30156             topText.onRender(top.el, null);
30157             bottomText.onRender(bottom.el, null);
30158             
30159             item.topEl = top;
30160             item.bottomEl = bottom;
30161         }
30162         
30163         this.barItems.push(item);
30164         
30165         return item;
30166     },
30167     
30168     getActive : function()
30169     {
30170         var active = false;
30171         
30172         Roo.each(this.barItems, function(v){
30173             
30174             if (!v.isActive()) {
30175                 return;
30176             }
30177             
30178             active = v;
30179             return false;
30180             
30181         });
30182         
30183         return active;
30184     },
30185     
30186     setActiveItem : function(item)
30187     {
30188         var prev = false;
30189         
30190         Roo.each(this.barItems, function(v){
30191             if (v.rid == item.rid) {
30192                 return ;
30193             }
30194             
30195             if (v.isActive()) {
30196                 v.setActive(false);
30197                 prev = v;
30198             }
30199         });
30200
30201         item.setActive(true);
30202         
30203         this.fireEvent('changed', this, item, prev);
30204     },
30205     
30206     getBarItem: function(rid)
30207     {
30208         var ret = false;
30209         
30210         Roo.each(this.barItems, function(e) {
30211             if (e.rid != rid) {
30212                 return;
30213             }
30214             
30215             ret =  e;
30216             return false;
30217         });
30218         
30219         return ret;
30220     },
30221     
30222     indexOfItem : function(item)
30223     {
30224         var index = false;
30225         
30226         Roo.each(this.barItems, function(v, i){
30227             
30228             if (v.rid != item.rid) {
30229                 return;
30230             }
30231             
30232             index = i;
30233             return false
30234         });
30235         
30236         return index;
30237     },
30238     
30239     setActiveNext : function()
30240     {
30241         var i = this.indexOfItem(this.getActive());
30242         
30243         if (i > this.barItems.length) {
30244             return;
30245         }
30246         
30247         this.setActiveItem(this.barItems[i+1]);
30248     },
30249     
30250     setActivePrev : function()
30251     {
30252         var i = this.indexOfItem(this.getActive());
30253         
30254         if (i  < 1) {
30255             return;
30256         }
30257         
30258         this.setActiveItem(this.barItems[i-1]);
30259     },
30260     
30261     format : function()
30262     {
30263         if(!this.barItems.length){
30264             return;
30265         }
30266      
30267         var width = 100 / this.barItems.length;
30268         
30269         Roo.each(this.barItems, function(i){
30270             i.el.setStyle('width', width + '%');
30271             i.topEl.el.setStyle('width', width + '%');
30272             i.bottomEl.el.setStyle('width', width + '%');
30273         }, this);
30274         
30275     }
30276     
30277 });
30278 /*
30279  * - LGPL
30280  *
30281  * Nav Progress Item
30282  * 
30283  */
30284
30285 /**
30286  * @class Roo.bootstrap.NavProgressItem
30287  * @extends Roo.bootstrap.Component
30288  * Bootstrap NavProgressItem class
30289  * @cfg {String} rid the reference id
30290  * @cfg {Boolean} active (true|false) Is item active default false
30291  * @cfg {Boolean} disabled (true|false) Is item active default false
30292  * @cfg {String} html
30293  * @cfg {String} position (top|bottom) text position default bottom
30294  * @cfg {String} icon show icon instead of number
30295  * 
30296  * @constructor
30297  * Create a new NavProgressItem
30298  * @param {Object} config The config object
30299  */
30300 Roo.bootstrap.NavProgressItem = function(config){
30301     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30302     this.addEvents({
30303         // raw events
30304         /**
30305          * @event click
30306          * The raw click event for the entire grid.
30307          * @param {Roo.bootstrap.NavProgressItem} this
30308          * @param {Roo.EventObject} e
30309          */
30310         "click" : true
30311     });
30312    
30313 };
30314
30315 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30316     
30317     rid : '',
30318     active : false,
30319     disabled : false,
30320     html : '',
30321     position : 'bottom',
30322     icon : false,
30323     
30324     getAutoCreate : function()
30325     {
30326         var iconCls = 'roo-navigation-bar-item-icon';
30327         
30328         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30329         
30330         var cfg = {
30331             tag: 'li',
30332             cls: 'roo-navigation-bar-item',
30333             cn : [
30334                 {
30335                     tag : 'i',
30336                     cls : iconCls
30337                 }
30338             ]
30339         };
30340         
30341         if(this.active){
30342             cfg.cls += ' active';
30343         }
30344         if(this.disabled){
30345             cfg.cls += ' disabled';
30346         }
30347         
30348         return cfg;
30349     },
30350     
30351     disable : function()
30352     {
30353         this.setDisabled(true);
30354     },
30355     
30356     enable : function()
30357     {
30358         this.setDisabled(false);
30359     },
30360     
30361     initEvents: function() 
30362     {
30363         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30364         
30365         this.iconEl.on('click', this.onClick, this);
30366     },
30367     
30368     onClick : function(e)
30369     {
30370         e.preventDefault();
30371         
30372         if(this.disabled){
30373             return;
30374         }
30375         
30376         if(this.fireEvent('click', this, e) === false){
30377             return;
30378         };
30379         
30380         this.parent().setActiveItem(this);
30381     },
30382     
30383     isActive: function () 
30384     {
30385         return this.active;
30386     },
30387     
30388     setActive : function(state)
30389     {
30390         if(this.active == state){
30391             return;
30392         }
30393         
30394         this.active = state;
30395         
30396         if (state) {
30397             this.el.addClass('active');
30398             return;
30399         }
30400         
30401         this.el.removeClass('active');
30402         
30403         return;
30404     },
30405     
30406     setDisabled : function(state)
30407     {
30408         if(this.disabled == state){
30409             return;
30410         }
30411         
30412         this.disabled = state;
30413         
30414         if (state) {
30415             this.el.addClass('disabled');
30416             return;
30417         }
30418         
30419         this.el.removeClass('disabled');
30420     },
30421     
30422     tooltipEl : function()
30423     {
30424         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30425     }
30426 });
30427  
30428
30429  /*
30430  * - LGPL
30431  *
30432  * FieldLabel
30433  * 
30434  */
30435
30436 /**
30437  * @class Roo.bootstrap.FieldLabel
30438  * @extends Roo.bootstrap.Component
30439  * Bootstrap FieldLabel class
30440  * @cfg {String} html contents of the element
30441  * @cfg {String} tag tag of the element default label
30442  * @cfg {String} cls class of the element
30443  * @cfg {String} target label target 
30444  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30445  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30446  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30447  * @cfg {String} iconTooltip default "This field is required"
30448  * @cfg {String} indicatorpos (left|right) default left
30449  * 
30450  * @constructor
30451  * Create a new FieldLabel
30452  * @param {Object} config The config object
30453  */
30454
30455 Roo.bootstrap.FieldLabel = function(config){
30456     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30457     
30458     this.addEvents({
30459             /**
30460              * @event invalid
30461              * Fires after the field has been marked as invalid.
30462              * @param {Roo.form.FieldLabel} this
30463              * @param {String} msg The validation message
30464              */
30465             invalid : true,
30466             /**
30467              * @event valid
30468              * Fires after the field has been validated with no errors.
30469              * @param {Roo.form.FieldLabel} this
30470              */
30471             valid : true
30472         });
30473 };
30474
30475 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30476     
30477     tag: 'label',
30478     cls: '',
30479     html: '',
30480     target: '',
30481     allowBlank : true,
30482     invalidClass : 'has-warning',
30483     validClass : 'has-success',
30484     iconTooltip : 'This field is required',
30485     indicatorpos : 'left',
30486     
30487     getAutoCreate : function(){
30488         
30489         var cls = "";
30490         if (!this.allowBlank) {
30491             cls  = "visible";
30492         }
30493         
30494         var cfg = {
30495             tag : this.tag,
30496             cls : 'roo-bootstrap-field-label ' + this.cls,
30497             for : this.target,
30498             cn : [
30499                 {
30500                     tag : 'i',
30501                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30502                     tooltip : this.iconTooltip
30503                 },
30504                 {
30505                     tag : 'span',
30506                     html : this.html
30507                 }
30508             ] 
30509         };
30510         
30511         if(this.indicatorpos == 'right'){
30512             var cfg = {
30513                 tag : this.tag,
30514                 cls : 'roo-bootstrap-field-label ' + this.cls,
30515                 for : this.target,
30516                 cn : [
30517                     {
30518                         tag : 'span',
30519                         html : this.html
30520                     },
30521                     {
30522                         tag : 'i',
30523                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30524                         tooltip : this.iconTooltip
30525                     }
30526                 ] 
30527             };
30528         }
30529         
30530         return cfg;
30531     },
30532     
30533     initEvents: function() 
30534     {
30535         Roo.bootstrap.Element.superclass.initEvents.call(this);
30536         
30537         this.indicator = this.indicatorEl();
30538         
30539         if(this.indicator){
30540             this.indicator.removeClass('visible');
30541             this.indicator.addClass('invisible');
30542         }
30543         
30544         Roo.bootstrap.FieldLabel.register(this);
30545     },
30546     
30547     indicatorEl : function()
30548     {
30549         var indicator = this.el.select('i.roo-required-indicator',true).first();
30550         
30551         if(!indicator){
30552             return false;
30553         }
30554         
30555         return indicator;
30556         
30557     },
30558     
30559     /**
30560      * Mark this field as valid
30561      */
30562     markValid : function()
30563     {
30564         if(this.indicator){
30565             this.indicator.removeClass('visible');
30566             this.indicator.addClass('invisible');
30567         }
30568         if (Roo.bootstrap.version == 3) {
30569             this.el.removeClass(this.invalidClass);
30570             this.el.addClass(this.validClass);
30571         } else {
30572             this.el.removeClass('is-invalid');
30573             this.el.addClass('is-valid');
30574         }
30575         
30576         
30577         this.fireEvent('valid', this);
30578     },
30579     
30580     /**
30581      * Mark this field as invalid
30582      * @param {String} msg The validation message
30583      */
30584     markInvalid : function(msg)
30585     {
30586         if(this.indicator){
30587             this.indicator.removeClass('invisible');
30588             this.indicator.addClass('visible');
30589         }
30590           if (Roo.bootstrap.version == 3) {
30591             this.el.removeClass(this.validClass);
30592             this.el.addClass(this.invalidClass);
30593         } else {
30594             this.el.removeClass('is-valid');
30595             this.el.addClass('is-invalid');
30596         }
30597         
30598         
30599         this.fireEvent('invalid', this, msg);
30600     }
30601     
30602    
30603 });
30604
30605 Roo.apply(Roo.bootstrap.FieldLabel, {
30606     
30607     groups: {},
30608     
30609      /**
30610     * register a FieldLabel Group
30611     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30612     */
30613     register : function(label)
30614     {
30615         if(this.groups.hasOwnProperty(label.target)){
30616             return;
30617         }
30618      
30619         this.groups[label.target] = label;
30620         
30621     },
30622     /**
30623     * fetch a FieldLabel Group based on the target
30624     * @param {string} target
30625     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30626     */
30627     get: function(target) {
30628         if (typeof(this.groups[target]) == 'undefined') {
30629             return false;
30630         }
30631         
30632         return this.groups[target] ;
30633     }
30634 });
30635
30636  
30637
30638  /*
30639  * - LGPL
30640  *
30641  * page DateSplitField.
30642  * 
30643  */
30644
30645
30646 /**
30647  * @class Roo.bootstrap.DateSplitField
30648  * @extends Roo.bootstrap.Component
30649  * Bootstrap DateSplitField class
30650  * @cfg {string} fieldLabel - the label associated
30651  * @cfg {Number} labelWidth set the width of label (0-12)
30652  * @cfg {String} labelAlign (top|left)
30653  * @cfg {Boolean} dayAllowBlank (true|false) default false
30654  * @cfg {Boolean} monthAllowBlank (true|false) default false
30655  * @cfg {Boolean} yearAllowBlank (true|false) default false
30656  * @cfg {string} dayPlaceholder 
30657  * @cfg {string} monthPlaceholder
30658  * @cfg {string} yearPlaceholder
30659  * @cfg {string} dayFormat default 'd'
30660  * @cfg {string} monthFormat default 'm'
30661  * @cfg {string} yearFormat default 'Y'
30662  * @cfg {Number} labellg set the width of label (1-12)
30663  * @cfg {Number} labelmd set the width of label (1-12)
30664  * @cfg {Number} labelsm set the width of label (1-12)
30665  * @cfg {Number} labelxs set the width of label (1-12)
30666
30667  *     
30668  * @constructor
30669  * Create a new DateSplitField
30670  * @param {Object} config The config object
30671  */
30672
30673 Roo.bootstrap.DateSplitField = function(config){
30674     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30675     
30676     this.addEvents({
30677         // raw events
30678          /**
30679          * @event years
30680          * getting the data of years
30681          * @param {Roo.bootstrap.DateSplitField} this
30682          * @param {Object} years
30683          */
30684         "years" : true,
30685         /**
30686          * @event days
30687          * getting the data of days
30688          * @param {Roo.bootstrap.DateSplitField} this
30689          * @param {Object} days
30690          */
30691         "days" : true,
30692         /**
30693          * @event invalid
30694          * Fires after the field has been marked as invalid.
30695          * @param {Roo.form.Field} this
30696          * @param {String} msg The validation message
30697          */
30698         invalid : true,
30699        /**
30700          * @event valid
30701          * Fires after the field has been validated with no errors.
30702          * @param {Roo.form.Field} this
30703          */
30704         valid : true
30705     });
30706 };
30707
30708 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30709     
30710     fieldLabel : '',
30711     labelAlign : 'top',
30712     labelWidth : 3,
30713     dayAllowBlank : false,
30714     monthAllowBlank : false,
30715     yearAllowBlank : false,
30716     dayPlaceholder : '',
30717     monthPlaceholder : '',
30718     yearPlaceholder : '',
30719     dayFormat : 'd',
30720     monthFormat : 'm',
30721     yearFormat : 'Y',
30722     isFormField : true,
30723     labellg : 0,
30724     labelmd : 0,
30725     labelsm : 0,
30726     labelxs : 0,
30727     
30728     getAutoCreate : function()
30729     {
30730         var cfg = {
30731             tag : 'div',
30732             cls : 'row roo-date-split-field-group',
30733             cn : [
30734                 {
30735                     tag : 'input',
30736                     type : 'hidden',
30737                     cls : 'form-hidden-field roo-date-split-field-group-value',
30738                     name : this.name
30739                 }
30740             ]
30741         };
30742         
30743         var labelCls = 'col-md-12';
30744         var contentCls = 'col-md-4';
30745         
30746         if(this.fieldLabel){
30747             
30748             var label = {
30749                 tag : 'div',
30750                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30751                 cn : [
30752                     {
30753                         tag : 'label',
30754                         html : this.fieldLabel
30755                     }
30756                 ]
30757             };
30758             
30759             if(this.labelAlign == 'left'){
30760             
30761                 if(this.labelWidth > 12){
30762                     label.style = "width: " + this.labelWidth + 'px';
30763                 }
30764
30765                 if(this.labelWidth < 13 && this.labelmd == 0){
30766                     this.labelmd = this.labelWidth;
30767                 }
30768
30769                 if(this.labellg > 0){
30770                     labelCls = ' col-lg-' + this.labellg;
30771                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30772                 }
30773
30774                 if(this.labelmd > 0){
30775                     labelCls = ' col-md-' + this.labelmd;
30776                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30777                 }
30778
30779                 if(this.labelsm > 0){
30780                     labelCls = ' col-sm-' + this.labelsm;
30781                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30782                 }
30783
30784                 if(this.labelxs > 0){
30785                     labelCls = ' col-xs-' + this.labelxs;
30786                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30787                 }
30788             }
30789             
30790             label.cls += ' ' + labelCls;
30791             
30792             cfg.cn.push(label);
30793         }
30794         
30795         Roo.each(['day', 'month', 'year'], function(t){
30796             cfg.cn.push({
30797                 tag : 'div',
30798                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30799             });
30800         }, this);
30801         
30802         return cfg;
30803     },
30804     
30805     inputEl: function ()
30806     {
30807         return this.el.select('.roo-date-split-field-group-value', true).first();
30808     },
30809     
30810     onRender : function(ct, position) 
30811     {
30812         var _this = this;
30813         
30814         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30815         
30816         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30817         
30818         this.dayField = new Roo.bootstrap.ComboBox({
30819             allowBlank : this.dayAllowBlank,
30820             alwaysQuery : true,
30821             displayField : 'value',
30822             editable : false,
30823             fieldLabel : '',
30824             forceSelection : true,
30825             mode : 'local',
30826             placeholder : this.dayPlaceholder,
30827             selectOnFocus : true,
30828             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30829             triggerAction : 'all',
30830             typeAhead : true,
30831             valueField : 'value',
30832             store : new Roo.data.SimpleStore({
30833                 data : (function() {    
30834                     var days = [];
30835                     _this.fireEvent('days', _this, days);
30836                     return days;
30837                 })(),
30838                 fields : [ 'value' ]
30839             }),
30840             listeners : {
30841                 select : function (_self, record, index)
30842                 {
30843                     _this.setValue(_this.getValue());
30844                 }
30845             }
30846         });
30847
30848         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30849         
30850         this.monthField = new Roo.bootstrap.MonthField({
30851             after : '<i class=\"fa fa-calendar\"></i>',
30852             allowBlank : this.monthAllowBlank,
30853             placeholder : this.monthPlaceholder,
30854             readOnly : true,
30855             listeners : {
30856                 render : function (_self)
30857                 {
30858                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30859                         e.preventDefault();
30860                         _self.focus();
30861                     });
30862                 },
30863                 select : function (_self, oldvalue, newvalue)
30864                 {
30865                     _this.setValue(_this.getValue());
30866                 }
30867             }
30868         });
30869         
30870         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30871         
30872         this.yearField = new Roo.bootstrap.ComboBox({
30873             allowBlank : this.yearAllowBlank,
30874             alwaysQuery : true,
30875             displayField : 'value',
30876             editable : false,
30877             fieldLabel : '',
30878             forceSelection : true,
30879             mode : 'local',
30880             placeholder : this.yearPlaceholder,
30881             selectOnFocus : true,
30882             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30883             triggerAction : 'all',
30884             typeAhead : true,
30885             valueField : 'value',
30886             store : new Roo.data.SimpleStore({
30887                 data : (function() {
30888                     var years = [];
30889                     _this.fireEvent('years', _this, years);
30890                     return years;
30891                 })(),
30892                 fields : [ 'value' ]
30893             }),
30894             listeners : {
30895                 select : function (_self, record, index)
30896                 {
30897                     _this.setValue(_this.getValue());
30898                 }
30899             }
30900         });
30901
30902         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30903     },
30904     
30905     setValue : function(v, format)
30906     {
30907         this.inputEl.dom.value = v;
30908         
30909         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30910         
30911         var d = Date.parseDate(v, f);
30912         
30913         if(!d){
30914             this.validate();
30915             return;
30916         }
30917         
30918         this.setDay(d.format(this.dayFormat));
30919         this.setMonth(d.format(this.monthFormat));
30920         this.setYear(d.format(this.yearFormat));
30921         
30922         this.validate();
30923         
30924         return;
30925     },
30926     
30927     setDay : function(v)
30928     {
30929         this.dayField.setValue(v);
30930         this.inputEl.dom.value = this.getValue();
30931         this.validate();
30932         return;
30933     },
30934     
30935     setMonth : function(v)
30936     {
30937         this.monthField.setValue(v, true);
30938         this.inputEl.dom.value = this.getValue();
30939         this.validate();
30940         return;
30941     },
30942     
30943     setYear : function(v)
30944     {
30945         this.yearField.setValue(v);
30946         this.inputEl.dom.value = this.getValue();
30947         this.validate();
30948         return;
30949     },
30950     
30951     getDay : function()
30952     {
30953         return this.dayField.getValue();
30954     },
30955     
30956     getMonth : function()
30957     {
30958         return this.monthField.getValue();
30959     },
30960     
30961     getYear : function()
30962     {
30963         return this.yearField.getValue();
30964     },
30965     
30966     getValue : function()
30967     {
30968         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30969         
30970         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30971         
30972         return date;
30973     },
30974     
30975     reset : function()
30976     {
30977         this.setDay('');
30978         this.setMonth('');
30979         this.setYear('');
30980         this.inputEl.dom.value = '';
30981         this.validate();
30982         return;
30983     },
30984     
30985     validate : function()
30986     {
30987         var d = this.dayField.validate();
30988         var m = this.monthField.validate();
30989         var y = this.yearField.validate();
30990         
30991         var valid = true;
30992         
30993         if(
30994                 (!this.dayAllowBlank && !d) ||
30995                 (!this.monthAllowBlank && !m) ||
30996                 (!this.yearAllowBlank && !y)
30997         ){
30998             valid = false;
30999         }
31000         
31001         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31002             return valid;
31003         }
31004         
31005         if(valid){
31006             this.markValid();
31007             return valid;
31008         }
31009         
31010         this.markInvalid();
31011         
31012         return valid;
31013     },
31014     
31015     markValid : function()
31016     {
31017         
31018         var label = this.el.select('label', true).first();
31019         var icon = this.el.select('i.fa-star', true).first();
31020
31021         if(label && icon){
31022             icon.remove();
31023         }
31024         
31025         this.fireEvent('valid', this);
31026     },
31027     
31028      /**
31029      * Mark this field as invalid
31030      * @param {String} msg The validation message
31031      */
31032     markInvalid : function(msg)
31033     {
31034         
31035         var label = this.el.select('label', true).first();
31036         var icon = this.el.select('i.fa-star', true).first();
31037
31038         if(label && !icon){
31039             this.el.select('.roo-date-split-field-label', true).createChild({
31040                 tag : 'i',
31041                 cls : 'text-danger fa fa-lg fa-star',
31042                 tooltip : 'This field is required',
31043                 style : 'margin-right:5px;'
31044             }, label, true);
31045         }
31046         
31047         this.fireEvent('invalid', this, msg);
31048     },
31049     
31050     clearInvalid : function()
31051     {
31052         var label = this.el.select('label', true).first();
31053         var icon = this.el.select('i.fa-star', true).first();
31054
31055         if(label && icon){
31056             icon.remove();
31057         }
31058         
31059         this.fireEvent('valid', this);
31060     },
31061     
31062     getName: function()
31063     {
31064         return this.name;
31065     }
31066     
31067 });
31068
31069  /**
31070  *
31071  * This is based on 
31072  * http://masonry.desandro.com
31073  *
31074  * The idea is to render all the bricks based on vertical width...
31075  *
31076  * The original code extends 'outlayer' - we might need to use that....
31077  * 
31078  */
31079
31080
31081 /**
31082  * @class Roo.bootstrap.LayoutMasonry
31083  * @extends Roo.bootstrap.Component
31084  * Bootstrap Layout Masonry class
31085  * 
31086  * @constructor
31087  * Create a new Element
31088  * @param {Object} config The config object
31089  */
31090
31091 Roo.bootstrap.LayoutMasonry = function(config){
31092     
31093     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31094     
31095     this.bricks = [];
31096     
31097     Roo.bootstrap.LayoutMasonry.register(this);
31098     
31099     this.addEvents({
31100         // raw events
31101         /**
31102          * @event layout
31103          * Fire after layout the items
31104          * @param {Roo.bootstrap.LayoutMasonry} this
31105          * @param {Roo.EventObject} e
31106          */
31107         "layout" : true
31108     });
31109     
31110 };
31111
31112 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31113     
31114     /**
31115      * @cfg {Boolean} isLayoutInstant = no animation?
31116      */   
31117     isLayoutInstant : false, // needed?
31118    
31119     /**
31120      * @cfg {Number} boxWidth  width of the columns
31121      */   
31122     boxWidth : 450,
31123     
31124       /**
31125      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31126      */   
31127     boxHeight : 0,
31128     
31129     /**
31130      * @cfg {Number} padWidth padding below box..
31131      */   
31132     padWidth : 10, 
31133     
31134     /**
31135      * @cfg {Number} gutter gutter width..
31136      */   
31137     gutter : 10,
31138     
31139      /**
31140      * @cfg {Number} maxCols maximum number of columns
31141      */   
31142     
31143     maxCols: 0,
31144     
31145     /**
31146      * @cfg {Boolean} isAutoInitial defalut true
31147      */   
31148     isAutoInitial : true, 
31149     
31150     containerWidth: 0,
31151     
31152     /**
31153      * @cfg {Boolean} isHorizontal defalut false
31154      */   
31155     isHorizontal : false, 
31156
31157     currentSize : null,
31158     
31159     tag: 'div',
31160     
31161     cls: '',
31162     
31163     bricks: null, //CompositeElement
31164     
31165     cols : 1,
31166     
31167     _isLayoutInited : false,
31168     
31169 //    isAlternative : false, // only use for vertical layout...
31170     
31171     /**
31172      * @cfg {Number} alternativePadWidth padding below box..
31173      */   
31174     alternativePadWidth : 50,
31175     
31176     selectedBrick : [],
31177     
31178     getAutoCreate : function(){
31179         
31180         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31181         
31182         var cfg = {
31183             tag: this.tag,
31184             cls: 'blog-masonary-wrapper ' + this.cls,
31185             cn : {
31186                 cls : 'mas-boxes masonary'
31187             }
31188         };
31189         
31190         return cfg;
31191     },
31192     
31193     getChildContainer: function( )
31194     {
31195         if (this.boxesEl) {
31196             return this.boxesEl;
31197         }
31198         
31199         this.boxesEl = this.el.select('.mas-boxes').first();
31200         
31201         return this.boxesEl;
31202     },
31203     
31204     
31205     initEvents : function()
31206     {
31207         var _this = this;
31208         
31209         if(this.isAutoInitial){
31210             Roo.log('hook children rendered');
31211             this.on('childrenrendered', function() {
31212                 Roo.log('children rendered');
31213                 _this.initial();
31214             } ,this);
31215         }
31216     },
31217     
31218     initial : function()
31219     {
31220         this.selectedBrick = [];
31221         
31222         this.currentSize = this.el.getBox(true);
31223         
31224         Roo.EventManager.onWindowResize(this.resize, this); 
31225
31226         if(!this.isAutoInitial){
31227             this.layout();
31228             return;
31229         }
31230         
31231         this.layout();
31232         
31233         return;
31234         //this.layout.defer(500,this);
31235         
31236     },
31237     
31238     resize : function()
31239     {
31240         var cs = this.el.getBox(true);
31241         
31242         if (
31243                 this.currentSize.width == cs.width && 
31244                 this.currentSize.x == cs.x && 
31245                 this.currentSize.height == cs.height && 
31246                 this.currentSize.y == cs.y 
31247         ) {
31248             Roo.log("no change in with or X or Y");
31249             return;
31250         }
31251         
31252         this.currentSize = cs;
31253         
31254         this.layout();
31255         
31256     },
31257     
31258     layout : function()
31259     {   
31260         this._resetLayout();
31261         
31262         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31263         
31264         this.layoutItems( isInstant );
31265       
31266         this._isLayoutInited = true;
31267         
31268         this.fireEvent('layout', this);
31269         
31270     },
31271     
31272     _resetLayout : function()
31273     {
31274         if(this.isHorizontal){
31275             this.horizontalMeasureColumns();
31276             return;
31277         }
31278         
31279         this.verticalMeasureColumns();
31280         
31281     },
31282     
31283     verticalMeasureColumns : function()
31284     {
31285         this.getContainerWidth();
31286         
31287 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31288 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31289 //            return;
31290 //        }
31291         
31292         var boxWidth = this.boxWidth + this.padWidth;
31293         
31294         if(this.containerWidth < this.boxWidth){
31295             boxWidth = this.containerWidth
31296         }
31297         
31298         var containerWidth = this.containerWidth;
31299         
31300         var cols = Math.floor(containerWidth / boxWidth);
31301         
31302         this.cols = Math.max( cols, 1 );
31303         
31304         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31305         
31306         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31307         
31308         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31309         
31310         this.colWidth = boxWidth + avail - this.padWidth;
31311         
31312         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31313         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31314     },
31315     
31316     horizontalMeasureColumns : function()
31317     {
31318         this.getContainerWidth();
31319         
31320         var boxWidth = this.boxWidth;
31321         
31322         if(this.containerWidth < boxWidth){
31323             boxWidth = this.containerWidth;
31324         }
31325         
31326         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31327         
31328         this.el.setHeight(boxWidth);
31329         
31330     },
31331     
31332     getContainerWidth : function()
31333     {
31334         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31335     },
31336     
31337     layoutItems : function( isInstant )
31338     {
31339         Roo.log(this.bricks);
31340         
31341         var items = Roo.apply([], this.bricks);
31342         
31343         if(this.isHorizontal){
31344             this._horizontalLayoutItems( items , isInstant );
31345             return;
31346         }
31347         
31348 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31349 //            this._verticalAlternativeLayoutItems( items , isInstant );
31350 //            return;
31351 //        }
31352         
31353         this._verticalLayoutItems( items , isInstant );
31354         
31355     },
31356     
31357     _verticalLayoutItems : function ( items , isInstant)
31358     {
31359         if ( !items || !items.length ) {
31360             return;
31361         }
31362         
31363         var standard = [
31364             ['xs', 'xs', 'xs', 'tall'],
31365             ['xs', 'xs', 'tall'],
31366             ['xs', 'xs', 'sm'],
31367             ['xs', 'xs', 'xs'],
31368             ['xs', 'tall'],
31369             ['xs', 'sm'],
31370             ['xs', 'xs'],
31371             ['xs'],
31372             
31373             ['sm', 'xs', 'xs'],
31374             ['sm', 'xs'],
31375             ['sm'],
31376             
31377             ['tall', 'xs', 'xs', 'xs'],
31378             ['tall', 'xs', 'xs'],
31379             ['tall', 'xs'],
31380             ['tall']
31381             
31382         ];
31383         
31384         var queue = [];
31385         
31386         var boxes = [];
31387         
31388         var box = [];
31389         
31390         Roo.each(items, function(item, k){
31391             
31392             switch (item.size) {
31393                 // these layouts take up a full box,
31394                 case 'md' :
31395                 case 'md-left' :
31396                 case 'md-right' :
31397                 case 'wide' :
31398                     
31399                     if(box.length){
31400                         boxes.push(box);
31401                         box = [];
31402                     }
31403                     
31404                     boxes.push([item]);
31405                     
31406                     break;
31407                     
31408                 case 'xs' :
31409                 case 'sm' :
31410                 case 'tall' :
31411                     
31412                     box.push(item);
31413                     
31414                     break;
31415                 default :
31416                     break;
31417                     
31418             }
31419             
31420         }, this);
31421         
31422         if(box.length){
31423             boxes.push(box);
31424             box = [];
31425         }
31426         
31427         var filterPattern = function(box, length)
31428         {
31429             if(!box.length){
31430                 return;
31431             }
31432             
31433             var match = false;
31434             
31435             var pattern = box.slice(0, length);
31436             
31437             var format = [];
31438             
31439             Roo.each(pattern, function(i){
31440                 format.push(i.size);
31441             }, this);
31442             
31443             Roo.each(standard, function(s){
31444                 
31445                 if(String(s) != String(format)){
31446                     return;
31447                 }
31448                 
31449                 match = true;
31450                 return false;
31451                 
31452             }, this);
31453             
31454             if(!match && length == 1){
31455                 return;
31456             }
31457             
31458             if(!match){
31459                 filterPattern(box, length - 1);
31460                 return;
31461             }
31462                 
31463             queue.push(pattern);
31464
31465             box = box.slice(length, box.length);
31466
31467             filterPattern(box, 4);
31468
31469             return;
31470             
31471         }
31472         
31473         Roo.each(boxes, function(box, k){
31474             
31475             if(!box.length){
31476                 return;
31477             }
31478             
31479             if(box.length == 1){
31480                 queue.push(box);
31481                 return;
31482             }
31483             
31484             filterPattern(box, 4);
31485             
31486         }, this);
31487         
31488         this._processVerticalLayoutQueue( queue, isInstant );
31489         
31490     },
31491     
31492 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31493 //    {
31494 //        if ( !items || !items.length ) {
31495 //            return;
31496 //        }
31497 //
31498 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31499 //        
31500 //    },
31501     
31502     _horizontalLayoutItems : function ( items , isInstant)
31503     {
31504         if ( !items || !items.length || items.length < 3) {
31505             return;
31506         }
31507         
31508         items.reverse();
31509         
31510         var eItems = items.slice(0, 3);
31511         
31512         items = items.slice(3, items.length);
31513         
31514         var standard = [
31515             ['xs', 'xs', 'xs', 'wide'],
31516             ['xs', 'xs', 'wide'],
31517             ['xs', 'xs', 'sm'],
31518             ['xs', 'xs', 'xs'],
31519             ['xs', 'wide'],
31520             ['xs', 'sm'],
31521             ['xs', 'xs'],
31522             ['xs'],
31523             
31524             ['sm', 'xs', 'xs'],
31525             ['sm', 'xs'],
31526             ['sm'],
31527             
31528             ['wide', 'xs', 'xs', 'xs'],
31529             ['wide', 'xs', 'xs'],
31530             ['wide', 'xs'],
31531             ['wide'],
31532             
31533             ['wide-thin']
31534         ];
31535         
31536         var queue = [];
31537         
31538         var boxes = [];
31539         
31540         var box = [];
31541         
31542         Roo.each(items, function(item, k){
31543             
31544             switch (item.size) {
31545                 case 'md' :
31546                 case 'md-left' :
31547                 case 'md-right' :
31548                 case 'tall' :
31549                     
31550                     if(box.length){
31551                         boxes.push(box);
31552                         box = [];
31553                     }
31554                     
31555                     boxes.push([item]);
31556                     
31557                     break;
31558                     
31559                 case 'xs' :
31560                 case 'sm' :
31561                 case 'wide' :
31562                 case 'wide-thin' :
31563                     
31564                     box.push(item);
31565                     
31566                     break;
31567                 default :
31568                     break;
31569                     
31570             }
31571             
31572         }, this);
31573         
31574         if(box.length){
31575             boxes.push(box);
31576             box = [];
31577         }
31578         
31579         var filterPattern = function(box, length)
31580         {
31581             if(!box.length){
31582                 return;
31583             }
31584             
31585             var match = false;
31586             
31587             var pattern = box.slice(0, length);
31588             
31589             var format = [];
31590             
31591             Roo.each(pattern, function(i){
31592                 format.push(i.size);
31593             }, this);
31594             
31595             Roo.each(standard, function(s){
31596                 
31597                 if(String(s) != String(format)){
31598                     return;
31599                 }
31600                 
31601                 match = true;
31602                 return false;
31603                 
31604             }, this);
31605             
31606             if(!match && length == 1){
31607                 return;
31608             }
31609             
31610             if(!match){
31611                 filterPattern(box, length - 1);
31612                 return;
31613             }
31614                 
31615             queue.push(pattern);
31616
31617             box = box.slice(length, box.length);
31618
31619             filterPattern(box, 4);
31620
31621             return;
31622             
31623         }
31624         
31625         Roo.each(boxes, function(box, k){
31626             
31627             if(!box.length){
31628                 return;
31629             }
31630             
31631             if(box.length == 1){
31632                 queue.push(box);
31633                 return;
31634             }
31635             
31636             filterPattern(box, 4);
31637             
31638         }, this);
31639         
31640         
31641         var prune = [];
31642         
31643         var pos = this.el.getBox(true);
31644         
31645         var minX = pos.x;
31646         
31647         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31648         
31649         var hit_end = false;
31650         
31651         Roo.each(queue, function(box){
31652             
31653             if(hit_end){
31654                 
31655                 Roo.each(box, function(b){
31656                 
31657                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31658                     b.el.hide();
31659
31660                 }, this);
31661
31662                 return;
31663             }
31664             
31665             var mx = 0;
31666             
31667             Roo.each(box, function(b){
31668                 
31669                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31670                 b.el.show();
31671
31672                 mx = Math.max(mx, b.x);
31673                 
31674             }, this);
31675             
31676             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31677             
31678             if(maxX < minX){
31679                 
31680                 Roo.each(box, function(b){
31681                 
31682                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31683                     b.el.hide();
31684                     
31685                 }, this);
31686                 
31687                 hit_end = true;
31688                 
31689                 return;
31690             }
31691             
31692             prune.push(box);
31693             
31694         }, this);
31695         
31696         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31697     },
31698     
31699     /** Sets position of item in DOM
31700     * @param {Element} item
31701     * @param {Number} x - horizontal position
31702     * @param {Number} y - vertical position
31703     * @param {Boolean} isInstant - disables transitions
31704     */
31705     _processVerticalLayoutQueue : function( queue, isInstant )
31706     {
31707         var pos = this.el.getBox(true);
31708         var x = pos.x;
31709         var y = pos.y;
31710         var maxY = [];
31711         
31712         for (var i = 0; i < this.cols; i++){
31713             maxY[i] = pos.y;
31714         }
31715         
31716         Roo.each(queue, function(box, k){
31717             
31718             var col = k % this.cols;
31719             
31720             Roo.each(box, function(b,kk){
31721                 
31722                 b.el.position('absolute');
31723                 
31724                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31725                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31726                 
31727                 if(b.size == 'md-left' || b.size == 'md-right'){
31728                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31729                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31730                 }
31731                 
31732                 b.el.setWidth(width);
31733                 b.el.setHeight(height);
31734                 // iframe?
31735                 b.el.select('iframe',true).setSize(width,height);
31736                 
31737             }, this);
31738             
31739             for (var i = 0; i < this.cols; i++){
31740                 
31741                 if(maxY[i] < maxY[col]){
31742                     col = i;
31743                     continue;
31744                 }
31745                 
31746                 col = Math.min(col, i);
31747                 
31748             }
31749             
31750             x = pos.x + col * (this.colWidth + this.padWidth);
31751             
31752             y = maxY[col];
31753             
31754             var positions = [];
31755             
31756             switch (box.length){
31757                 case 1 :
31758                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31759                     break;
31760                 case 2 :
31761                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31762                     break;
31763                 case 3 :
31764                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31765                     break;
31766                 case 4 :
31767                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31768                     break;
31769                 default :
31770                     break;
31771             }
31772             
31773             Roo.each(box, function(b,kk){
31774                 
31775                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31776                 
31777                 var sz = b.el.getSize();
31778                 
31779                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31780                 
31781             }, this);
31782             
31783         }, this);
31784         
31785         var mY = 0;
31786         
31787         for (var i = 0; i < this.cols; i++){
31788             mY = Math.max(mY, maxY[i]);
31789         }
31790         
31791         this.el.setHeight(mY - pos.y);
31792         
31793     },
31794     
31795 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31796 //    {
31797 //        var pos = this.el.getBox(true);
31798 //        var x = pos.x;
31799 //        var y = pos.y;
31800 //        var maxX = pos.right;
31801 //        
31802 //        var maxHeight = 0;
31803 //        
31804 //        Roo.each(items, function(item, k){
31805 //            
31806 //            var c = k % 2;
31807 //            
31808 //            item.el.position('absolute');
31809 //                
31810 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31811 //
31812 //            item.el.setWidth(width);
31813 //
31814 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31815 //
31816 //            item.el.setHeight(height);
31817 //            
31818 //            if(c == 0){
31819 //                item.el.setXY([x, y], isInstant ? false : true);
31820 //            } else {
31821 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31822 //            }
31823 //            
31824 //            y = y + height + this.alternativePadWidth;
31825 //            
31826 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31827 //            
31828 //        }, this);
31829 //        
31830 //        this.el.setHeight(maxHeight);
31831 //        
31832 //    },
31833     
31834     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31835     {
31836         var pos = this.el.getBox(true);
31837         
31838         var minX = pos.x;
31839         var minY = pos.y;
31840         
31841         var maxX = pos.right;
31842         
31843         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31844         
31845         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31846         
31847         Roo.each(queue, function(box, k){
31848             
31849             Roo.each(box, function(b, kk){
31850                 
31851                 b.el.position('absolute');
31852                 
31853                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31854                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31855                 
31856                 if(b.size == 'md-left' || b.size == 'md-right'){
31857                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31858                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31859                 }
31860                 
31861                 b.el.setWidth(width);
31862                 b.el.setHeight(height);
31863                 
31864             }, this);
31865             
31866             if(!box.length){
31867                 return;
31868             }
31869             
31870             var positions = [];
31871             
31872             switch (box.length){
31873                 case 1 :
31874                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31875                     break;
31876                 case 2 :
31877                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31878                     break;
31879                 case 3 :
31880                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31881                     break;
31882                 case 4 :
31883                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31884                     break;
31885                 default :
31886                     break;
31887             }
31888             
31889             Roo.each(box, function(b,kk){
31890                 
31891                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31892                 
31893                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31894                 
31895             }, this);
31896             
31897         }, this);
31898         
31899     },
31900     
31901     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31902     {
31903         Roo.each(eItems, function(b,k){
31904             
31905             b.size = (k == 0) ? 'sm' : 'xs';
31906             b.x = (k == 0) ? 2 : 1;
31907             b.y = (k == 0) ? 2 : 1;
31908             
31909             b.el.position('absolute');
31910             
31911             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31912                 
31913             b.el.setWidth(width);
31914             
31915             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31916             
31917             b.el.setHeight(height);
31918             
31919         }, this);
31920
31921         var positions = [];
31922         
31923         positions.push({
31924             x : maxX - this.unitWidth * 2 - this.gutter,
31925             y : minY
31926         });
31927         
31928         positions.push({
31929             x : maxX - this.unitWidth,
31930             y : minY + (this.unitWidth + this.gutter) * 2
31931         });
31932         
31933         positions.push({
31934             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31935             y : minY
31936         });
31937         
31938         Roo.each(eItems, function(b,k){
31939             
31940             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31941
31942         }, this);
31943         
31944     },
31945     
31946     getVerticalOneBoxColPositions : function(x, y, box)
31947     {
31948         var pos = [];
31949         
31950         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31951         
31952         if(box[0].size == 'md-left'){
31953             rand = 0;
31954         }
31955         
31956         if(box[0].size == 'md-right'){
31957             rand = 1;
31958         }
31959         
31960         pos.push({
31961             x : x + (this.unitWidth + this.gutter) * rand,
31962             y : y
31963         });
31964         
31965         return pos;
31966     },
31967     
31968     getVerticalTwoBoxColPositions : function(x, y, box)
31969     {
31970         var pos = [];
31971         
31972         if(box[0].size == 'xs'){
31973             
31974             pos.push({
31975                 x : x,
31976                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31977             });
31978
31979             pos.push({
31980                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31981                 y : y
31982             });
31983             
31984             return pos;
31985             
31986         }
31987         
31988         pos.push({
31989             x : x,
31990             y : y
31991         });
31992
31993         pos.push({
31994             x : x + (this.unitWidth + this.gutter) * 2,
31995             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31996         });
31997         
31998         return pos;
31999         
32000     },
32001     
32002     getVerticalThreeBoxColPositions : function(x, y, box)
32003     {
32004         var pos = [];
32005         
32006         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32007             
32008             pos.push({
32009                 x : x,
32010                 y : y
32011             });
32012
32013             pos.push({
32014                 x : x + (this.unitWidth + this.gutter) * 1,
32015                 y : y
32016             });
32017             
32018             pos.push({
32019                 x : x + (this.unitWidth + this.gutter) * 2,
32020                 y : y
32021             });
32022             
32023             return pos;
32024             
32025         }
32026         
32027         if(box[0].size == 'xs' && box[1].size == 'xs'){
32028             
32029             pos.push({
32030                 x : x,
32031                 y : y
32032             });
32033
32034             pos.push({
32035                 x : x,
32036                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32037             });
32038             
32039             pos.push({
32040                 x : x + (this.unitWidth + this.gutter) * 1,
32041                 y : y
32042             });
32043             
32044             return pos;
32045             
32046         }
32047         
32048         pos.push({
32049             x : x,
32050             y : y
32051         });
32052
32053         pos.push({
32054             x : x + (this.unitWidth + this.gutter) * 2,
32055             y : y
32056         });
32057
32058         pos.push({
32059             x : x + (this.unitWidth + this.gutter) * 2,
32060             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32061         });
32062             
32063         return pos;
32064         
32065     },
32066     
32067     getVerticalFourBoxColPositions : function(x, y, box)
32068     {
32069         var pos = [];
32070         
32071         if(box[0].size == 'xs'){
32072             
32073             pos.push({
32074                 x : x,
32075                 y : y
32076             });
32077
32078             pos.push({
32079                 x : x,
32080                 y : y + (this.unitHeight + this.gutter) * 1
32081             });
32082             
32083             pos.push({
32084                 x : x,
32085                 y : y + (this.unitHeight + this.gutter) * 2
32086             });
32087             
32088             pos.push({
32089                 x : x + (this.unitWidth + this.gutter) * 1,
32090                 y : y
32091             });
32092             
32093             return pos;
32094             
32095         }
32096         
32097         pos.push({
32098             x : x,
32099             y : y
32100         });
32101
32102         pos.push({
32103             x : x + (this.unitWidth + this.gutter) * 2,
32104             y : y
32105         });
32106
32107         pos.push({
32108             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32109             y : y + (this.unitHeight + this.gutter) * 1
32110         });
32111
32112         pos.push({
32113             x : x + (this.unitWidth + this.gutter) * 2,
32114             y : y + (this.unitWidth + this.gutter) * 2
32115         });
32116
32117         return pos;
32118         
32119     },
32120     
32121     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32122     {
32123         var pos = [];
32124         
32125         if(box[0].size == 'md-left'){
32126             pos.push({
32127                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32128                 y : minY
32129             });
32130             
32131             return pos;
32132         }
32133         
32134         if(box[0].size == 'md-right'){
32135             pos.push({
32136                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32137                 y : minY + (this.unitWidth + this.gutter) * 1
32138             });
32139             
32140             return pos;
32141         }
32142         
32143         var rand = Math.floor(Math.random() * (4 - box[0].y));
32144         
32145         pos.push({
32146             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32147             y : minY + (this.unitWidth + this.gutter) * rand
32148         });
32149         
32150         return pos;
32151         
32152     },
32153     
32154     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32155     {
32156         var pos = [];
32157         
32158         if(box[0].size == 'xs'){
32159             
32160             pos.push({
32161                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32162                 y : minY
32163             });
32164
32165             pos.push({
32166                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32167                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32168             });
32169             
32170             return pos;
32171             
32172         }
32173         
32174         pos.push({
32175             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32176             y : minY
32177         });
32178
32179         pos.push({
32180             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32181             y : minY + (this.unitWidth + this.gutter) * 2
32182         });
32183         
32184         return pos;
32185         
32186     },
32187     
32188     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32189     {
32190         var pos = [];
32191         
32192         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32193             
32194             pos.push({
32195                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32196                 y : minY
32197             });
32198
32199             pos.push({
32200                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32201                 y : minY + (this.unitWidth + this.gutter) * 1
32202             });
32203             
32204             pos.push({
32205                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32206                 y : minY + (this.unitWidth + this.gutter) * 2
32207             });
32208             
32209             return pos;
32210             
32211         }
32212         
32213         if(box[0].size == 'xs' && box[1].size == 'xs'){
32214             
32215             pos.push({
32216                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32217                 y : minY
32218             });
32219
32220             pos.push({
32221                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32222                 y : minY
32223             });
32224             
32225             pos.push({
32226                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32227                 y : minY + (this.unitWidth + this.gutter) * 1
32228             });
32229             
32230             return pos;
32231             
32232         }
32233         
32234         pos.push({
32235             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32236             y : minY
32237         });
32238
32239         pos.push({
32240             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32241             y : minY + (this.unitWidth + this.gutter) * 2
32242         });
32243
32244         pos.push({
32245             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32246             y : minY + (this.unitWidth + this.gutter) * 2
32247         });
32248             
32249         return pos;
32250         
32251     },
32252     
32253     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32254     {
32255         var pos = [];
32256         
32257         if(box[0].size == 'xs'){
32258             
32259             pos.push({
32260                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32261                 y : minY
32262             });
32263
32264             pos.push({
32265                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32266                 y : minY
32267             });
32268             
32269             pos.push({
32270                 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),
32271                 y : minY
32272             });
32273             
32274             pos.push({
32275                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32276                 y : minY + (this.unitWidth + this.gutter) * 1
32277             });
32278             
32279             return pos;
32280             
32281         }
32282         
32283         pos.push({
32284             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32285             y : minY
32286         });
32287         
32288         pos.push({
32289             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32290             y : minY + (this.unitWidth + this.gutter) * 2
32291         });
32292         
32293         pos.push({
32294             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32295             y : minY + (this.unitWidth + this.gutter) * 2
32296         });
32297         
32298         pos.push({
32299             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),
32300             y : minY + (this.unitWidth + this.gutter) * 2
32301         });
32302
32303         return pos;
32304         
32305     },
32306     
32307     /**
32308     * remove a Masonry Brick
32309     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32310     */
32311     removeBrick : function(brick_id)
32312     {
32313         if (!brick_id) {
32314             return;
32315         }
32316         
32317         for (var i = 0; i<this.bricks.length; i++) {
32318             if (this.bricks[i].id == brick_id) {
32319                 this.bricks.splice(i,1);
32320                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32321                 this.initial();
32322             }
32323         }
32324     },
32325     
32326     /**
32327     * adds a Masonry Brick
32328     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32329     */
32330     addBrick : function(cfg)
32331     {
32332         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32333         //this.register(cn);
32334         cn.parentId = this.id;
32335         cn.render(this.el);
32336         return cn;
32337     },
32338     
32339     /**
32340     * register a Masonry Brick
32341     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32342     */
32343     
32344     register : function(brick)
32345     {
32346         this.bricks.push(brick);
32347         brick.masonryId = this.id;
32348     },
32349     
32350     /**
32351     * clear all the Masonry Brick
32352     */
32353     clearAll : function()
32354     {
32355         this.bricks = [];
32356         //this.getChildContainer().dom.innerHTML = "";
32357         this.el.dom.innerHTML = '';
32358     },
32359     
32360     getSelected : function()
32361     {
32362         if (!this.selectedBrick) {
32363             return false;
32364         }
32365         
32366         return this.selectedBrick;
32367     }
32368 });
32369
32370 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32371     
32372     groups: {},
32373      /**
32374     * register a Masonry Layout
32375     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32376     */
32377     
32378     register : function(layout)
32379     {
32380         this.groups[layout.id] = layout;
32381     },
32382     /**
32383     * fetch a  Masonry Layout based on the masonry layout ID
32384     * @param {string} the masonry layout to add
32385     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32386     */
32387     
32388     get: function(layout_id) {
32389         if (typeof(this.groups[layout_id]) == 'undefined') {
32390             return false;
32391         }
32392         return this.groups[layout_id] ;
32393     }
32394     
32395     
32396     
32397 });
32398
32399  
32400
32401  /**
32402  *
32403  * This is based on 
32404  * http://masonry.desandro.com
32405  *
32406  * The idea is to render all the bricks based on vertical width...
32407  *
32408  * The original code extends 'outlayer' - we might need to use that....
32409  * 
32410  */
32411
32412
32413 /**
32414  * @class Roo.bootstrap.LayoutMasonryAuto
32415  * @extends Roo.bootstrap.Component
32416  * Bootstrap Layout Masonry class
32417  * 
32418  * @constructor
32419  * Create a new Element
32420  * @param {Object} config The config object
32421  */
32422
32423 Roo.bootstrap.LayoutMasonryAuto = function(config){
32424     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32425 };
32426
32427 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32428     
32429       /**
32430      * @cfg {Boolean} isFitWidth  - resize the width..
32431      */   
32432     isFitWidth : false,  // options..
32433     /**
32434      * @cfg {Boolean} isOriginLeft = left align?
32435      */   
32436     isOriginLeft : true,
32437     /**
32438      * @cfg {Boolean} isOriginTop = top align?
32439      */   
32440     isOriginTop : false,
32441     /**
32442      * @cfg {Boolean} isLayoutInstant = no animation?
32443      */   
32444     isLayoutInstant : false, // needed?
32445     /**
32446      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32447      */   
32448     isResizingContainer : true,
32449     /**
32450      * @cfg {Number} columnWidth  width of the columns 
32451      */   
32452     
32453     columnWidth : 0,
32454     
32455     /**
32456      * @cfg {Number} maxCols maximum number of columns
32457      */   
32458     
32459     maxCols: 0,
32460     /**
32461      * @cfg {Number} padHeight padding below box..
32462      */   
32463     
32464     padHeight : 10, 
32465     
32466     /**
32467      * @cfg {Boolean} isAutoInitial defalut true
32468      */   
32469     
32470     isAutoInitial : true, 
32471     
32472     // private?
32473     gutter : 0,
32474     
32475     containerWidth: 0,
32476     initialColumnWidth : 0,
32477     currentSize : null,
32478     
32479     colYs : null, // array.
32480     maxY : 0,
32481     padWidth: 10,
32482     
32483     
32484     tag: 'div',
32485     cls: '',
32486     bricks: null, //CompositeElement
32487     cols : 0, // array?
32488     // element : null, // wrapped now this.el
32489     _isLayoutInited : null, 
32490     
32491     
32492     getAutoCreate : function(){
32493         
32494         var cfg = {
32495             tag: this.tag,
32496             cls: 'blog-masonary-wrapper ' + this.cls,
32497             cn : {
32498                 cls : 'mas-boxes masonary'
32499             }
32500         };
32501         
32502         return cfg;
32503     },
32504     
32505     getChildContainer: function( )
32506     {
32507         if (this.boxesEl) {
32508             return this.boxesEl;
32509         }
32510         
32511         this.boxesEl = this.el.select('.mas-boxes').first();
32512         
32513         return this.boxesEl;
32514     },
32515     
32516     
32517     initEvents : function()
32518     {
32519         var _this = this;
32520         
32521         if(this.isAutoInitial){
32522             Roo.log('hook children rendered');
32523             this.on('childrenrendered', function() {
32524                 Roo.log('children rendered');
32525                 _this.initial();
32526             } ,this);
32527         }
32528         
32529     },
32530     
32531     initial : function()
32532     {
32533         this.reloadItems();
32534
32535         this.currentSize = this.el.getBox(true);
32536
32537         /// was window resize... - let's see if this works..
32538         Roo.EventManager.onWindowResize(this.resize, this); 
32539
32540         if(!this.isAutoInitial){
32541             this.layout();
32542             return;
32543         }
32544         
32545         this.layout.defer(500,this);
32546     },
32547     
32548     reloadItems: function()
32549     {
32550         this.bricks = this.el.select('.masonry-brick', true);
32551         
32552         this.bricks.each(function(b) {
32553             //Roo.log(b.getSize());
32554             if (!b.attr('originalwidth')) {
32555                 b.attr('originalwidth',  b.getSize().width);
32556             }
32557             
32558         });
32559         
32560         Roo.log(this.bricks.elements.length);
32561     },
32562     
32563     resize : function()
32564     {
32565         Roo.log('resize');
32566         var cs = this.el.getBox(true);
32567         
32568         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32569             Roo.log("no change in with or X");
32570             return;
32571         }
32572         this.currentSize = cs;
32573         this.layout();
32574     },
32575     
32576     layout : function()
32577     {
32578          Roo.log('layout');
32579         this._resetLayout();
32580         //this._manageStamps();
32581       
32582         // don't animate first layout
32583         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32584         this.layoutItems( isInstant );
32585       
32586         // flag for initalized
32587         this._isLayoutInited = true;
32588     },
32589     
32590     layoutItems : function( isInstant )
32591     {
32592         //var items = this._getItemsForLayout( this.items );
32593         // original code supports filtering layout items.. we just ignore it..
32594         
32595         this._layoutItems( this.bricks , isInstant );
32596       
32597         this._postLayout();
32598     },
32599     _layoutItems : function ( items , isInstant)
32600     {
32601        //this.fireEvent( 'layout', this, items );
32602     
32603
32604         if ( !items || !items.elements.length ) {
32605           // no items, emit event with empty array
32606             return;
32607         }
32608
32609         var queue = [];
32610         items.each(function(item) {
32611             Roo.log("layout item");
32612             Roo.log(item);
32613             // get x/y object from method
32614             var position = this._getItemLayoutPosition( item );
32615             // enqueue
32616             position.item = item;
32617             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32618             queue.push( position );
32619         }, this);
32620       
32621         this._processLayoutQueue( queue );
32622     },
32623     /** Sets position of item in DOM
32624     * @param {Element} item
32625     * @param {Number} x - horizontal position
32626     * @param {Number} y - vertical position
32627     * @param {Boolean} isInstant - disables transitions
32628     */
32629     _processLayoutQueue : function( queue )
32630     {
32631         for ( var i=0, len = queue.length; i < len; i++ ) {
32632             var obj = queue[i];
32633             obj.item.position('absolute');
32634             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32635         }
32636     },
32637       
32638     
32639     /**
32640     * Any logic you want to do after each layout,
32641     * i.e. size the container
32642     */
32643     _postLayout : function()
32644     {
32645         this.resizeContainer();
32646     },
32647     
32648     resizeContainer : function()
32649     {
32650         if ( !this.isResizingContainer ) {
32651             return;
32652         }
32653         var size = this._getContainerSize();
32654         if ( size ) {
32655             this.el.setSize(size.width,size.height);
32656             this.boxesEl.setSize(size.width,size.height);
32657         }
32658     },
32659     
32660     
32661     
32662     _resetLayout : function()
32663     {
32664         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32665         this.colWidth = this.el.getWidth();
32666         //this.gutter = this.el.getWidth(); 
32667         
32668         this.measureColumns();
32669
32670         // reset column Y
32671         var i = this.cols;
32672         this.colYs = [];
32673         while (i--) {
32674             this.colYs.push( 0 );
32675         }
32676     
32677         this.maxY = 0;
32678     },
32679
32680     measureColumns : function()
32681     {
32682         this.getContainerWidth();
32683       // if columnWidth is 0, default to outerWidth of first item
32684         if ( !this.columnWidth ) {
32685             var firstItem = this.bricks.first();
32686             Roo.log(firstItem);
32687             this.columnWidth  = this.containerWidth;
32688             if (firstItem && firstItem.attr('originalwidth') ) {
32689                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32690             }
32691             // columnWidth fall back to item of first element
32692             Roo.log("set column width?");
32693                         this.initialColumnWidth = this.columnWidth  ;
32694
32695             // if first elem has no width, default to size of container
32696             
32697         }
32698         
32699         
32700         if (this.initialColumnWidth) {
32701             this.columnWidth = this.initialColumnWidth;
32702         }
32703         
32704         
32705             
32706         // column width is fixed at the top - however if container width get's smaller we should
32707         // reduce it...
32708         
32709         // this bit calcs how man columns..
32710             
32711         var columnWidth = this.columnWidth += this.gutter;
32712       
32713         // calculate columns
32714         var containerWidth = this.containerWidth + this.gutter;
32715         
32716         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32717         // fix rounding errors, typically with gutters
32718         var excess = columnWidth - containerWidth % columnWidth;
32719         
32720         
32721         // if overshoot is less than a pixel, round up, otherwise floor it
32722         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32723         cols = Math[ mathMethod ]( cols );
32724         this.cols = Math.max( cols, 1 );
32725         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32726         
32727          // padding positioning..
32728         var totalColWidth = this.cols * this.columnWidth;
32729         var padavail = this.containerWidth - totalColWidth;
32730         // so for 2 columns - we need 3 'pads'
32731         
32732         var padNeeded = (1+this.cols) * this.padWidth;
32733         
32734         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32735         
32736         this.columnWidth += padExtra
32737         //this.padWidth = Math.floor(padavail /  ( this.cols));
32738         
32739         // adjust colum width so that padding is fixed??
32740         
32741         // we have 3 columns ... total = width * 3
32742         // we have X left over... that should be used by 
32743         
32744         //if (this.expandC) {
32745             
32746         //}
32747         
32748         
32749         
32750     },
32751     
32752     getContainerWidth : function()
32753     {
32754        /* // container is parent if fit width
32755         var container = this.isFitWidth ? this.element.parentNode : this.element;
32756         // check that this.size and size are there
32757         // IE8 triggers resize on body size change, so they might not be
32758         
32759         var size = getSize( container );  //FIXME
32760         this.containerWidth = size && size.innerWidth; //FIXME
32761         */
32762          
32763         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32764         
32765     },
32766     
32767     _getItemLayoutPosition : function( item )  // what is item?
32768     {
32769         // we resize the item to our columnWidth..
32770       
32771         item.setWidth(this.columnWidth);
32772         item.autoBoxAdjust  = false;
32773         
32774         var sz = item.getSize();
32775  
32776         // how many columns does this brick span
32777         var remainder = this.containerWidth % this.columnWidth;
32778         
32779         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32780         // round if off by 1 pixel, otherwise use ceil
32781         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32782         colSpan = Math.min( colSpan, this.cols );
32783         
32784         // normally this should be '1' as we dont' currently allow multi width columns..
32785         
32786         var colGroup = this._getColGroup( colSpan );
32787         // get the minimum Y value from the columns
32788         var minimumY = Math.min.apply( Math, colGroup );
32789         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32790         
32791         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32792          
32793         // position the brick
32794         var position = {
32795             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32796             y: this.currentSize.y + minimumY + this.padHeight
32797         };
32798         
32799         Roo.log(position);
32800         // apply setHeight to necessary columns
32801         var setHeight = minimumY + sz.height + this.padHeight;
32802         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32803         
32804         var setSpan = this.cols + 1 - colGroup.length;
32805         for ( var i = 0; i < setSpan; i++ ) {
32806           this.colYs[ shortColIndex + i ] = setHeight ;
32807         }
32808       
32809         return position;
32810     },
32811     
32812     /**
32813      * @param {Number} colSpan - number of columns the element spans
32814      * @returns {Array} colGroup
32815      */
32816     _getColGroup : function( colSpan )
32817     {
32818         if ( colSpan < 2 ) {
32819           // if brick spans only one column, use all the column Ys
32820           return this.colYs;
32821         }
32822       
32823         var colGroup = [];
32824         // how many different places could this brick fit horizontally
32825         var groupCount = this.cols + 1 - colSpan;
32826         // for each group potential horizontal position
32827         for ( var i = 0; i < groupCount; i++ ) {
32828           // make an array of colY values for that one group
32829           var groupColYs = this.colYs.slice( i, i + colSpan );
32830           // and get the max value of the array
32831           colGroup[i] = Math.max.apply( Math, groupColYs );
32832         }
32833         return colGroup;
32834     },
32835     /*
32836     _manageStamp : function( stamp )
32837     {
32838         var stampSize =  stamp.getSize();
32839         var offset = stamp.getBox();
32840         // get the columns that this stamp affects
32841         var firstX = this.isOriginLeft ? offset.x : offset.right;
32842         var lastX = firstX + stampSize.width;
32843         var firstCol = Math.floor( firstX / this.columnWidth );
32844         firstCol = Math.max( 0, firstCol );
32845         
32846         var lastCol = Math.floor( lastX / this.columnWidth );
32847         // lastCol should not go over if multiple of columnWidth #425
32848         lastCol -= lastX % this.columnWidth ? 0 : 1;
32849         lastCol = Math.min( this.cols - 1, lastCol );
32850         
32851         // set colYs to bottom of the stamp
32852         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32853             stampSize.height;
32854             
32855         for ( var i = firstCol; i <= lastCol; i++ ) {
32856           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32857         }
32858     },
32859     */
32860     
32861     _getContainerSize : function()
32862     {
32863         this.maxY = Math.max.apply( Math, this.colYs );
32864         var size = {
32865             height: this.maxY
32866         };
32867       
32868         if ( this.isFitWidth ) {
32869             size.width = this._getContainerFitWidth();
32870         }
32871       
32872         return size;
32873     },
32874     
32875     _getContainerFitWidth : function()
32876     {
32877         var unusedCols = 0;
32878         // count unused columns
32879         var i = this.cols;
32880         while ( --i ) {
32881           if ( this.colYs[i] !== 0 ) {
32882             break;
32883           }
32884           unusedCols++;
32885         }
32886         // fit container to columns that have been used
32887         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32888     },
32889     
32890     needsResizeLayout : function()
32891     {
32892         var previousWidth = this.containerWidth;
32893         this.getContainerWidth();
32894         return previousWidth !== this.containerWidth;
32895     }
32896  
32897 });
32898
32899  
32900
32901  /*
32902  * - LGPL
32903  *
32904  * element
32905  * 
32906  */
32907
32908 /**
32909  * @class Roo.bootstrap.MasonryBrick
32910  * @extends Roo.bootstrap.Component
32911  * Bootstrap MasonryBrick class
32912  * 
32913  * @constructor
32914  * Create a new MasonryBrick
32915  * @param {Object} config The config object
32916  */
32917
32918 Roo.bootstrap.MasonryBrick = function(config){
32919     
32920     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32921     
32922     Roo.bootstrap.MasonryBrick.register(this);
32923     
32924     this.addEvents({
32925         // raw events
32926         /**
32927          * @event click
32928          * When a MasonryBrick is clcik
32929          * @param {Roo.bootstrap.MasonryBrick} this
32930          * @param {Roo.EventObject} e
32931          */
32932         "click" : true
32933     });
32934 };
32935
32936 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32937     
32938     /**
32939      * @cfg {String} title
32940      */   
32941     title : '',
32942     /**
32943      * @cfg {String} html
32944      */   
32945     html : '',
32946     /**
32947      * @cfg {String} bgimage
32948      */   
32949     bgimage : '',
32950     /**
32951      * @cfg {String} videourl
32952      */   
32953     videourl : '',
32954     /**
32955      * @cfg {String} cls
32956      */   
32957     cls : '',
32958     /**
32959      * @cfg {String} href
32960      */   
32961     href : '',
32962     /**
32963      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32964      */   
32965     size : 'xs',
32966     
32967     /**
32968      * @cfg {String} placetitle (center|bottom)
32969      */   
32970     placetitle : '',
32971     
32972     /**
32973      * @cfg {Boolean} isFitContainer defalut true
32974      */   
32975     isFitContainer : true, 
32976     
32977     /**
32978      * @cfg {Boolean} preventDefault defalut false
32979      */   
32980     preventDefault : false, 
32981     
32982     /**
32983      * @cfg {Boolean} inverse defalut false
32984      */   
32985     maskInverse : false, 
32986     
32987     getAutoCreate : function()
32988     {
32989         if(!this.isFitContainer){
32990             return this.getSplitAutoCreate();
32991         }
32992         
32993         var cls = 'masonry-brick masonry-brick-full';
32994         
32995         if(this.href.length){
32996             cls += ' masonry-brick-link';
32997         }
32998         
32999         if(this.bgimage.length){
33000             cls += ' masonry-brick-image';
33001         }
33002         
33003         if(this.maskInverse){
33004             cls += ' mask-inverse';
33005         }
33006         
33007         if(!this.html.length && !this.maskInverse && !this.videourl.length){
33008             cls += ' enable-mask';
33009         }
33010         
33011         if(this.size){
33012             cls += ' masonry-' + this.size + '-brick';
33013         }
33014         
33015         if(this.placetitle.length){
33016             
33017             switch (this.placetitle) {
33018                 case 'center' :
33019                     cls += ' masonry-center-title';
33020                     break;
33021                 case 'bottom' :
33022                     cls += ' masonry-bottom-title';
33023                     break;
33024                 default:
33025                     break;
33026             }
33027             
33028         } else {
33029             if(!this.html.length && !this.bgimage.length){
33030                 cls += ' masonry-center-title';
33031             }
33032
33033             if(!this.html.length && this.bgimage.length){
33034                 cls += ' masonry-bottom-title';
33035             }
33036         }
33037         
33038         if(this.cls){
33039             cls += ' ' + this.cls;
33040         }
33041         
33042         var cfg = {
33043             tag: (this.href.length) ? 'a' : 'div',
33044             cls: cls,
33045             cn: [
33046                 {
33047                     tag: 'div',
33048                     cls: 'masonry-brick-mask'
33049                 },
33050                 {
33051                     tag: 'div',
33052                     cls: 'masonry-brick-paragraph',
33053                     cn: []
33054                 }
33055             ]
33056         };
33057         
33058         if(this.href.length){
33059             cfg.href = this.href;
33060         }
33061         
33062         var cn = cfg.cn[1].cn;
33063         
33064         if(this.title.length){
33065             cn.push({
33066                 tag: 'h4',
33067                 cls: 'masonry-brick-title',
33068                 html: this.title
33069             });
33070         }
33071         
33072         if(this.html.length){
33073             cn.push({
33074                 tag: 'p',
33075                 cls: 'masonry-brick-text',
33076                 html: this.html
33077             });
33078         }
33079         
33080         if (!this.title.length && !this.html.length) {
33081             cfg.cn[1].cls += ' hide';
33082         }
33083         
33084         if(this.bgimage.length){
33085             cfg.cn.push({
33086                 tag: 'img',
33087                 cls: 'masonry-brick-image-view',
33088                 src: this.bgimage
33089             });
33090         }
33091         
33092         if(this.videourl.length){
33093             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33094             // youtube support only?
33095             cfg.cn.push({
33096                 tag: 'iframe',
33097                 cls: 'masonry-brick-image-view',
33098                 src: vurl,
33099                 frameborder : 0,
33100                 allowfullscreen : true
33101             });
33102         }
33103         
33104         return cfg;
33105         
33106     },
33107     
33108     getSplitAutoCreate : function()
33109     {
33110         var cls = 'masonry-brick masonry-brick-split';
33111         
33112         if(this.href.length){
33113             cls += ' masonry-brick-link';
33114         }
33115         
33116         if(this.bgimage.length){
33117             cls += ' masonry-brick-image';
33118         }
33119         
33120         if(this.size){
33121             cls += ' masonry-' + this.size + '-brick';
33122         }
33123         
33124         switch (this.placetitle) {
33125             case 'center' :
33126                 cls += ' masonry-center-title';
33127                 break;
33128             case 'bottom' :
33129                 cls += ' masonry-bottom-title';
33130                 break;
33131             default:
33132                 if(!this.bgimage.length){
33133                     cls += ' masonry-center-title';
33134                 }
33135
33136                 if(this.bgimage.length){
33137                     cls += ' masonry-bottom-title';
33138                 }
33139                 break;
33140         }
33141         
33142         if(this.cls){
33143             cls += ' ' + this.cls;
33144         }
33145         
33146         var cfg = {
33147             tag: (this.href.length) ? 'a' : 'div',
33148             cls: cls,
33149             cn: [
33150                 {
33151                     tag: 'div',
33152                     cls: 'masonry-brick-split-head',
33153                     cn: [
33154                         {
33155                             tag: 'div',
33156                             cls: 'masonry-brick-paragraph',
33157                             cn: []
33158                         }
33159                     ]
33160                 },
33161                 {
33162                     tag: 'div',
33163                     cls: 'masonry-brick-split-body',
33164                     cn: []
33165                 }
33166             ]
33167         };
33168         
33169         if(this.href.length){
33170             cfg.href = this.href;
33171         }
33172         
33173         if(this.title.length){
33174             cfg.cn[0].cn[0].cn.push({
33175                 tag: 'h4',
33176                 cls: 'masonry-brick-title',
33177                 html: this.title
33178             });
33179         }
33180         
33181         if(this.html.length){
33182             cfg.cn[1].cn.push({
33183                 tag: 'p',
33184                 cls: 'masonry-brick-text',
33185                 html: this.html
33186             });
33187         }
33188
33189         if(this.bgimage.length){
33190             cfg.cn[0].cn.push({
33191                 tag: 'img',
33192                 cls: 'masonry-brick-image-view',
33193                 src: this.bgimage
33194             });
33195         }
33196         
33197         if(this.videourl.length){
33198             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33199             // youtube support only?
33200             cfg.cn[0].cn.cn.push({
33201                 tag: 'iframe',
33202                 cls: 'masonry-brick-image-view',
33203                 src: vurl,
33204                 frameborder : 0,
33205                 allowfullscreen : true
33206             });
33207         }
33208         
33209         return cfg;
33210     },
33211     
33212     initEvents: function() 
33213     {
33214         switch (this.size) {
33215             case 'xs' :
33216                 this.x = 1;
33217                 this.y = 1;
33218                 break;
33219             case 'sm' :
33220                 this.x = 2;
33221                 this.y = 2;
33222                 break;
33223             case 'md' :
33224             case 'md-left' :
33225             case 'md-right' :
33226                 this.x = 3;
33227                 this.y = 3;
33228                 break;
33229             case 'tall' :
33230                 this.x = 2;
33231                 this.y = 3;
33232                 break;
33233             case 'wide' :
33234                 this.x = 3;
33235                 this.y = 2;
33236                 break;
33237             case 'wide-thin' :
33238                 this.x = 3;
33239                 this.y = 1;
33240                 break;
33241                         
33242             default :
33243                 break;
33244         }
33245         
33246         if(Roo.isTouch){
33247             this.el.on('touchstart', this.onTouchStart, this);
33248             this.el.on('touchmove', this.onTouchMove, this);
33249             this.el.on('touchend', this.onTouchEnd, this);
33250             this.el.on('contextmenu', this.onContextMenu, this);
33251         } else {
33252             this.el.on('mouseenter'  ,this.enter, this);
33253             this.el.on('mouseleave', this.leave, this);
33254             this.el.on('click', this.onClick, this);
33255         }
33256         
33257         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33258             this.parent().bricks.push(this);   
33259         }
33260         
33261     },
33262     
33263     onClick: function(e, el)
33264     {
33265         var time = this.endTimer - this.startTimer;
33266         // Roo.log(e.preventDefault());
33267         if(Roo.isTouch){
33268             if(time > 1000){
33269                 e.preventDefault();
33270                 return;
33271             }
33272         }
33273         
33274         if(!this.preventDefault){
33275             return;
33276         }
33277         
33278         e.preventDefault();
33279         
33280         if (this.activeClass != '') {
33281             this.selectBrick();
33282         }
33283         
33284         this.fireEvent('click', this, e);
33285     },
33286     
33287     enter: function(e, el)
33288     {
33289         e.preventDefault();
33290         
33291         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33292             return;
33293         }
33294         
33295         if(this.bgimage.length && this.html.length){
33296             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33297         }
33298     },
33299     
33300     leave: function(e, el)
33301     {
33302         e.preventDefault();
33303         
33304         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33305             return;
33306         }
33307         
33308         if(this.bgimage.length && this.html.length){
33309             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33310         }
33311     },
33312     
33313     onTouchStart: function(e, el)
33314     {
33315 //        e.preventDefault();
33316         
33317         this.touchmoved = false;
33318         
33319         if(!this.isFitContainer){
33320             return;
33321         }
33322         
33323         if(!this.bgimage.length || !this.html.length){
33324             return;
33325         }
33326         
33327         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33328         
33329         this.timer = new Date().getTime();
33330         
33331     },
33332     
33333     onTouchMove: function(e, el)
33334     {
33335         this.touchmoved = true;
33336     },
33337     
33338     onContextMenu : function(e,el)
33339     {
33340         e.preventDefault();
33341         e.stopPropagation();
33342         return false;
33343     },
33344     
33345     onTouchEnd: function(e, el)
33346     {
33347 //        e.preventDefault();
33348         
33349         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33350         
33351             this.leave(e,el);
33352             
33353             return;
33354         }
33355         
33356         if(!this.bgimage.length || !this.html.length){
33357             
33358             if(this.href.length){
33359                 window.location.href = this.href;
33360             }
33361             
33362             return;
33363         }
33364         
33365         if(!this.isFitContainer){
33366             return;
33367         }
33368         
33369         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33370         
33371         window.location.href = this.href;
33372     },
33373     
33374     //selection on single brick only
33375     selectBrick : function() {
33376         
33377         if (!this.parentId) {
33378             return;
33379         }
33380         
33381         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33382         var index = m.selectedBrick.indexOf(this.id);
33383         
33384         if ( index > -1) {
33385             m.selectedBrick.splice(index,1);
33386             this.el.removeClass(this.activeClass);
33387             return;
33388         }
33389         
33390         for(var i = 0; i < m.selectedBrick.length; i++) {
33391             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33392             b.el.removeClass(b.activeClass);
33393         }
33394         
33395         m.selectedBrick = [];
33396         
33397         m.selectedBrick.push(this.id);
33398         this.el.addClass(this.activeClass);
33399         return;
33400     },
33401     
33402     isSelected : function(){
33403         return this.el.hasClass(this.activeClass);
33404         
33405     }
33406 });
33407
33408 Roo.apply(Roo.bootstrap.MasonryBrick, {
33409     
33410     //groups: {},
33411     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33412      /**
33413     * register a Masonry Brick
33414     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33415     */
33416     
33417     register : function(brick)
33418     {
33419         //this.groups[brick.id] = brick;
33420         this.groups.add(brick.id, brick);
33421     },
33422     /**
33423     * fetch a  masonry brick based on the masonry brick ID
33424     * @param {string} the masonry brick to add
33425     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33426     */
33427     
33428     get: function(brick_id) 
33429     {
33430         // if (typeof(this.groups[brick_id]) == 'undefined') {
33431         //     return false;
33432         // }
33433         // return this.groups[brick_id] ;
33434         
33435         if(this.groups.key(brick_id)) {
33436             return this.groups.key(brick_id);
33437         }
33438         
33439         return false;
33440     }
33441     
33442     
33443     
33444 });
33445
33446  /*
33447  * - LGPL
33448  *
33449  * element
33450  * 
33451  */
33452
33453 /**
33454  * @class Roo.bootstrap.Brick
33455  * @extends Roo.bootstrap.Component
33456  * Bootstrap Brick class
33457  * 
33458  * @constructor
33459  * Create a new Brick
33460  * @param {Object} config The config object
33461  */
33462
33463 Roo.bootstrap.Brick = function(config){
33464     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33465     
33466     this.addEvents({
33467         // raw events
33468         /**
33469          * @event click
33470          * When a Brick is click
33471          * @param {Roo.bootstrap.Brick} this
33472          * @param {Roo.EventObject} e
33473          */
33474         "click" : true
33475     });
33476 };
33477
33478 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33479     
33480     /**
33481      * @cfg {String} title
33482      */   
33483     title : '',
33484     /**
33485      * @cfg {String} html
33486      */   
33487     html : '',
33488     /**
33489      * @cfg {String} bgimage
33490      */   
33491     bgimage : '',
33492     /**
33493      * @cfg {String} cls
33494      */   
33495     cls : '',
33496     /**
33497      * @cfg {String} href
33498      */   
33499     href : '',
33500     /**
33501      * @cfg {String} video
33502      */   
33503     video : '',
33504     /**
33505      * @cfg {Boolean} square
33506      */   
33507     square : true,
33508     
33509     getAutoCreate : function()
33510     {
33511         var cls = 'roo-brick';
33512         
33513         if(this.href.length){
33514             cls += ' roo-brick-link';
33515         }
33516         
33517         if(this.bgimage.length){
33518             cls += ' roo-brick-image';
33519         }
33520         
33521         if(!this.html.length && !this.bgimage.length){
33522             cls += ' roo-brick-center-title';
33523         }
33524         
33525         if(!this.html.length && this.bgimage.length){
33526             cls += ' roo-brick-bottom-title';
33527         }
33528         
33529         if(this.cls){
33530             cls += ' ' + this.cls;
33531         }
33532         
33533         var cfg = {
33534             tag: (this.href.length) ? 'a' : 'div',
33535             cls: cls,
33536             cn: [
33537                 {
33538                     tag: 'div',
33539                     cls: 'roo-brick-paragraph',
33540                     cn: []
33541                 }
33542             ]
33543         };
33544         
33545         if(this.href.length){
33546             cfg.href = this.href;
33547         }
33548         
33549         var cn = cfg.cn[0].cn;
33550         
33551         if(this.title.length){
33552             cn.push({
33553                 tag: 'h4',
33554                 cls: 'roo-brick-title',
33555                 html: this.title
33556             });
33557         }
33558         
33559         if(this.html.length){
33560             cn.push({
33561                 tag: 'p',
33562                 cls: 'roo-brick-text',
33563                 html: this.html
33564             });
33565         } else {
33566             cn.cls += ' hide';
33567         }
33568         
33569         if(this.bgimage.length){
33570             cfg.cn.push({
33571                 tag: 'img',
33572                 cls: 'roo-brick-image-view',
33573                 src: this.bgimage
33574             });
33575         }
33576         
33577         return cfg;
33578     },
33579     
33580     initEvents: function() 
33581     {
33582         if(this.title.length || this.html.length){
33583             this.el.on('mouseenter'  ,this.enter, this);
33584             this.el.on('mouseleave', this.leave, this);
33585         }
33586         
33587         Roo.EventManager.onWindowResize(this.resize, this); 
33588         
33589         if(this.bgimage.length){
33590             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33591             this.imageEl.on('load', this.onImageLoad, this);
33592             return;
33593         }
33594         
33595         this.resize();
33596     },
33597     
33598     onImageLoad : function()
33599     {
33600         this.resize();
33601     },
33602     
33603     resize : function()
33604     {
33605         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33606         
33607         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33608         
33609         if(this.bgimage.length){
33610             var image = this.el.select('.roo-brick-image-view', true).first();
33611             
33612             image.setWidth(paragraph.getWidth());
33613             
33614             if(this.square){
33615                 image.setHeight(paragraph.getWidth());
33616             }
33617             
33618             this.el.setHeight(image.getHeight());
33619             paragraph.setHeight(image.getHeight());
33620             
33621         }
33622         
33623     },
33624     
33625     enter: function(e, el)
33626     {
33627         e.preventDefault();
33628         
33629         if(this.bgimage.length){
33630             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33631             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33632         }
33633     },
33634     
33635     leave: function(e, el)
33636     {
33637         e.preventDefault();
33638         
33639         if(this.bgimage.length){
33640             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33641             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33642         }
33643     }
33644     
33645 });
33646
33647  
33648
33649  /*
33650  * - LGPL
33651  *
33652  * Number field 
33653  */
33654
33655 /**
33656  * @class Roo.bootstrap.NumberField
33657  * @extends Roo.bootstrap.Input
33658  * Bootstrap NumberField class
33659  * 
33660  * 
33661  * 
33662  * 
33663  * @constructor
33664  * Create a new NumberField
33665  * @param {Object} config The config object
33666  */
33667
33668 Roo.bootstrap.NumberField = function(config){
33669     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33670 };
33671
33672 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33673     
33674     /**
33675      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33676      */
33677     allowDecimals : true,
33678     /**
33679      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33680      */
33681     decimalSeparator : ".",
33682     /**
33683      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33684      */
33685     decimalPrecision : 2,
33686     /**
33687      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33688      */
33689     allowNegative : true,
33690     
33691     /**
33692      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33693      */
33694     allowZero: true,
33695     /**
33696      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33697      */
33698     minValue : Number.NEGATIVE_INFINITY,
33699     /**
33700      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33701      */
33702     maxValue : Number.MAX_VALUE,
33703     /**
33704      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33705      */
33706     minText : "The minimum value for this field is {0}",
33707     /**
33708      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33709      */
33710     maxText : "The maximum value for this field is {0}",
33711     /**
33712      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33713      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33714      */
33715     nanText : "{0} is not a valid number",
33716     /**
33717      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33718      */
33719     thousandsDelimiter : false,
33720     /**
33721      * @cfg {String} valueAlign alignment of value
33722      */
33723     valueAlign : "left",
33724
33725     getAutoCreate : function()
33726     {
33727         var hiddenInput = {
33728             tag: 'input',
33729             type: 'hidden',
33730             id: Roo.id(),
33731             cls: 'hidden-number-input'
33732         };
33733         
33734         if (this.name) {
33735             hiddenInput.name = this.name;
33736         }
33737         
33738         this.name = '';
33739         
33740         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33741         
33742         this.name = hiddenInput.name;
33743         
33744         if(cfg.cn.length > 0) {
33745             cfg.cn.push(hiddenInput);
33746         }
33747         
33748         return cfg;
33749     },
33750
33751     // private
33752     initEvents : function()
33753     {   
33754         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33755         
33756         var allowed = "0123456789";
33757         
33758         if(this.allowDecimals){
33759             allowed += this.decimalSeparator;
33760         }
33761         
33762         if(this.allowNegative){
33763             allowed += "-";
33764         }
33765         
33766         if(this.thousandsDelimiter) {
33767             allowed += ",";
33768         }
33769         
33770         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33771         
33772         var keyPress = function(e){
33773             
33774             var k = e.getKey();
33775             
33776             var c = e.getCharCode();
33777             
33778             if(
33779                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33780                     allowed.indexOf(String.fromCharCode(c)) === -1
33781             ){
33782                 e.stopEvent();
33783                 return;
33784             }
33785             
33786             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33787                 return;
33788             }
33789             
33790             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33791                 e.stopEvent();
33792             }
33793         };
33794         
33795         this.el.on("keypress", keyPress, this);
33796     },
33797     
33798     validateValue : function(value)
33799     {
33800         
33801         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33802             return false;
33803         }
33804         
33805         var num = this.parseValue(value);
33806         
33807         if(isNaN(num)){
33808             this.markInvalid(String.format(this.nanText, value));
33809             return false;
33810         }
33811         
33812         if(num < this.minValue){
33813             this.markInvalid(String.format(this.minText, this.minValue));
33814             return false;
33815         }
33816         
33817         if(num > this.maxValue){
33818             this.markInvalid(String.format(this.maxText, this.maxValue));
33819             return false;
33820         }
33821         
33822         return true;
33823     },
33824
33825     getValue : function()
33826     {
33827         var v = this.hiddenEl().getValue();
33828         
33829         return this.fixPrecision(this.parseValue(v));
33830     },
33831
33832     parseValue : function(value)
33833     {
33834         if(this.thousandsDelimiter) {
33835             value += "";
33836             r = new RegExp(",", "g");
33837             value = value.replace(r, "");
33838         }
33839         
33840         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33841         return isNaN(value) ? '' : value;
33842     },
33843
33844     fixPrecision : function(value)
33845     {
33846         if(this.thousandsDelimiter) {
33847             value += "";
33848             r = new RegExp(",", "g");
33849             value = value.replace(r, "");
33850         }
33851         
33852         var nan = isNaN(value);
33853         
33854         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33855             return nan ? '' : value;
33856         }
33857         return parseFloat(value).toFixed(this.decimalPrecision);
33858     },
33859
33860     setValue : function(v)
33861     {
33862         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33863         
33864         this.value = v;
33865         
33866         if(this.rendered){
33867             
33868             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33869             
33870             this.inputEl().dom.value = (v == '') ? '' :
33871                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33872             
33873             if(!this.allowZero && v === '0') {
33874                 this.hiddenEl().dom.value = '';
33875                 this.inputEl().dom.value = '';
33876             }
33877             
33878             this.validate();
33879         }
33880     },
33881
33882     decimalPrecisionFcn : function(v)
33883     {
33884         return Math.floor(v);
33885     },
33886
33887     beforeBlur : function()
33888     {
33889         var v = this.parseValue(this.getRawValue());
33890         
33891         if(v || v === 0 || v === ''){
33892             this.setValue(v);
33893         }
33894     },
33895     
33896     hiddenEl : function()
33897     {
33898         return this.el.select('input.hidden-number-input',true).first();
33899     }
33900     
33901 });
33902
33903  
33904
33905 /*
33906 * Licence: LGPL
33907 */
33908
33909 /**
33910  * @class Roo.bootstrap.DocumentSlider
33911  * @extends Roo.bootstrap.Component
33912  * Bootstrap DocumentSlider class
33913  * 
33914  * @constructor
33915  * Create a new DocumentViewer
33916  * @param {Object} config The config object
33917  */
33918
33919 Roo.bootstrap.DocumentSlider = function(config){
33920     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33921     
33922     this.files = [];
33923     
33924     this.addEvents({
33925         /**
33926          * @event initial
33927          * Fire after initEvent
33928          * @param {Roo.bootstrap.DocumentSlider} this
33929          */
33930         "initial" : true,
33931         /**
33932          * @event update
33933          * Fire after update
33934          * @param {Roo.bootstrap.DocumentSlider} this
33935          */
33936         "update" : true,
33937         /**
33938          * @event click
33939          * Fire after click
33940          * @param {Roo.bootstrap.DocumentSlider} this
33941          */
33942         "click" : true
33943     });
33944 };
33945
33946 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33947     
33948     files : false,
33949     
33950     indicator : 0,
33951     
33952     getAutoCreate : function()
33953     {
33954         var cfg = {
33955             tag : 'div',
33956             cls : 'roo-document-slider',
33957             cn : [
33958                 {
33959                     tag : 'div',
33960                     cls : 'roo-document-slider-header',
33961                     cn : [
33962                         {
33963                             tag : 'div',
33964                             cls : 'roo-document-slider-header-title'
33965                         }
33966                     ]
33967                 },
33968                 {
33969                     tag : 'div',
33970                     cls : 'roo-document-slider-body',
33971                     cn : [
33972                         {
33973                             tag : 'div',
33974                             cls : 'roo-document-slider-prev',
33975                             cn : [
33976                                 {
33977                                     tag : 'i',
33978                                     cls : 'fa fa-chevron-left'
33979                                 }
33980                             ]
33981                         },
33982                         {
33983                             tag : 'div',
33984                             cls : 'roo-document-slider-thumb',
33985                             cn : [
33986                                 {
33987                                     tag : 'img',
33988                                     cls : 'roo-document-slider-image'
33989                                 }
33990                             ]
33991                         },
33992                         {
33993                             tag : 'div',
33994                             cls : 'roo-document-slider-next',
33995                             cn : [
33996                                 {
33997                                     tag : 'i',
33998                                     cls : 'fa fa-chevron-right'
33999                                 }
34000                             ]
34001                         }
34002                     ]
34003                 }
34004             ]
34005         };
34006         
34007         return cfg;
34008     },
34009     
34010     initEvents : function()
34011     {
34012         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34013         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34014         
34015         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34016         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34017         
34018         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34019         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34020         
34021         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34022         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34023         
34024         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34025         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34026         
34027         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34028         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34029         
34030         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34031         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34032         
34033         this.thumbEl.on('click', this.onClick, this);
34034         
34035         this.prevIndicator.on('click', this.prev, this);
34036         
34037         this.nextIndicator.on('click', this.next, this);
34038         
34039     },
34040     
34041     initial : function()
34042     {
34043         if(this.files.length){
34044             this.indicator = 1;
34045             this.update()
34046         }
34047         
34048         this.fireEvent('initial', this);
34049     },
34050     
34051     update : function()
34052     {
34053         this.imageEl.attr('src', this.files[this.indicator - 1]);
34054         
34055         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34056         
34057         this.prevIndicator.show();
34058         
34059         if(this.indicator == 1){
34060             this.prevIndicator.hide();
34061         }
34062         
34063         this.nextIndicator.show();
34064         
34065         if(this.indicator == this.files.length){
34066             this.nextIndicator.hide();
34067         }
34068         
34069         this.thumbEl.scrollTo('top');
34070         
34071         this.fireEvent('update', this);
34072     },
34073     
34074     onClick : function(e)
34075     {
34076         e.preventDefault();
34077         
34078         this.fireEvent('click', this);
34079     },
34080     
34081     prev : function(e)
34082     {
34083         e.preventDefault();
34084         
34085         this.indicator = Math.max(1, this.indicator - 1);
34086         
34087         this.update();
34088     },
34089     
34090     next : function(e)
34091     {
34092         e.preventDefault();
34093         
34094         this.indicator = Math.min(this.files.length, this.indicator + 1);
34095         
34096         this.update();
34097     }
34098 });
34099 /*
34100  * - LGPL
34101  *
34102  * RadioSet
34103  *
34104  *
34105  */
34106
34107 /**
34108  * @class Roo.bootstrap.RadioSet
34109  * @extends Roo.bootstrap.Input
34110  * Bootstrap RadioSet class
34111  * @cfg {String} indicatorpos (left|right) default left
34112  * @cfg {Boolean} inline (true|false) inline the element (default true)
34113  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34114  * @constructor
34115  * Create a new RadioSet
34116  * @param {Object} config The config object
34117  */
34118
34119 Roo.bootstrap.RadioSet = function(config){
34120     
34121     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34122     
34123     this.radioes = [];
34124     
34125     Roo.bootstrap.RadioSet.register(this);
34126     
34127     this.addEvents({
34128         /**
34129         * @event check
34130         * Fires when the element is checked or unchecked.
34131         * @param {Roo.bootstrap.RadioSet} this This radio
34132         * @param {Roo.bootstrap.Radio} item The checked item
34133         */
34134        check : true,
34135        /**
34136         * @event click
34137         * Fires when the element is click.
34138         * @param {Roo.bootstrap.RadioSet} this This radio set
34139         * @param {Roo.bootstrap.Radio} item The checked item
34140         * @param {Roo.EventObject} e The event object
34141         */
34142        click : true
34143     });
34144     
34145 };
34146
34147 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34148
34149     radioes : false,
34150     
34151     inline : true,
34152     
34153     weight : '',
34154     
34155     indicatorpos : 'left',
34156     
34157     getAutoCreate : function()
34158     {
34159         var label = {
34160             tag : 'label',
34161             cls : 'roo-radio-set-label',
34162             cn : [
34163                 {
34164                     tag : 'span',
34165                     html : this.fieldLabel
34166                 }
34167             ]
34168         };
34169         if (Roo.bootstrap.version == 3) {
34170             
34171             
34172             if(this.indicatorpos == 'left'){
34173                 label.cn.unshift({
34174                     tag : 'i',
34175                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34176                     tooltip : 'This field is required'
34177                 });
34178             } else {
34179                 label.cn.push({
34180                     tag : 'i',
34181                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34182                     tooltip : 'This field is required'
34183                 });
34184             }
34185         }
34186         var items = {
34187             tag : 'div',
34188             cls : 'roo-radio-set-items'
34189         };
34190         
34191         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34192         
34193         if (align === 'left' && this.fieldLabel.length) {
34194             
34195             items = {
34196                 cls : "roo-radio-set-right", 
34197                 cn: [
34198                     items
34199                 ]
34200             };
34201             
34202             if(this.labelWidth > 12){
34203                 label.style = "width: " + this.labelWidth + 'px';
34204             }
34205             
34206             if(this.labelWidth < 13 && this.labelmd == 0){
34207                 this.labelmd = this.labelWidth;
34208             }
34209             
34210             if(this.labellg > 0){
34211                 label.cls += ' col-lg-' + this.labellg;
34212                 items.cls += ' col-lg-' + (12 - this.labellg);
34213             }
34214             
34215             if(this.labelmd > 0){
34216                 label.cls += ' col-md-' + this.labelmd;
34217                 items.cls += ' col-md-' + (12 - this.labelmd);
34218             }
34219             
34220             if(this.labelsm > 0){
34221                 label.cls += ' col-sm-' + this.labelsm;
34222                 items.cls += ' col-sm-' + (12 - this.labelsm);
34223             }
34224             
34225             if(this.labelxs > 0){
34226                 label.cls += ' col-xs-' + this.labelxs;
34227                 items.cls += ' col-xs-' + (12 - this.labelxs);
34228             }
34229         }
34230         
34231         var cfg = {
34232             tag : 'div',
34233             cls : 'roo-radio-set',
34234             cn : [
34235                 {
34236                     tag : 'input',
34237                     cls : 'roo-radio-set-input',
34238                     type : 'hidden',
34239                     name : this.name,
34240                     value : this.value ? this.value :  ''
34241                 },
34242                 label,
34243                 items
34244             ]
34245         };
34246         
34247         if(this.weight.length){
34248             cfg.cls += ' roo-radio-' + this.weight;
34249         }
34250         
34251         if(this.inline) {
34252             cfg.cls += ' roo-radio-set-inline';
34253         }
34254         
34255         var settings=this;
34256         ['xs','sm','md','lg'].map(function(size){
34257             if (settings[size]) {
34258                 cfg.cls += ' col-' + size + '-' + settings[size];
34259             }
34260         });
34261         
34262         return cfg;
34263         
34264     },
34265
34266     initEvents : function()
34267     {
34268         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34269         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34270         
34271         if(!this.fieldLabel.length){
34272             this.labelEl.hide();
34273         }
34274         
34275         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34276         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34277         
34278         this.indicator = this.indicatorEl();
34279         
34280         if(this.indicator){
34281             this.indicator.addClass('invisible');
34282         }
34283         
34284         this.originalValue = this.getValue();
34285         
34286     },
34287     
34288     inputEl: function ()
34289     {
34290         return this.el.select('.roo-radio-set-input', true).first();
34291     },
34292     
34293     getChildContainer : function()
34294     {
34295         return this.itemsEl;
34296     },
34297     
34298     register : function(item)
34299     {
34300         this.radioes.push(item);
34301         
34302     },
34303     
34304     validate : function()
34305     {   
34306         if(this.getVisibilityEl().hasClass('hidden')){
34307             return true;
34308         }
34309         
34310         var valid = false;
34311         
34312         Roo.each(this.radioes, function(i){
34313             if(!i.checked){
34314                 return;
34315             }
34316             
34317             valid = true;
34318             return false;
34319         });
34320         
34321         if(this.allowBlank) {
34322             return true;
34323         }
34324         
34325         if(this.disabled || valid){
34326             this.markValid();
34327             return true;
34328         }
34329         
34330         this.markInvalid();
34331         return false;
34332         
34333     },
34334     
34335     markValid : function()
34336     {
34337         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34338             this.indicatorEl().removeClass('visible');
34339             this.indicatorEl().addClass('invisible');
34340         }
34341         
34342         
34343         if (Roo.bootstrap.version == 3) {
34344             this.el.removeClass([this.invalidClass, this.validClass]);
34345             this.el.addClass(this.validClass);
34346         } else {
34347             this.el.removeClass(['is-invalid','is-valid']);
34348             this.el.addClass(['is-valid']);
34349         }
34350         this.fireEvent('valid', this);
34351     },
34352     
34353     markInvalid : function(msg)
34354     {
34355         if(this.allowBlank || this.disabled){
34356             return;
34357         }
34358         
34359         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34360             this.indicatorEl().removeClass('invisible');
34361             this.indicatorEl().addClass('visible');
34362         }
34363         if (Roo.bootstrap.version == 3) {
34364             this.el.removeClass([this.invalidClass, this.validClass]);
34365             this.el.addClass(this.invalidClass);
34366         } else {
34367             this.el.removeClass(['is-invalid','is-valid']);
34368             this.el.addClass(['is-invalid']);
34369         }
34370         
34371         this.fireEvent('invalid', this, msg);
34372         
34373     },
34374     
34375     setValue : function(v, suppressEvent)
34376     {   
34377         if(this.value === v){
34378             return;
34379         }
34380         
34381         this.value = v;
34382         
34383         if(this.rendered){
34384             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34385         }
34386         
34387         Roo.each(this.radioes, function(i){
34388             i.checked = false;
34389             i.el.removeClass('checked');
34390         });
34391         
34392         Roo.each(this.radioes, function(i){
34393             
34394             if(i.value === v || i.value.toString() === v.toString()){
34395                 i.checked = true;
34396                 i.el.addClass('checked');
34397                 
34398                 if(suppressEvent !== true){
34399                     this.fireEvent('check', this, i);
34400                 }
34401                 
34402                 return false;
34403             }
34404             
34405         }, this);
34406         
34407         this.validate();
34408     },
34409     
34410     clearInvalid : function(){
34411         
34412         if(!this.el || this.preventMark){
34413             return;
34414         }
34415         
34416         this.el.removeClass([this.invalidClass]);
34417         
34418         this.fireEvent('valid', this);
34419     }
34420     
34421 });
34422
34423 Roo.apply(Roo.bootstrap.RadioSet, {
34424     
34425     groups: {},
34426     
34427     register : function(set)
34428     {
34429         this.groups[set.name] = set;
34430     },
34431     
34432     get: function(name) 
34433     {
34434         if (typeof(this.groups[name]) == 'undefined') {
34435             return false;
34436         }
34437         
34438         return this.groups[name] ;
34439     }
34440     
34441 });
34442 /*
34443  * Based on:
34444  * Ext JS Library 1.1.1
34445  * Copyright(c) 2006-2007, Ext JS, LLC.
34446  *
34447  * Originally Released Under LGPL - original licence link has changed is not relivant.
34448  *
34449  * Fork - LGPL
34450  * <script type="text/javascript">
34451  */
34452
34453
34454 /**
34455  * @class Roo.bootstrap.SplitBar
34456  * @extends Roo.util.Observable
34457  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34458  * <br><br>
34459  * Usage:
34460  * <pre><code>
34461 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34462                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34463 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34464 split.minSize = 100;
34465 split.maxSize = 600;
34466 split.animate = true;
34467 split.on('moved', splitterMoved);
34468 </code></pre>
34469  * @constructor
34470  * Create a new SplitBar
34471  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34472  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34473  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34474  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34475                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34476                         position of the SplitBar).
34477  */
34478 Roo.bootstrap.SplitBar = function(cfg){
34479     
34480     /** @private */
34481     
34482     //{
34483     //  dragElement : elm
34484     //  resizingElement: el,
34485         // optional..
34486     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34487     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34488         // existingProxy ???
34489     //}
34490     
34491     this.el = Roo.get(cfg.dragElement, true);
34492     this.el.dom.unselectable = "on";
34493     /** @private */
34494     this.resizingEl = Roo.get(cfg.resizingElement, true);
34495
34496     /**
34497      * @private
34498      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34499      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34500      * @type Number
34501      */
34502     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34503     
34504     /**
34505      * The minimum size of the resizing element. (Defaults to 0)
34506      * @type Number
34507      */
34508     this.minSize = 0;
34509     
34510     /**
34511      * The maximum size of the resizing element. (Defaults to 2000)
34512      * @type Number
34513      */
34514     this.maxSize = 2000;
34515     
34516     /**
34517      * Whether to animate the transition to the new size
34518      * @type Boolean
34519      */
34520     this.animate = false;
34521     
34522     /**
34523      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34524      * @type Boolean
34525      */
34526     this.useShim = false;
34527     
34528     /** @private */
34529     this.shim = null;
34530     
34531     if(!cfg.existingProxy){
34532         /** @private */
34533         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34534     }else{
34535         this.proxy = Roo.get(cfg.existingProxy).dom;
34536     }
34537     /** @private */
34538     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34539     
34540     /** @private */
34541     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34542     
34543     /** @private */
34544     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34545     
34546     /** @private */
34547     this.dragSpecs = {};
34548     
34549     /**
34550      * @private The adapter to use to positon and resize elements
34551      */
34552     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34553     this.adapter.init(this);
34554     
34555     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34556         /** @private */
34557         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34558         this.el.addClass("roo-splitbar-h");
34559     }else{
34560         /** @private */
34561         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34562         this.el.addClass("roo-splitbar-v");
34563     }
34564     
34565     this.addEvents({
34566         /**
34567          * @event resize
34568          * Fires when the splitter is moved (alias for {@link #event-moved})
34569          * @param {Roo.bootstrap.SplitBar} this
34570          * @param {Number} newSize the new width or height
34571          */
34572         "resize" : true,
34573         /**
34574          * @event moved
34575          * Fires when the splitter is moved
34576          * @param {Roo.bootstrap.SplitBar} this
34577          * @param {Number} newSize the new width or height
34578          */
34579         "moved" : true,
34580         /**
34581          * @event beforeresize
34582          * Fires before the splitter is dragged
34583          * @param {Roo.bootstrap.SplitBar} this
34584          */
34585         "beforeresize" : true,
34586
34587         "beforeapply" : true
34588     });
34589
34590     Roo.util.Observable.call(this);
34591 };
34592
34593 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34594     onStartProxyDrag : function(x, y){
34595         this.fireEvent("beforeresize", this);
34596         if(!this.overlay){
34597             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34598             o.unselectable();
34599             o.enableDisplayMode("block");
34600             // all splitbars share the same overlay
34601             Roo.bootstrap.SplitBar.prototype.overlay = o;
34602         }
34603         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34604         this.overlay.show();
34605         Roo.get(this.proxy).setDisplayed("block");
34606         var size = this.adapter.getElementSize(this);
34607         this.activeMinSize = this.getMinimumSize();;
34608         this.activeMaxSize = this.getMaximumSize();;
34609         var c1 = size - this.activeMinSize;
34610         var c2 = Math.max(this.activeMaxSize - size, 0);
34611         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34612             this.dd.resetConstraints();
34613             this.dd.setXConstraint(
34614                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34615                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34616             );
34617             this.dd.setYConstraint(0, 0);
34618         }else{
34619             this.dd.resetConstraints();
34620             this.dd.setXConstraint(0, 0);
34621             this.dd.setYConstraint(
34622                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34623                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34624             );
34625          }
34626         this.dragSpecs.startSize = size;
34627         this.dragSpecs.startPoint = [x, y];
34628         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34629     },
34630     
34631     /** 
34632      * @private Called after the drag operation by the DDProxy
34633      */
34634     onEndProxyDrag : function(e){
34635         Roo.get(this.proxy).setDisplayed(false);
34636         var endPoint = Roo.lib.Event.getXY(e);
34637         if(this.overlay){
34638             this.overlay.hide();
34639         }
34640         var newSize;
34641         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34642             newSize = this.dragSpecs.startSize + 
34643                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34644                     endPoint[0] - this.dragSpecs.startPoint[0] :
34645                     this.dragSpecs.startPoint[0] - endPoint[0]
34646                 );
34647         }else{
34648             newSize = this.dragSpecs.startSize + 
34649                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34650                     endPoint[1] - this.dragSpecs.startPoint[1] :
34651                     this.dragSpecs.startPoint[1] - endPoint[1]
34652                 );
34653         }
34654         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34655         if(newSize != this.dragSpecs.startSize){
34656             if(this.fireEvent('beforeapply', this, newSize) !== false){
34657                 this.adapter.setElementSize(this, newSize);
34658                 this.fireEvent("moved", this, newSize);
34659                 this.fireEvent("resize", this, newSize);
34660             }
34661         }
34662     },
34663     
34664     /**
34665      * Get the adapter this SplitBar uses
34666      * @return The adapter object
34667      */
34668     getAdapter : function(){
34669         return this.adapter;
34670     },
34671     
34672     /**
34673      * Set the adapter this SplitBar uses
34674      * @param {Object} adapter A SplitBar adapter object
34675      */
34676     setAdapter : function(adapter){
34677         this.adapter = adapter;
34678         this.adapter.init(this);
34679     },
34680     
34681     /**
34682      * Gets the minimum size for the resizing element
34683      * @return {Number} The minimum size
34684      */
34685     getMinimumSize : function(){
34686         return this.minSize;
34687     },
34688     
34689     /**
34690      * Sets the minimum size for the resizing element
34691      * @param {Number} minSize The minimum size
34692      */
34693     setMinimumSize : function(minSize){
34694         this.minSize = minSize;
34695     },
34696     
34697     /**
34698      * Gets the maximum size for the resizing element
34699      * @return {Number} The maximum size
34700      */
34701     getMaximumSize : function(){
34702         return this.maxSize;
34703     },
34704     
34705     /**
34706      * Sets the maximum size for the resizing element
34707      * @param {Number} maxSize The maximum size
34708      */
34709     setMaximumSize : function(maxSize){
34710         this.maxSize = maxSize;
34711     },
34712     
34713     /**
34714      * Sets the initialize size for the resizing element
34715      * @param {Number} size The initial size
34716      */
34717     setCurrentSize : function(size){
34718         var oldAnimate = this.animate;
34719         this.animate = false;
34720         this.adapter.setElementSize(this, size);
34721         this.animate = oldAnimate;
34722     },
34723     
34724     /**
34725      * Destroy this splitbar. 
34726      * @param {Boolean} removeEl True to remove the element
34727      */
34728     destroy : function(removeEl){
34729         if(this.shim){
34730             this.shim.remove();
34731         }
34732         this.dd.unreg();
34733         this.proxy.parentNode.removeChild(this.proxy);
34734         if(removeEl){
34735             this.el.remove();
34736         }
34737     }
34738 });
34739
34740 /**
34741  * @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.
34742  */
34743 Roo.bootstrap.SplitBar.createProxy = function(dir){
34744     var proxy = new Roo.Element(document.createElement("div"));
34745     proxy.unselectable();
34746     var cls = 'roo-splitbar-proxy';
34747     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34748     document.body.appendChild(proxy.dom);
34749     return proxy.dom;
34750 };
34751
34752 /** 
34753  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34754  * Default Adapter. It assumes the splitter and resizing element are not positioned
34755  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34756  */
34757 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34758 };
34759
34760 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34761     // do nothing for now
34762     init : function(s){
34763     
34764     },
34765     /**
34766      * Called before drag operations to get the current size of the resizing element. 
34767      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34768      */
34769      getElementSize : function(s){
34770         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34771             return s.resizingEl.getWidth();
34772         }else{
34773             return s.resizingEl.getHeight();
34774         }
34775     },
34776     
34777     /**
34778      * Called after drag operations to set the size of the resizing element.
34779      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34780      * @param {Number} newSize The new size to set
34781      * @param {Function} onComplete A function to be invoked when resizing is complete
34782      */
34783     setElementSize : function(s, newSize, onComplete){
34784         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34785             if(!s.animate){
34786                 s.resizingEl.setWidth(newSize);
34787                 if(onComplete){
34788                     onComplete(s, newSize);
34789                 }
34790             }else{
34791                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34792             }
34793         }else{
34794             
34795             if(!s.animate){
34796                 s.resizingEl.setHeight(newSize);
34797                 if(onComplete){
34798                     onComplete(s, newSize);
34799                 }
34800             }else{
34801                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34802             }
34803         }
34804     }
34805 };
34806
34807 /** 
34808  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34809  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34810  * Adapter that  moves the splitter element to align with the resized sizing element. 
34811  * Used with an absolute positioned SplitBar.
34812  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34813  * document.body, make sure you assign an id to the body element.
34814  */
34815 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34816     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34817     this.container = Roo.get(container);
34818 };
34819
34820 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34821     init : function(s){
34822         this.basic.init(s);
34823     },
34824     
34825     getElementSize : function(s){
34826         return this.basic.getElementSize(s);
34827     },
34828     
34829     setElementSize : function(s, newSize, onComplete){
34830         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34831     },
34832     
34833     moveSplitter : function(s){
34834         var yes = Roo.bootstrap.SplitBar;
34835         switch(s.placement){
34836             case yes.LEFT:
34837                 s.el.setX(s.resizingEl.getRight());
34838                 break;
34839             case yes.RIGHT:
34840                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34841                 break;
34842             case yes.TOP:
34843                 s.el.setY(s.resizingEl.getBottom());
34844                 break;
34845             case yes.BOTTOM:
34846                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34847                 break;
34848         }
34849     }
34850 };
34851
34852 /**
34853  * Orientation constant - Create a vertical SplitBar
34854  * @static
34855  * @type Number
34856  */
34857 Roo.bootstrap.SplitBar.VERTICAL = 1;
34858
34859 /**
34860  * Orientation constant - Create a horizontal SplitBar
34861  * @static
34862  * @type Number
34863  */
34864 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34865
34866 /**
34867  * Placement constant - The resizing element is to the left of the splitter element
34868  * @static
34869  * @type Number
34870  */
34871 Roo.bootstrap.SplitBar.LEFT = 1;
34872
34873 /**
34874  * Placement constant - The resizing element is to the right of the splitter element
34875  * @static
34876  * @type Number
34877  */
34878 Roo.bootstrap.SplitBar.RIGHT = 2;
34879
34880 /**
34881  * Placement constant - The resizing element is positioned above the splitter element
34882  * @static
34883  * @type Number
34884  */
34885 Roo.bootstrap.SplitBar.TOP = 3;
34886
34887 /**
34888  * Placement constant - The resizing element is positioned under splitter element
34889  * @static
34890  * @type Number
34891  */
34892 Roo.bootstrap.SplitBar.BOTTOM = 4;
34893 Roo.namespace("Roo.bootstrap.layout");/*
34894  * Based on:
34895  * Ext JS Library 1.1.1
34896  * Copyright(c) 2006-2007, Ext JS, LLC.
34897  *
34898  * Originally Released Under LGPL - original licence link has changed is not relivant.
34899  *
34900  * Fork - LGPL
34901  * <script type="text/javascript">
34902  */
34903
34904 /**
34905  * @class Roo.bootstrap.layout.Manager
34906  * @extends Roo.bootstrap.Component
34907  * Base class for layout managers.
34908  */
34909 Roo.bootstrap.layout.Manager = function(config)
34910 {
34911     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34912
34913
34914
34915
34916
34917     /** false to disable window resize monitoring @type Boolean */
34918     this.monitorWindowResize = true;
34919     this.regions = {};
34920     this.addEvents({
34921         /**
34922          * @event layout
34923          * Fires when a layout is performed.
34924          * @param {Roo.LayoutManager} this
34925          */
34926         "layout" : true,
34927         /**
34928          * @event regionresized
34929          * Fires when the user resizes a region.
34930          * @param {Roo.LayoutRegion} region The resized region
34931          * @param {Number} newSize The new size (width for east/west, height for north/south)
34932          */
34933         "regionresized" : true,
34934         /**
34935          * @event regioncollapsed
34936          * Fires when a region is collapsed.
34937          * @param {Roo.LayoutRegion} region The collapsed region
34938          */
34939         "regioncollapsed" : true,
34940         /**
34941          * @event regionexpanded
34942          * Fires when a region is expanded.
34943          * @param {Roo.LayoutRegion} region The expanded region
34944          */
34945         "regionexpanded" : true
34946     });
34947     this.updating = false;
34948
34949     if (config.el) {
34950         this.el = Roo.get(config.el);
34951         this.initEvents();
34952     }
34953
34954 };
34955
34956 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34957
34958
34959     regions : null,
34960
34961     monitorWindowResize : true,
34962
34963
34964     updating : false,
34965
34966
34967     onRender : function(ct, position)
34968     {
34969         if(!this.el){
34970             this.el = Roo.get(ct);
34971             this.initEvents();
34972         }
34973         //this.fireEvent('render',this);
34974     },
34975
34976
34977     initEvents: function()
34978     {
34979
34980
34981         // ie scrollbar fix
34982         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34983             document.body.scroll = "no";
34984         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34985             this.el.position('relative');
34986         }
34987         this.id = this.el.id;
34988         this.el.addClass("roo-layout-container");
34989         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34990         if(this.el.dom != document.body ) {
34991             this.el.on('resize', this.layout,this);
34992             this.el.on('show', this.layout,this);
34993         }
34994
34995     },
34996
34997     /**
34998      * Returns true if this layout is currently being updated
34999      * @return {Boolean}
35000      */
35001     isUpdating : function(){
35002         return this.updating;
35003     },
35004
35005     /**
35006      * Suspend the LayoutManager from doing auto-layouts while
35007      * making multiple add or remove calls
35008      */
35009     beginUpdate : function(){
35010         this.updating = true;
35011     },
35012
35013     /**
35014      * Restore auto-layouts and optionally disable the manager from performing a layout
35015      * @param {Boolean} noLayout true to disable a layout update
35016      */
35017     endUpdate : function(noLayout){
35018         this.updating = false;
35019         if(!noLayout){
35020             this.layout();
35021         }
35022     },
35023
35024     layout: function(){
35025         // abstract...
35026     },
35027
35028     onRegionResized : function(region, newSize){
35029         this.fireEvent("regionresized", region, newSize);
35030         this.layout();
35031     },
35032
35033     onRegionCollapsed : function(region){
35034         this.fireEvent("regioncollapsed", region);
35035     },
35036
35037     onRegionExpanded : function(region){
35038         this.fireEvent("regionexpanded", region);
35039     },
35040
35041     /**
35042      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35043      * performs box-model adjustments.
35044      * @return {Object} The size as an object {width: (the width), height: (the height)}
35045      */
35046     getViewSize : function()
35047     {
35048         var size;
35049         if(this.el.dom != document.body){
35050             size = this.el.getSize();
35051         }else{
35052             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35053         }
35054         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35055         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35056         return size;
35057     },
35058
35059     /**
35060      * Returns the Element this layout is bound to.
35061      * @return {Roo.Element}
35062      */
35063     getEl : function(){
35064         return this.el;
35065     },
35066
35067     /**
35068      * Returns the specified region.
35069      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35070      * @return {Roo.LayoutRegion}
35071      */
35072     getRegion : function(target){
35073         return this.regions[target.toLowerCase()];
35074     },
35075
35076     onWindowResize : function(){
35077         if(this.monitorWindowResize){
35078             this.layout();
35079         }
35080     }
35081 });
35082 /*
35083  * Based on:
35084  * Ext JS Library 1.1.1
35085  * Copyright(c) 2006-2007, Ext JS, LLC.
35086  *
35087  * Originally Released Under LGPL - original licence link has changed is not relivant.
35088  *
35089  * Fork - LGPL
35090  * <script type="text/javascript">
35091  */
35092 /**
35093  * @class Roo.bootstrap.layout.Border
35094  * @extends Roo.bootstrap.layout.Manager
35095  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35096  * please see: examples/bootstrap/nested.html<br><br>
35097  
35098 <b>The container the layout is rendered into can be either the body element or any other element.
35099 If it is not the body element, the container needs to either be an absolute positioned element,
35100 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35101 the container size if it is not the body element.</b>
35102
35103 * @constructor
35104 * Create a new Border
35105 * @param {Object} config Configuration options
35106  */
35107 Roo.bootstrap.layout.Border = function(config){
35108     config = config || {};
35109     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35110     
35111     
35112     
35113     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35114         if(config[region]){
35115             config[region].region = region;
35116             this.addRegion(config[region]);
35117         }
35118     },this);
35119     
35120 };
35121
35122 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35123
35124 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35125     
35126     parent : false, // this might point to a 'nest' or a ???
35127     
35128     /**
35129      * Creates and adds a new region if it doesn't already exist.
35130      * @param {String} target The target region key (north, south, east, west or center).
35131      * @param {Object} config The regions config object
35132      * @return {BorderLayoutRegion} The new region
35133      */
35134     addRegion : function(config)
35135     {
35136         if(!this.regions[config.region]){
35137             var r = this.factory(config);
35138             this.bindRegion(r);
35139         }
35140         return this.regions[config.region];
35141     },
35142
35143     // private (kinda)
35144     bindRegion : function(r){
35145         this.regions[r.config.region] = r;
35146         
35147         r.on("visibilitychange",    this.layout, this);
35148         r.on("paneladded",          this.layout, this);
35149         r.on("panelremoved",        this.layout, this);
35150         r.on("invalidated",         this.layout, this);
35151         r.on("resized",             this.onRegionResized, this);
35152         r.on("collapsed",           this.onRegionCollapsed, this);
35153         r.on("expanded",            this.onRegionExpanded, this);
35154     },
35155
35156     /**
35157      * Performs a layout update.
35158      */
35159     layout : function()
35160     {
35161         if(this.updating) {
35162             return;
35163         }
35164         
35165         // render all the rebions if they have not been done alreayd?
35166         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35167             if(this.regions[region] && !this.regions[region].bodyEl){
35168                 this.regions[region].onRender(this.el)
35169             }
35170         },this);
35171         
35172         var size = this.getViewSize();
35173         var w = size.width;
35174         var h = size.height;
35175         var centerW = w;
35176         var centerH = h;
35177         var centerY = 0;
35178         var centerX = 0;
35179         //var x = 0, y = 0;
35180
35181         var rs = this.regions;
35182         var north = rs["north"];
35183         var south = rs["south"]; 
35184         var west = rs["west"];
35185         var east = rs["east"];
35186         var center = rs["center"];
35187         //if(this.hideOnLayout){ // not supported anymore
35188             //c.el.setStyle("display", "none");
35189         //}
35190         if(north && north.isVisible()){
35191             var b = north.getBox();
35192             var m = north.getMargins();
35193             b.width = w - (m.left+m.right);
35194             b.x = m.left;
35195             b.y = m.top;
35196             centerY = b.height + b.y + m.bottom;
35197             centerH -= centerY;
35198             north.updateBox(this.safeBox(b));
35199         }
35200         if(south && south.isVisible()){
35201             var b = south.getBox();
35202             var m = south.getMargins();
35203             b.width = w - (m.left+m.right);
35204             b.x = m.left;
35205             var totalHeight = (b.height + m.top + m.bottom);
35206             b.y = h - totalHeight + m.top;
35207             centerH -= totalHeight;
35208             south.updateBox(this.safeBox(b));
35209         }
35210         if(west && west.isVisible()){
35211             var b = west.getBox();
35212             var m = west.getMargins();
35213             b.height = centerH - (m.top+m.bottom);
35214             b.x = m.left;
35215             b.y = centerY + m.top;
35216             var totalWidth = (b.width + m.left + m.right);
35217             centerX += totalWidth;
35218             centerW -= totalWidth;
35219             west.updateBox(this.safeBox(b));
35220         }
35221         if(east && east.isVisible()){
35222             var b = east.getBox();
35223             var m = east.getMargins();
35224             b.height = centerH - (m.top+m.bottom);
35225             var totalWidth = (b.width + m.left + m.right);
35226             b.x = w - totalWidth + m.left;
35227             b.y = centerY + m.top;
35228             centerW -= totalWidth;
35229             east.updateBox(this.safeBox(b));
35230         }
35231         if(center){
35232             var m = center.getMargins();
35233             var centerBox = {
35234                 x: centerX + m.left,
35235                 y: centerY + m.top,
35236                 width: centerW - (m.left+m.right),
35237                 height: centerH - (m.top+m.bottom)
35238             };
35239             //if(this.hideOnLayout){
35240                 //center.el.setStyle("display", "block");
35241             //}
35242             center.updateBox(this.safeBox(centerBox));
35243         }
35244         this.el.repaint();
35245         this.fireEvent("layout", this);
35246     },
35247
35248     // private
35249     safeBox : function(box){
35250         box.width = Math.max(0, box.width);
35251         box.height = Math.max(0, box.height);
35252         return box;
35253     },
35254
35255     /**
35256      * Adds a ContentPanel (or subclass) to this layout.
35257      * @param {String} target The target region key (north, south, east, west or center).
35258      * @param {Roo.ContentPanel} panel The panel to add
35259      * @return {Roo.ContentPanel} The added panel
35260      */
35261     add : function(target, panel){
35262          
35263         target = target.toLowerCase();
35264         return this.regions[target].add(panel);
35265     },
35266
35267     /**
35268      * Remove a ContentPanel (or subclass) to this layout.
35269      * @param {String} target The target region key (north, south, east, west or center).
35270      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35271      * @return {Roo.ContentPanel} The removed panel
35272      */
35273     remove : function(target, panel){
35274         target = target.toLowerCase();
35275         return this.regions[target].remove(panel);
35276     },
35277
35278     /**
35279      * Searches all regions for a panel with the specified id
35280      * @param {String} panelId
35281      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35282      */
35283     findPanel : function(panelId){
35284         var rs = this.regions;
35285         for(var target in rs){
35286             if(typeof rs[target] != "function"){
35287                 var p = rs[target].getPanel(panelId);
35288                 if(p){
35289                     return p;
35290                 }
35291             }
35292         }
35293         return null;
35294     },
35295
35296     /**
35297      * Searches all regions for a panel with the specified id and activates (shows) it.
35298      * @param {String/ContentPanel} panelId The panels id or the panel itself
35299      * @return {Roo.ContentPanel} The shown panel or null
35300      */
35301     showPanel : function(panelId) {
35302       var rs = this.regions;
35303       for(var target in rs){
35304          var r = rs[target];
35305          if(typeof r != "function"){
35306             if(r.hasPanel(panelId)){
35307                return r.showPanel(panelId);
35308             }
35309          }
35310       }
35311       return null;
35312    },
35313
35314    /**
35315      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35316      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35317      */
35318    /*
35319     restoreState : function(provider){
35320         if(!provider){
35321             provider = Roo.state.Manager;
35322         }
35323         var sm = new Roo.LayoutStateManager();
35324         sm.init(this, provider);
35325     },
35326 */
35327  
35328  
35329     /**
35330      * Adds a xtype elements to the layout.
35331      * <pre><code>
35332
35333 layout.addxtype({
35334        xtype : 'ContentPanel',
35335        region: 'west',
35336        items: [ .... ]
35337    }
35338 );
35339
35340 layout.addxtype({
35341         xtype : 'NestedLayoutPanel',
35342         region: 'west',
35343         layout: {
35344            center: { },
35345            west: { }   
35346         },
35347         items : [ ... list of content panels or nested layout panels.. ]
35348    }
35349 );
35350 </code></pre>
35351      * @param {Object} cfg Xtype definition of item to add.
35352      */
35353     addxtype : function(cfg)
35354     {
35355         // basically accepts a pannel...
35356         // can accept a layout region..!?!?
35357         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35358         
35359         
35360         // theory?  children can only be panels??
35361         
35362         //if (!cfg.xtype.match(/Panel$/)) {
35363         //    return false;
35364         //}
35365         var ret = false;
35366         
35367         if (typeof(cfg.region) == 'undefined') {
35368             Roo.log("Failed to add Panel, region was not set");
35369             Roo.log(cfg);
35370             return false;
35371         }
35372         var region = cfg.region;
35373         delete cfg.region;
35374         
35375           
35376         var xitems = [];
35377         if (cfg.items) {
35378             xitems = cfg.items;
35379             delete cfg.items;
35380         }
35381         var nb = false;
35382         
35383         if ( region == 'center') {
35384             Roo.log("Center: " + cfg.title);
35385         }
35386         
35387         
35388         switch(cfg.xtype) 
35389         {
35390             case 'Content':  // ContentPanel (el, cfg)
35391             case 'Scroll':  // ContentPanel (el, cfg)
35392             case 'View': 
35393                 cfg.autoCreate = cfg.autoCreate || true;
35394                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35395                 //} else {
35396                 //    var el = this.el.createChild();
35397                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35398                 //}
35399                 
35400                 this.add(region, ret);
35401                 break;
35402             
35403             /*
35404             case 'TreePanel': // our new panel!
35405                 cfg.el = this.el.createChild();
35406                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35407                 this.add(region, ret);
35408                 break;
35409             */
35410             
35411             case 'Nest': 
35412                 // create a new Layout (which is  a Border Layout...
35413                 
35414                 var clayout = cfg.layout;
35415                 clayout.el  = this.el.createChild();
35416                 clayout.items   = clayout.items  || [];
35417                 
35418                 delete cfg.layout;
35419                 
35420                 // replace this exitems with the clayout ones..
35421                 xitems = clayout.items;
35422                  
35423                 // force background off if it's in center...
35424                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35425                     cfg.background = false;
35426                 }
35427                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35428                 
35429                 
35430                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35431                 //console.log('adding nested layout panel '  + cfg.toSource());
35432                 this.add(region, ret);
35433                 nb = {}; /// find first...
35434                 break;
35435             
35436             case 'Grid':
35437                 
35438                 // needs grid and region
35439                 
35440                 //var el = this.getRegion(region).el.createChild();
35441                 /*
35442                  *var el = this.el.createChild();
35443                 // create the grid first...
35444                 cfg.grid.container = el;
35445                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35446                 */
35447                 
35448                 if (region == 'center' && this.active ) {
35449                     cfg.background = false;
35450                 }
35451                 
35452                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35453                 
35454                 this.add(region, ret);
35455                 /*
35456                 if (cfg.background) {
35457                     // render grid on panel activation (if panel background)
35458                     ret.on('activate', function(gp) {
35459                         if (!gp.grid.rendered) {
35460                     //        gp.grid.render(el);
35461                         }
35462                     });
35463                 } else {
35464                   //  cfg.grid.render(el);
35465                 }
35466                 */
35467                 break;
35468            
35469            
35470             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35471                 // it was the old xcomponent building that caused this before.
35472                 // espeically if border is the top element in the tree.
35473                 ret = this;
35474                 break; 
35475                 
35476                     
35477                 
35478                 
35479                 
35480             default:
35481                 /*
35482                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35483                     
35484                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35485                     this.add(region, ret);
35486                 } else {
35487                 */
35488                     Roo.log(cfg);
35489                     throw "Can not add '" + cfg.xtype + "' to Border";
35490                     return null;
35491              
35492                                 
35493              
35494         }
35495         this.beginUpdate();
35496         // add children..
35497         var region = '';
35498         var abn = {};
35499         Roo.each(xitems, function(i)  {
35500             region = nb && i.region ? i.region : false;
35501             
35502             var add = ret.addxtype(i);
35503            
35504             if (region) {
35505                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35506                 if (!i.background) {
35507                     abn[region] = nb[region] ;
35508                 }
35509             }
35510             
35511         });
35512         this.endUpdate();
35513
35514         // make the last non-background panel active..
35515         //if (nb) { Roo.log(abn); }
35516         if (nb) {
35517             
35518             for(var r in abn) {
35519                 region = this.getRegion(r);
35520                 if (region) {
35521                     // tried using nb[r], but it does not work..
35522                      
35523                     region.showPanel(abn[r]);
35524                    
35525                 }
35526             }
35527         }
35528         return ret;
35529         
35530     },
35531     
35532     
35533 // private
35534     factory : function(cfg)
35535     {
35536         
35537         var validRegions = Roo.bootstrap.layout.Border.regions;
35538
35539         var target = cfg.region;
35540         cfg.mgr = this;
35541         
35542         var r = Roo.bootstrap.layout;
35543         Roo.log(target);
35544         switch(target){
35545             case "north":
35546                 return new r.North(cfg);
35547             case "south":
35548                 return new r.South(cfg);
35549             case "east":
35550                 return new r.East(cfg);
35551             case "west":
35552                 return new r.West(cfg);
35553             case "center":
35554                 return new r.Center(cfg);
35555         }
35556         throw 'Layout region "'+target+'" not supported.';
35557     }
35558     
35559     
35560 });
35561  /*
35562  * Based on:
35563  * Ext JS Library 1.1.1
35564  * Copyright(c) 2006-2007, Ext JS, LLC.
35565  *
35566  * Originally Released Under LGPL - original licence link has changed is not relivant.
35567  *
35568  * Fork - LGPL
35569  * <script type="text/javascript">
35570  */
35571  
35572 /**
35573  * @class Roo.bootstrap.layout.Basic
35574  * @extends Roo.util.Observable
35575  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35576  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35577  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35578  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35579  * @cfg {string}   region  the region that it inhabits..
35580  * @cfg {bool}   skipConfig skip config?
35581  * 
35582
35583  */
35584 Roo.bootstrap.layout.Basic = function(config){
35585     
35586     this.mgr = config.mgr;
35587     
35588     this.position = config.region;
35589     
35590     var skipConfig = config.skipConfig;
35591     
35592     this.events = {
35593         /**
35594          * @scope Roo.BasicLayoutRegion
35595          */
35596         
35597         /**
35598          * @event beforeremove
35599          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35600          * @param {Roo.LayoutRegion} this
35601          * @param {Roo.ContentPanel} panel The panel
35602          * @param {Object} e The cancel event object
35603          */
35604         "beforeremove" : true,
35605         /**
35606          * @event invalidated
35607          * Fires when the layout for this region is changed.
35608          * @param {Roo.LayoutRegion} this
35609          */
35610         "invalidated" : true,
35611         /**
35612          * @event visibilitychange
35613          * Fires when this region is shown or hidden 
35614          * @param {Roo.LayoutRegion} this
35615          * @param {Boolean} visibility true or false
35616          */
35617         "visibilitychange" : true,
35618         /**
35619          * @event paneladded
35620          * Fires when a panel is added. 
35621          * @param {Roo.LayoutRegion} this
35622          * @param {Roo.ContentPanel} panel The panel
35623          */
35624         "paneladded" : true,
35625         /**
35626          * @event panelremoved
35627          * Fires when a panel is removed. 
35628          * @param {Roo.LayoutRegion} this
35629          * @param {Roo.ContentPanel} panel The panel
35630          */
35631         "panelremoved" : true,
35632         /**
35633          * @event beforecollapse
35634          * Fires when this region before collapse.
35635          * @param {Roo.LayoutRegion} this
35636          */
35637         "beforecollapse" : true,
35638         /**
35639          * @event collapsed
35640          * Fires when this region is collapsed.
35641          * @param {Roo.LayoutRegion} this
35642          */
35643         "collapsed" : true,
35644         /**
35645          * @event expanded
35646          * Fires when this region is expanded.
35647          * @param {Roo.LayoutRegion} this
35648          */
35649         "expanded" : true,
35650         /**
35651          * @event slideshow
35652          * Fires when this region is slid into view.
35653          * @param {Roo.LayoutRegion} this
35654          */
35655         "slideshow" : true,
35656         /**
35657          * @event slidehide
35658          * Fires when this region slides out of view. 
35659          * @param {Roo.LayoutRegion} this
35660          */
35661         "slidehide" : true,
35662         /**
35663          * @event panelactivated
35664          * Fires when a panel is activated. 
35665          * @param {Roo.LayoutRegion} this
35666          * @param {Roo.ContentPanel} panel The activated panel
35667          */
35668         "panelactivated" : true,
35669         /**
35670          * @event resized
35671          * Fires when the user resizes this region. 
35672          * @param {Roo.LayoutRegion} this
35673          * @param {Number} newSize The new size (width for east/west, height for north/south)
35674          */
35675         "resized" : true
35676     };
35677     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35678     this.panels = new Roo.util.MixedCollection();
35679     this.panels.getKey = this.getPanelId.createDelegate(this);
35680     this.box = null;
35681     this.activePanel = null;
35682     // ensure listeners are added...
35683     
35684     if (config.listeners || config.events) {
35685         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35686             listeners : config.listeners || {},
35687             events : config.events || {}
35688         });
35689     }
35690     
35691     if(skipConfig !== true){
35692         this.applyConfig(config);
35693     }
35694 };
35695
35696 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35697 {
35698     getPanelId : function(p){
35699         return p.getId();
35700     },
35701     
35702     applyConfig : function(config){
35703         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35704         this.config = config;
35705         
35706     },
35707     
35708     /**
35709      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35710      * the width, for horizontal (north, south) the height.
35711      * @param {Number} newSize The new width or height
35712      */
35713     resizeTo : function(newSize){
35714         var el = this.el ? this.el :
35715                  (this.activePanel ? this.activePanel.getEl() : null);
35716         if(el){
35717             switch(this.position){
35718                 case "east":
35719                 case "west":
35720                     el.setWidth(newSize);
35721                     this.fireEvent("resized", this, newSize);
35722                 break;
35723                 case "north":
35724                 case "south":
35725                     el.setHeight(newSize);
35726                     this.fireEvent("resized", this, newSize);
35727                 break;                
35728             }
35729         }
35730     },
35731     
35732     getBox : function(){
35733         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35734     },
35735     
35736     getMargins : function(){
35737         return this.margins;
35738     },
35739     
35740     updateBox : function(box){
35741         this.box = box;
35742         var el = this.activePanel.getEl();
35743         el.dom.style.left = box.x + "px";
35744         el.dom.style.top = box.y + "px";
35745         this.activePanel.setSize(box.width, box.height);
35746     },
35747     
35748     /**
35749      * Returns the container element for this region.
35750      * @return {Roo.Element}
35751      */
35752     getEl : function(){
35753         return this.activePanel;
35754     },
35755     
35756     /**
35757      * Returns true if this region is currently visible.
35758      * @return {Boolean}
35759      */
35760     isVisible : function(){
35761         return this.activePanel ? true : false;
35762     },
35763     
35764     setActivePanel : function(panel){
35765         panel = this.getPanel(panel);
35766         if(this.activePanel && this.activePanel != panel){
35767             this.activePanel.setActiveState(false);
35768             this.activePanel.getEl().setLeftTop(-10000,-10000);
35769         }
35770         this.activePanel = panel;
35771         panel.setActiveState(true);
35772         if(this.box){
35773             panel.setSize(this.box.width, this.box.height);
35774         }
35775         this.fireEvent("panelactivated", this, panel);
35776         this.fireEvent("invalidated");
35777     },
35778     
35779     /**
35780      * Show the specified panel.
35781      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35782      * @return {Roo.ContentPanel} The shown panel or null
35783      */
35784     showPanel : function(panel){
35785         panel = this.getPanel(panel);
35786         if(panel){
35787             this.setActivePanel(panel);
35788         }
35789         return panel;
35790     },
35791     
35792     /**
35793      * Get the active panel for this region.
35794      * @return {Roo.ContentPanel} The active panel or null
35795      */
35796     getActivePanel : function(){
35797         return this.activePanel;
35798     },
35799     
35800     /**
35801      * Add the passed ContentPanel(s)
35802      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35803      * @return {Roo.ContentPanel} The panel added (if only one was added)
35804      */
35805     add : function(panel){
35806         if(arguments.length > 1){
35807             for(var i = 0, len = arguments.length; i < len; i++) {
35808                 this.add(arguments[i]);
35809             }
35810             return null;
35811         }
35812         if(this.hasPanel(panel)){
35813             this.showPanel(panel);
35814             return panel;
35815         }
35816         var el = panel.getEl();
35817         if(el.dom.parentNode != this.mgr.el.dom){
35818             this.mgr.el.dom.appendChild(el.dom);
35819         }
35820         if(panel.setRegion){
35821             panel.setRegion(this);
35822         }
35823         this.panels.add(panel);
35824         el.setStyle("position", "absolute");
35825         if(!panel.background){
35826             this.setActivePanel(panel);
35827             if(this.config.initialSize && this.panels.getCount()==1){
35828                 this.resizeTo(this.config.initialSize);
35829             }
35830         }
35831         this.fireEvent("paneladded", this, panel);
35832         return panel;
35833     },
35834     
35835     /**
35836      * Returns true if the panel is in this region.
35837      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35838      * @return {Boolean}
35839      */
35840     hasPanel : function(panel){
35841         if(typeof panel == "object"){ // must be panel obj
35842             panel = panel.getId();
35843         }
35844         return this.getPanel(panel) ? true : false;
35845     },
35846     
35847     /**
35848      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35849      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35850      * @param {Boolean} preservePanel Overrides the config preservePanel option
35851      * @return {Roo.ContentPanel} The panel that was removed
35852      */
35853     remove : function(panel, preservePanel){
35854         panel = this.getPanel(panel);
35855         if(!panel){
35856             return null;
35857         }
35858         var e = {};
35859         this.fireEvent("beforeremove", this, panel, e);
35860         if(e.cancel === true){
35861             return null;
35862         }
35863         var panelId = panel.getId();
35864         this.panels.removeKey(panelId);
35865         return panel;
35866     },
35867     
35868     /**
35869      * Returns the panel specified or null if it's not in this region.
35870      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35871      * @return {Roo.ContentPanel}
35872      */
35873     getPanel : function(id){
35874         if(typeof id == "object"){ // must be panel obj
35875             return id;
35876         }
35877         return this.panels.get(id);
35878     },
35879     
35880     /**
35881      * Returns this regions position (north/south/east/west/center).
35882      * @return {String} 
35883      */
35884     getPosition: function(){
35885         return this.position;    
35886     }
35887 });/*
35888  * Based on:
35889  * Ext JS Library 1.1.1
35890  * Copyright(c) 2006-2007, Ext JS, LLC.
35891  *
35892  * Originally Released Under LGPL - original licence link has changed is not relivant.
35893  *
35894  * Fork - LGPL
35895  * <script type="text/javascript">
35896  */
35897  
35898 /**
35899  * @class Roo.bootstrap.layout.Region
35900  * @extends Roo.bootstrap.layout.Basic
35901  * This class represents a region in a layout manager.
35902  
35903  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35904  * @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})
35905  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35906  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35907  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35908  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35909  * @cfg {String}    title           The title for the region (overrides panel titles)
35910  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35911  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35912  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35913  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35914  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35915  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35916  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35917  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35918  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35919  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35920
35921  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35922  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35923  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35924  * @cfg {Number}    width           For East/West panels
35925  * @cfg {Number}    height          For North/South panels
35926  * @cfg {Boolean}   split           To show the splitter
35927  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35928  * 
35929  * @cfg {string}   cls             Extra CSS classes to add to region
35930  * 
35931  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35932  * @cfg {string}   region  the region that it inhabits..
35933  *
35934
35935  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35936  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35937
35938  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35939  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35940  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35941  */
35942 Roo.bootstrap.layout.Region = function(config)
35943 {
35944     this.applyConfig(config);
35945
35946     var mgr = config.mgr;
35947     var pos = config.region;
35948     config.skipConfig = true;
35949     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35950     
35951     if (mgr.el) {
35952         this.onRender(mgr.el);   
35953     }
35954      
35955     this.visible = true;
35956     this.collapsed = false;
35957     this.unrendered_panels = [];
35958 };
35959
35960 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35961
35962     position: '', // set by wrapper (eg. north/south etc..)
35963     unrendered_panels : null,  // unrendered panels.
35964     
35965     tabPosition : false,
35966     
35967     mgr: false, // points to 'Border'
35968     
35969     
35970     createBody : function(){
35971         /** This region's body element 
35972         * @type Roo.Element */
35973         this.bodyEl = this.el.createChild({
35974                 tag: "div",
35975                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35976         });
35977     },
35978
35979     onRender: function(ctr, pos)
35980     {
35981         var dh = Roo.DomHelper;
35982         /** This region's container element 
35983         * @type Roo.Element */
35984         this.el = dh.append(ctr.dom, {
35985                 tag: "div",
35986                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35987             }, true);
35988         /** This region's title element 
35989         * @type Roo.Element */
35990     
35991         this.titleEl = dh.append(this.el.dom,  {
35992                 tag: "div",
35993                 unselectable: "on",
35994                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35995                 children:[
35996                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35997                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35998                 ]
35999             }, true);
36000         
36001         this.titleEl.enableDisplayMode();
36002         /** This region's title text element 
36003         * @type HTMLElement */
36004         this.titleTextEl = this.titleEl.dom.firstChild;
36005         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36006         /*
36007         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36008         this.closeBtn.enableDisplayMode();
36009         this.closeBtn.on("click", this.closeClicked, this);
36010         this.closeBtn.hide();
36011     */
36012         this.createBody(this.config);
36013         if(this.config.hideWhenEmpty){
36014             this.hide();
36015             this.on("paneladded", this.validateVisibility, this);
36016             this.on("panelremoved", this.validateVisibility, this);
36017         }
36018         if(this.autoScroll){
36019             this.bodyEl.setStyle("overflow", "auto");
36020         }else{
36021             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36022         }
36023         //if(c.titlebar !== false){
36024             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36025                 this.titleEl.hide();
36026             }else{
36027                 this.titleEl.show();
36028                 if(this.config.title){
36029                     this.titleTextEl.innerHTML = this.config.title;
36030                 }
36031             }
36032         //}
36033         if(this.config.collapsed){
36034             this.collapse(true);
36035         }
36036         if(this.config.hidden){
36037             this.hide();
36038         }
36039         
36040         if (this.unrendered_panels && this.unrendered_panels.length) {
36041             for (var i =0;i< this.unrendered_panels.length; i++) {
36042                 this.add(this.unrendered_panels[i]);
36043             }
36044             this.unrendered_panels = null;
36045             
36046         }
36047         
36048     },
36049     
36050     applyConfig : function(c)
36051     {
36052         /*
36053          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36054             var dh = Roo.DomHelper;
36055             if(c.titlebar !== false){
36056                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36057                 this.collapseBtn.on("click", this.collapse, this);
36058                 this.collapseBtn.enableDisplayMode();
36059                 /*
36060                 if(c.showPin === true || this.showPin){
36061                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36062                     this.stickBtn.enableDisplayMode();
36063                     this.stickBtn.on("click", this.expand, this);
36064                     this.stickBtn.hide();
36065                 }
36066                 
36067             }
36068             */
36069             /** This region's collapsed element
36070             * @type Roo.Element */
36071             /*
36072              *
36073             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36074                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36075             ]}, true);
36076             
36077             if(c.floatable !== false){
36078                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36079                this.collapsedEl.on("click", this.collapseClick, this);
36080             }
36081
36082             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36083                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36084                    id: "message", unselectable: "on", style:{"float":"left"}});
36085                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36086              }
36087             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36088             this.expandBtn.on("click", this.expand, this);
36089             
36090         }
36091         
36092         if(this.collapseBtn){
36093             this.collapseBtn.setVisible(c.collapsible == true);
36094         }
36095         
36096         this.cmargins = c.cmargins || this.cmargins ||
36097                          (this.position == "west" || this.position == "east" ?
36098                              {top: 0, left: 2, right:2, bottom: 0} :
36099                              {top: 2, left: 0, right:0, bottom: 2});
36100         */
36101         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36102         
36103         
36104         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36105         
36106         this.autoScroll = c.autoScroll || false;
36107         
36108         
36109        
36110         
36111         this.duration = c.duration || .30;
36112         this.slideDuration = c.slideDuration || .45;
36113         this.config = c;
36114        
36115     },
36116     /**
36117      * Returns true if this region is currently visible.
36118      * @return {Boolean}
36119      */
36120     isVisible : function(){
36121         return this.visible;
36122     },
36123
36124     /**
36125      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36126      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36127      */
36128     //setCollapsedTitle : function(title){
36129     //    title = title || "&#160;";
36130      //   if(this.collapsedTitleTextEl){
36131       //      this.collapsedTitleTextEl.innerHTML = title;
36132        // }
36133     //},
36134
36135     getBox : function(){
36136         var b;
36137       //  if(!this.collapsed){
36138             b = this.el.getBox(false, true);
36139        // }else{
36140           //  b = this.collapsedEl.getBox(false, true);
36141         //}
36142         return b;
36143     },
36144
36145     getMargins : function(){
36146         return this.margins;
36147         //return this.collapsed ? this.cmargins : this.margins;
36148     },
36149 /*
36150     highlight : function(){
36151         this.el.addClass("x-layout-panel-dragover");
36152     },
36153
36154     unhighlight : function(){
36155         this.el.removeClass("x-layout-panel-dragover");
36156     },
36157 */
36158     updateBox : function(box)
36159     {
36160         if (!this.bodyEl) {
36161             return; // not rendered yet..
36162         }
36163         
36164         this.box = box;
36165         if(!this.collapsed){
36166             this.el.dom.style.left = box.x + "px";
36167             this.el.dom.style.top = box.y + "px";
36168             this.updateBody(box.width, box.height);
36169         }else{
36170             this.collapsedEl.dom.style.left = box.x + "px";
36171             this.collapsedEl.dom.style.top = box.y + "px";
36172             this.collapsedEl.setSize(box.width, box.height);
36173         }
36174         if(this.tabs){
36175             this.tabs.autoSizeTabs();
36176         }
36177     },
36178
36179     updateBody : function(w, h)
36180     {
36181         if(w !== null){
36182             this.el.setWidth(w);
36183             w -= this.el.getBorderWidth("rl");
36184             if(this.config.adjustments){
36185                 w += this.config.adjustments[0];
36186             }
36187         }
36188         if(h !== null && h > 0){
36189             this.el.setHeight(h);
36190             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36191             h -= this.el.getBorderWidth("tb");
36192             if(this.config.adjustments){
36193                 h += this.config.adjustments[1];
36194             }
36195             this.bodyEl.setHeight(h);
36196             if(this.tabs){
36197                 h = this.tabs.syncHeight(h);
36198             }
36199         }
36200         if(this.panelSize){
36201             w = w !== null ? w : this.panelSize.width;
36202             h = h !== null ? h : this.panelSize.height;
36203         }
36204         if(this.activePanel){
36205             var el = this.activePanel.getEl();
36206             w = w !== null ? w : el.getWidth();
36207             h = h !== null ? h : el.getHeight();
36208             this.panelSize = {width: w, height: h};
36209             this.activePanel.setSize(w, h);
36210         }
36211         if(Roo.isIE && this.tabs){
36212             this.tabs.el.repaint();
36213         }
36214     },
36215
36216     /**
36217      * Returns the container element for this region.
36218      * @return {Roo.Element}
36219      */
36220     getEl : function(){
36221         return this.el;
36222     },
36223
36224     /**
36225      * Hides this region.
36226      */
36227     hide : function(){
36228         //if(!this.collapsed){
36229             this.el.dom.style.left = "-2000px";
36230             this.el.hide();
36231         //}else{
36232          //   this.collapsedEl.dom.style.left = "-2000px";
36233          //   this.collapsedEl.hide();
36234        // }
36235         this.visible = false;
36236         this.fireEvent("visibilitychange", this, false);
36237     },
36238
36239     /**
36240      * Shows this region if it was previously hidden.
36241      */
36242     show : function(){
36243         //if(!this.collapsed){
36244             this.el.show();
36245         //}else{
36246         //    this.collapsedEl.show();
36247        // }
36248         this.visible = true;
36249         this.fireEvent("visibilitychange", this, true);
36250     },
36251 /*
36252     closeClicked : function(){
36253         if(this.activePanel){
36254             this.remove(this.activePanel);
36255         }
36256     },
36257
36258     collapseClick : function(e){
36259         if(this.isSlid){
36260            e.stopPropagation();
36261            this.slideIn();
36262         }else{
36263            e.stopPropagation();
36264            this.slideOut();
36265         }
36266     },
36267 */
36268     /**
36269      * Collapses this region.
36270      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36271      */
36272     /*
36273     collapse : function(skipAnim, skipCheck = false){
36274         if(this.collapsed) {
36275             return;
36276         }
36277         
36278         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36279             
36280             this.collapsed = true;
36281             if(this.split){
36282                 this.split.el.hide();
36283             }
36284             if(this.config.animate && skipAnim !== true){
36285                 this.fireEvent("invalidated", this);
36286                 this.animateCollapse();
36287             }else{
36288                 this.el.setLocation(-20000,-20000);
36289                 this.el.hide();
36290                 this.collapsedEl.show();
36291                 this.fireEvent("collapsed", this);
36292                 this.fireEvent("invalidated", this);
36293             }
36294         }
36295         
36296     },
36297 */
36298     animateCollapse : function(){
36299         // overridden
36300     },
36301
36302     /**
36303      * Expands this region if it was previously collapsed.
36304      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36305      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36306      */
36307     /*
36308     expand : function(e, skipAnim){
36309         if(e) {
36310             e.stopPropagation();
36311         }
36312         if(!this.collapsed || this.el.hasActiveFx()) {
36313             return;
36314         }
36315         if(this.isSlid){
36316             this.afterSlideIn();
36317             skipAnim = true;
36318         }
36319         this.collapsed = false;
36320         if(this.config.animate && skipAnim !== true){
36321             this.animateExpand();
36322         }else{
36323             this.el.show();
36324             if(this.split){
36325                 this.split.el.show();
36326             }
36327             this.collapsedEl.setLocation(-2000,-2000);
36328             this.collapsedEl.hide();
36329             this.fireEvent("invalidated", this);
36330             this.fireEvent("expanded", this);
36331         }
36332     },
36333 */
36334     animateExpand : function(){
36335         // overridden
36336     },
36337
36338     initTabs : function()
36339     {
36340         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36341         
36342         var ts = new Roo.bootstrap.panel.Tabs({
36343             el: this.bodyEl.dom,
36344             region : this,
36345             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
36346             disableTooltips: this.config.disableTabTips,
36347             toolbar : this.config.toolbar
36348         });
36349         
36350         if(this.config.hideTabs){
36351             ts.stripWrap.setDisplayed(false);
36352         }
36353         this.tabs = ts;
36354         ts.resizeTabs = this.config.resizeTabs === true;
36355         ts.minTabWidth = this.config.minTabWidth || 40;
36356         ts.maxTabWidth = this.config.maxTabWidth || 250;
36357         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36358         ts.monitorResize = false;
36359         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36360         ts.bodyEl.addClass('roo-layout-tabs-body');
36361         this.panels.each(this.initPanelAsTab, this);
36362     },
36363
36364     initPanelAsTab : function(panel){
36365         var ti = this.tabs.addTab(
36366             panel.getEl().id,
36367             panel.getTitle(),
36368             null,
36369             this.config.closeOnTab && panel.isClosable(),
36370             panel.tpl
36371         );
36372         if(panel.tabTip !== undefined){
36373             ti.setTooltip(panel.tabTip);
36374         }
36375         ti.on("activate", function(){
36376               this.setActivePanel(panel);
36377         }, this);
36378         
36379         if(this.config.closeOnTab){
36380             ti.on("beforeclose", function(t, e){
36381                 e.cancel = true;
36382                 this.remove(panel);
36383             }, this);
36384         }
36385         
36386         panel.tabItem = ti;
36387         
36388         return ti;
36389     },
36390
36391     updatePanelTitle : function(panel, title)
36392     {
36393         if(this.activePanel == panel){
36394             this.updateTitle(title);
36395         }
36396         if(this.tabs){
36397             var ti = this.tabs.getTab(panel.getEl().id);
36398             ti.setText(title);
36399             if(panel.tabTip !== undefined){
36400                 ti.setTooltip(panel.tabTip);
36401             }
36402         }
36403     },
36404
36405     updateTitle : function(title){
36406         if(this.titleTextEl && !this.config.title){
36407             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36408         }
36409     },
36410
36411     setActivePanel : function(panel)
36412     {
36413         panel = this.getPanel(panel);
36414         if(this.activePanel && this.activePanel != panel){
36415             if(this.activePanel.setActiveState(false) === false){
36416                 return;
36417             }
36418         }
36419         this.activePanel = panel;
36420         panel.setActiveState(true);
36421         if(this.panelSize){
36422             panel.setSize(this.panelSize.width, this.panelSize.height);
36423         }
36424         if(this.closeBtn){
36425             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36426         }
36427         this.updateTitle(panel.getTitle());
36428         if(this.tabs){
36429             this.fireEvent("invalidated", this);
36430         }
36431         this.fireEvent("panelactivated", this, panel);
36432     },
36433
36434     /**
36435      * Shows the specified panel.
36436      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36437      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36438      */
36439     showPanel : function(panel)
36440     {
36441         panel = this.getPanel(panel);
36442         if(panel){
36443             if(this.tabs){
36444                 var tab = this.tabs.getTab(panel.getEl().id);
36445                 if(tab.isHidden()){
36446                     this.tabs.unhideTab(tab.id);
36447                 }
36448                 tab.activate();
36449             }else{
36450                 this.setActivePanel(panel);
36451             }
36452         }
36453         return panel;
36454     },
36455
36456     /**
36457      * Get the active panel for this region.
36458      * @return {Roo.ContentPanel} The active panel or null
36459      */
36460     getActivePanel : function(){
36461         return this.activePanel;
36462     },
36463
36464     validateVisibility : function(){
36465         if(this.panels.getCount() < 1){
36466             this.updateTitle("&#160;");
36467             this.closeBtn.hide();
36468             this.hide();
36469         }else{
36470             if(!this.isVisible()){
36471                 this.show();
36472             }
36473         }
36474     },
36475
36476     /**
36477      * Adds the passed ContentPanel(s) to this region.
36478      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36479      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36480      */
36481     add : function(panel)
36482     {
36483         if(arguments.length > 1){
36484             for(var i = 0, len = arguments.length; i < len; i++) {
36485                 this.add(arguments[i]);
36486             }
36487             return null;
36488         }
36489         
36490         // if we have not been rendered yet, then we can not really do much of this..
36491         if (!this.bodyEl) {
36492             this.unrendered_panels.push(panel);
36493             return panel;
36494         }
36495         
36496         
36497         
36498         
36499         if(this.hasPanel(panel)){
36500             this.showPanel(panel);
36501             return panel;
36502         }
36503         panel.setRegion(this);
36504         this.panels.add(panel);
36505        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36506             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36507             // and hide them... ???
36508             this.bodyEl.dom.appendChild(panel.getEl().dom);
36509             if(panel.background !== true){
36510                 this.setActivePanel(panel);
36511             }
36512             this.fireEvent("paneladded", this, panel);
36513             return panel;
36514         }
36515         */
36516         if(!this.tabs){
36517             this.initTabs();
36518         }else{
36519             this.initPanelAsTab(panel);
36520         }
36521         
36522         
36523         if(panel.background !== true){
36524             this.tabs.activate(panel.getEl().id);
36525         }
36526         this.fireEvent("paneladded", this, panel);
36527         return panel;
36528     },
36529
36530     /**
36531      * Hides the tab for the specified panel.
36532      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36533      */
36534     hidePanel : function(panel){
36535         if(this.tabs && (panel = this.getPanel(panel))){
36536             this.tabs.hideTab(panel.getEl().id);
36537         }
36538     },
36539
36540     /**
36541      * Unhides the tab for a previously hidden panel.
36542      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36543      */
36544     unhidePanel : function(panel){
36545         if(this.tabs && (panel = this.getPanel(panel))){
36546             this.tabs.unhideTab(panel.getEl().id);
36547         }
36548     },
36549
36550     clearPanels : function(){
36551         while(this.panels.getCount() > 0){
36552              this.remove(this.panels.first());
36553         }
36554     },
36555
36556     /**
36557      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36558      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36559      * @param {Boolean} preservePanel Overrides the config preservePanel option
36560      * @return {Roo.ContentPanel} The panel that was removed
36561      */
36562     remove : function(panel, preservePanel)
36563     {
36564         panel = this.getPanel(panel);
36565         if(!panel){
36566             return null;
36567         }
36568         var e = {};
36569         this.fireEvent("beforeremove", this, panel, e);
36570         if(e.cancel === true){
36571             return null;
36572         }
36573         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36574         var panelId = panel.getId();
36575         this.panels.removeKey(panelId);
36576         if(preservePanel){
36577             document.body.appendChild(panel.getEl().dom);
36578         }
36579         if(this.tabs){
36580             this.tabs.removeTab(panel.getEl().id);
36581         }else if (!preservePanel){
36582             this.bodyEl.dom.removeChild(panel.getEl().dom);
36583         }
36584         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36585             var p = this.panels.first();
36586             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36587             tempEl.appendChild(p.getEl().dom);
36588             this.bodyEl.update("");
36589             this.bodyEl.dom.appendChild(p.getEl().dom);
36590             tempEl = null;
36591             this.updateTitle(p.getTitle());
36592             this.tabs = null;
36593             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36594             this.setActivePanel(p);
36595         }
36596         panel.setRegion(null);
36597         if(this.activePanel == panel){
36598             this.activePanel = null;
36599         }
36600         if(this.config.autoDestroy !== false && preservePanel !== true){
36601             try{panel.destroy();}catch(e){}
36602         }
36603         this.fireEvent("panelremoved", this, panel);
36604         return panel;
36605     },
36606
36607     /**
36608      * Returns the TabPanel component used by this region
36609      * @return {Roo.TabPanel}
36610      */
36611     getTabs : function(){
36612         return this.tabs;
36613     },
36614
36615     createTool : function(parentEl, className){
36616         var btn = Roo.DomHelper.append(parentEl, {
36617             tag: "div",
36618             cls: "x-layout-tools-button",
36619             children: [ {
36620                 tag: "div",
36621                 cls: "roo-layout-tools-button-inner " + className,
36622                 html: "&#160;"
36623             }]
36624         }, true);
36625         btn.addClassOnOver("roo-layout-tools-button-over");
36626         return btn;
36627     }
36628 });/*
36629  * Based on:
36630  * Ext JS Library 1.1.1
36631  * Copyright(c) 2006-2007, Ext JS, LLC.
36632  *
36633  * Originally Released Under LGPL - original licence link has changed is not relivant.
36634  *
36635  * Fork - LGPL
36636  * <script type="text/javascript">
36637  */
36638  
36639
36640
36641 /**
36642  * @class Roo.SplitLayoutRegion
36643  * @extends Roo.LayoutRegion
36644  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36645  */
36646 Roo.bootstrap.layout.Split = function(config){
36647     this.cursor = config.cursor;
36648     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36649 };
36650
36651 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36652 {
36653     splitTip : "Drag to resize.",
36654     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36655     useSplitTips : false,
36656
36657     applyConfig : function(config){
36658         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36659     },
36660     
36661     onRender : function(ctr,pos) {
36662         
36663         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36664         if(!this.config.split){
36665             return;
36666         }
36667         if(!this.split){
36668             
36669             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36670                             tag: "div",
36671                             id: this.el.id + "-split",
36672                             cls: "roo-layout-split roo-layout-split-"+this.position,
36673                             html: "&#160;"
36674             });
36675             /** The SplitBar for this region 
36676             * @type Roo.SplitBar */
36677             // does not exist yet...
36678             Roo.log([this.position, this.orientation]);
36679             
36680             this.split = new Roo.bootstrap.SplitBar({
36681                 dragElement : splitEl,
36682                 resizingElement: this.el,
36683                 orientation : this.orientation
36684             });
36685             
36686             this.split.on("moved", this.onSplitMove, this);
36687             this.split.useShim = this.config.useShim === true;
36688             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36689             if(this.useSplitTips){
36690                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36691             }
36692             //if(config.collapsible){
36693             //    this.split.el.on("dblclick", this.collapse,  this);
36694             //}
36695         }
36696         if(typeof this.config.minSize != "undefined"){
36697             this.split.minSize = this.config.minSize;
36698         }
36699         if(typeof this.config.maxSize != "undefined"){
36700             this.split.maxSize = this.config.maxSize;
36701         }
36702         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36703             this.hideSplitter();
36704         }
36705         
36706     },
36707
36708     getHMaxSize : function(){
36709          var cmax = this.config.maxSize || 10000;
36710          var center = this.mgr.getRegion("center");
36711          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36712     },
36713
36714     getVMaxSize : function(){
36715          var cmax = this.config.maxSize || 10000;
36716          var center = this.mgr.getRegion("center");
36717          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36718     },
36719
36720     onSplitMove : function(split, newSize){
36721         this.fireEvent("resized", this, newSize);
36722     },
36723     
36724     /** 
36725      * Returns the {@link Roo.SplitBar} for this region.
36726      * @return {Roo.SplitBar}
36727      */
36728     getSplitBar : function(){
36729         return this.split;
36730     },
36731     
36732     hide : function(){
36733         this.hideSplitter();
36734         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36735     },
36736
36737     hideSplitter : function(){
36738         if(this.split){
36739             this.split.el.setLocation(-2000,-2000);
36740             this.split.el.hide();
36741         }
36742     },
36743
36744     show : function(){
36745         if(this.split){
36746             this.split.el.show();
36747         }
36748         Roo.bootstrap.layout.Split.superclass.show.call(this);
36749     },
36750     
36751     beforeSlide: function(){
36752         if(Roo.isGecko){// firefox overflow auto bug workaround
36753             this.bodyEl.clip();
36754             if(this.tabs) {
36755                 this.tabs.bodyEl.clip();
36756             }
36757             if(this.activePanel){
36758                 this.activePanel.getEl().clip();
36759                 
36760                 if(this.activePanel.beforeSlide){
36761                     this.activePanel.beforeSlide();
36762                 }
36763             }
36764         }
36765     },
36766     
36767     afterSlide : function(){
36768         if(Roo.isGecko){// firefox overflow auto bug workaround
36769             this.bodyEl.unclip();
36770             if(this.tabs) {
36771                 this.tabs.bodyEl.unclip();
36772             }
36773             if(this.activePanel){
36774                 this.activePanel.getEl().unclip();
36775                 if(this.activePanel.afterSlide){
36776                     this.activePanel.afterSlide();
36777                 }
36778             }
36779         }
36780     },
36781
36782     initAutoHide : function(){
36783         if(this.autoHide !== false){
36784             if(!this.autoHideHd){
36785                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36786                 this.autoHideHd = {
36787                     "mouseout": function(e){
36788                         if(!e.within(this.el, true)){
36789                             st.delay(500);
36790                         }
36791                     },
36792                     "mouseover" : function(e){
36793                         st.cancel();
36794                     },
36795                     scope : this
36796                 };
36797             }
36798             this.el.on(this.autoHideHd);
36799         }
36800     },
36801
36802     clearAutoHide : function(){
36803         if(this.autoHide !== false){
36804             this.el.un("mouseout", this.autoHideHd.mouseout);
36805             this.el.un("mouseover", this.autoHideHd.mouseover);
36806         }
36807     },
36808
36809     clearMonitor : function(){
36810         Roo.get(document).un("click", this.slideInIf, this);
36811     },
36812
36813     // these names are backwards but not changed for compat
36814     slideOut : function(){
36815         if(this.isSlid || this.el.hasActiveFx()){
36816             return;
36817         }
36818         this.isSlid = true;
36819         if(this.collapseBtn){
36820             this.collapseBtn.hide();
36821         }
36822         this.closeBtnState = this.closeBtn.getStyle('display');
36823         this.closeBtn.hide();
36824         if(this.stickBtn){
36825             this.stickBtn.show();
36826         }
36827         this.el.show();
36828         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36829         this.beforeSlide();
36830         this.el.setStyle("z-index", 10001);
36831         this.el.slideIn(this.getSlideAnchor(), {
36832             callback: function(){
36833                 this.afterSlide();
36834                 this.initAutoHide();
36835                 Roo.get(document).on("click", this.slideInIf, this);
36836                 this.fireEvent("slideshow", this);
36837             },
36838             scope: this,
36839             block: true
36840         });
36841     },
36842
36843     afterSlideIn : function(){
36844         this.clearAutoHide();
36845         this.isSlid = false;
36846         this.clearMonitor();
36847         this.el.setStyle("z-index", "");
36848         if(this.collapseBtn){
36849             this.collapseBtn.show();
36850         }
36851         this.closeBtn.setStyle('display', this.closeBtnState);
36852         if(this.stickBtn){
36853             this.stickBtn.hide();
36854         }
36855         this.fireEvent("slidehide", this);
36856     },
36857
36858     slideIn : function(cb){
36859         if(!this.isSlid || this.el.hasActiveFx()){
36860             Roo.callback(cb);
36861             return;
36862         }
36863         this.isSlid = false;
36864         this.beforeSlide();
36865         this.el.slideOut(this.getSlideAnchor(), {
36866             callback: function(){
36867                 this.el.setLeftTop(-10000, -10000);
36868                 this.afterSlide();
36869                 this.afterSlideIn();
36870                 Roo.callback(cb);
36871             },
36872             scope: this,
36873             block: true
36874         });
36875     },
36876     
36877     slideInIf : function(e){
36878         if(!e.within(this.el)){
36879             this.slideIn();
36880         }
36881     },
36882
36883     animateCollapse : function(){
36884         this.beforeSlide();
36885         this.el.setStyle("z-index", 20000);
36886         var anchor = this.getSlideAnchor();
36887         this.el.slideOut(anchor, {
36888             callback : function(){
36889                 this.el.setStyle("z-index", "");
36890                 this.collapsedEl.slideIn(anchor, {duration:.3});
36891                 this.afterSlide();
36892                 this.el.setLocation(-10000,-10000);
36893                 this.el.hide();
36894                 this.fireEvent("collapsed", this);
36895             },
36896             scope: this,
36897             block: true
36898         });
36899     },
36900
36901     animateExpand : function(){
36902         this.beforeSlide();
36903         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36904         this.el.setStyle("z-index", 20000);
36905         this.collapsedEl.hide({
36906             duration:.1
36907         });
36908         this.el.slideIn(this.getSlideAnchor(), {
36909             callback : function(){
36910                 this.el.setStyle("z-index", "");
36911                 this.afterSlide();
36912                 if(this.split){
36913                     this.split.el.show();
36914                 }
36915                 this.fireEvent("invalidated", this);
36916                 this.fireEvent("expanded", this);
36917             },
36918             scope: this,
36919             block: true
36920         });
36921     },
36922
36923     anchors : {
36924         "west" : "left",
36925         "east" : "right",
36926         "north" : "top",
36927         "south" : "bottom"
36928     },
36929
36930     sanchors : {
36931         "west" : "l",
36932         "east" : "r",
36933         "north" : "t",
36934         "south" : "b"
36935     },
36936
36937     canchors : {
36938         "west" : "tl-tr",
36939         "east" : "tr-tl",
36940         "north" : "tl-bl",
36941         "south" : "bl-tl"
36942     },
36943
36944     getAnchor : function(){
36945         return this.anchors[this.position];
36946     },
36947
36948     getCollapseAnchor : function(){
36949         return this.canchors[this.position];
36950     },
36951
36952     getSlideAnchor : function(){
36953         return this.sanchors[this.position];
36954     },
36955
36956     getAlignAdj : function(){
36957         var cm = this.cmargins;
36958         switch(this.position){
36959             case "west":
36960                 return [0, 0];
36961             break;
36962             case "east":
36963                 return [0, 0];
36964             break;
36965             case "north":
36966                 return [0, 0];
36967             break;
36968             case "south":
36969                 return [0, 0];
36970             break;
36971         }
36972     },
36973
36974     getExpandAdj : function(){
36975         var c = this.collapsedEl, cm = this.cmargins;
36976         switch(this.position){
36977             case "west":
36978                 return [-(cm.right+c.getWidth()+cm.left), 0];
36979             break;
36980             case "east":
36981                 return [cm.right+c.getWidth()+cm.left, 0];
36982             break;
36983             case "north":
36984                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36985             break;
36986             case "south":
36987                 return [0, cm.top+cm.bottom+c.getHeight()];
36988             break;
36989         }
36990     }
36991 });/*
36992  * Based on:
36993  * Ext JS Library 1.1.1
36994  * Copyright(c) 2006-2007, Ext JS, LLC.
36995  *
36996  * Originally Released Under LGPL - original licence link has changed is not relivant.
36997  *
36998  * Fork - LGPL
36999  * <script type="text/javascript">
37000  */
37001 /*
37002  * These classes are private internal classes
37003  */
37004 Roo.bootstrap.layout.Center = function(config){
37005     config.region = "center";
37006     Roo.bootstrap.layout.Region.call(this, config);
37007     this.visible = true;
37008     this.minWidth = config.minWidth || 20;
37009     this.minHeight = config.minHeight || 20;
37010 };
37011
37012 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37013     hide : function(){
37014         // center panel can't be hidden
37015     },
37016     
37017     show : function(){
37018         // center panel can't be hidden
37019     },
37020     
37021     getMinWidth: function(){
37022         return this.minWidth;
37023     },
37024     
37025     getMinHeight: function(){
37026         return this.minHeight;
37027     }
37028 });
37029
37030
37031
37032
37033  
37034
37035
37036
37037
37038
37039
37040 Roo.bootstrap.layout.North = function(config)
37041 {
37042     config.region = 'north';
37043     config.cursor = 'n-resize';
37044     
37045     Roo.bootstrap.layout.Split.call(this, config);
37046     
37047     
37048     if(this.split){
37049         this.split.placement = Roo.bootstrap.SplitBar.TOP;
37050         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37051         this.split.el.addClass("roo-layout-split-v");
37052     }
37053     var size = config.initialSize || config.height;
37054     if(typeof size != "undefined"){
37055         this.el.setHeight(size);
37056     }
37057 };
37058 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37059 {
37060     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37061     
37062     
37063     
37064     getBox : function(){
37065         if(this.collapsed){
37066             return this.collapsedEl.getBox();
37067         }
37068         var box = this.el.getBox();
37069         if(this.split){
37070             box.height += this.split.el.getHeight();
37071         }
37072         return box;
37073     },
37074     
37075     updateBox : function(box){
37076         if(this.split && !this.collapsed){
37077             box.height -= this.split.el.getHeight();
37078             this.split.el.setLeft(box.x);
37079             this.split.el.setTop(box.y+box.height);
37080             this.split.el.setWidth(box.width);
37081         }
37082         if(this.collapsed){
37083             this.updateBody(box.width, null);
37084         }
37085         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37086     }
37087 });
37088
37089
37090
37091
37092
37093 Roo.bootstrap.layout.South = function(config){
37094     config.region = 'south';
37095     config.cursor = 's-resize';
37096     Roo.bootstrap.layout.Split.call(this, config);
37097     if(this.split){
37098         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37099         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37100         this.split.el.addClass("roo-layout-split-v");
37101     }
37102     var size = config.initialSize || config.height;
37103     if(typeof size != "undefined"){
37104         this.el.setHeight(size);
37105     }
37106 };
37107
37108 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37109     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37110     getBox : function(){
37111         if(this.collapsed){
37112             return this.collapsedEl.getBox();
37113         }
37114         var box = this.el.getBox();
37115         if(this.split){
37116             var sh = this.split.el.getHeight();
37117             box.height += sh;
37118             box.y -= sh;
37119         }
37120         return box;
37121     },
37122     
37123     updateBox : function(box){
37124         if(this.split && !this.collapsed){
37125             var sh = this.split.el.getHeight();
37126             box.height -= sh;
37127             box.y += sh;
37128             this.split.el.setLeft(box.x);
37129             this.split.el.setTop(box.y-sh);
37130             this.split.el.setWidth(box.width);
37131         }
37132         if(this.collapsed){
37133             this.updateBody(box.width, null);
37134         }
37135         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37136     }
37137 });
37138
37139 Roo.bootstrap.layout.East = function(config){
37140     config.region = "east";
37141     config.cursor = "e-resize";
37142     Roo.bootstrap.layout.Split.call(this, config);
37143     if(this.split){
37144         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37145         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37146         this.split.el.addClass("roo-layout-split-h");
37147     }
37148     var size = config.initialSize || config.width;
37149     if(typeof size != "undefined"){
37150         this.el.setWidth(size);
37151     }
37152 };
37153 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37154     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37155     getBox : function(){
37156         if(this.collapsed){
37157             return this.collapsedEl.getBox();
37158         }
37159         var box = this.el.getBox();
37160         if(this.split){
37161             var sw = this.split.el.getWidth();
37162             box.width += sw;
37163             box.x -= sw;
37164         }
37165         return box;
37166     },
37167
37168     updateBox : function(box){
37169         if(this.split && !this.collapsed){
37170             var sw = this.split.el.getWidth();
37171             box.width -= sw;
37172             this.split.el.setLeft(box.x);
37173             this.split.el.setTop(box.y);
37174             this.split.el.setHeight(box.height);
37175             box.x += sw;
37176         }
37177         if(this.collapsed){
37178             this.updateBody(null, box.height);
37179         }
37180         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37181     }
37182 });
37183
37184 Roo.bootstrap.layout.West = function(config){
37185     config.region = "west";
37186     config.cursor = "w-resize";
37187     
37188     Roo.bootstrap.layout.Split.call(this, config);
37189     if(this.split){
37190         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37191         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37192         this.split.el.addClass("roo-layout-split-h");
37193     }
37194     
37195 };
37196 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37197     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37198     
37199     onRender: function(ctr, pos)
37200     {
37201         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37202         var size = this.config.initialSize || this.config.width;
37203         if(typeof size != "undefined"){
37204             this.el.setWidth(size);
37205         }
37206     },
37207     
37208     getBox : function(){
37209         if(this.collapsed){
37210             return this.collapsedEl.getBox();
37211         }
37212         var box = this.el.getBox();
37213         if(this.split){
37214             box.width += this.split.el.getWidth();
37215         }
37216         return box;
37217     },
37218     
37219     updateBox : function(box){
37220         if(this.split && !this.collapsed){
37221             var sw = this.split.el.getWidth();
37222             box.width -= sw;
37223             this.split.el.setLeft(box.x+box.width);
37224             this.split.el.setTop(box.y);
37225             this.split.el.setHeight(box.height);
37226         }
37227         if(this.collapsed){
37228             this.updateBody(null, box.height);
37229         }
37230         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37231     }
37232 });Roo.namespace("Roo.bootstrap.panel");/*
37233  * Based on:
37234  * Ext JS Library 1.1.1
37235  * Copyright(c) 2006-2007, Ext JS, LLC.
37236  *
37237  * Originally Released Under LGPL - original licence link has changed is not relivant.
37238  *
37239  * Fork - LGPL
37240  * <script type="text/javascript">
37241  */
37242 /**
37243  * @class Roo.ContentPanel
37244  * @extends Roo.util.Observable
37245  * A basic ContentPanel element.
37246  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37247  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37248  * @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
37249  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37250  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37251  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37252  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37253  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37254  * @cfg {String} title          The title for this panel
37255  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37256  * @cfg {String} url            Calls {@link #setUrl} with this value
37257  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37258  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37259  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37260  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37261  * @cfg {Boolean} badges render the badges
37262
37263  * @constructor
37264  * Create a new ContentPanel.
37265  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37266  * @param {String/Object} config A string to set only the title or a config object
37267  * @param {String} content (optional) Set the HTML content for this panel
37268  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37269  */
37270 Roo.bootstrap.panel.Content = function( config){
37271     
37272     this.tpl = config.tpl || false;
37273     
37274     var el = config.el;
37275     var content = config.content;
37276
37277     if(config.autoCreate){ // xtype is available if this is called from factory
37278         el = Roo.id();
37279     }
37280     this.el = Roo.get(el);
37281     if(!this.el && config && config.autoCreate){
37282         if(typeof config.autoCreate == "object"){
37283             if(!config.autoCreate.id){
37284                 config.autoCreate.id = config.id||el;
37285             }
37286             this.el = Roo.DomHelper.append(document.body,
37287                         config.autoCreate, true);
37288         }else{
37289             var elcfg =  {   tag: "div",
37290                             cls: "roo-layout-inactive-content",
37291                             id: config.id||el
37292                             };
37293             if (config.html) {
37294                 elcfg.html = config.html;
37295                 
37296             }
37297                         
37298             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37299         }
37300     } 
37301     this.closable = false;
37302     this.loaded = false;
37303     this.active = false;
37304    
37305       
37306     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37307         
37308         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37309         
37310         this.wrapEl = this.el; //this.el.wrap();
37311         var ti = [];
37312         if (config.toolbar.items) {
37313             ti = config.toolbar.items ;
37314             delete config.toolbar.items ;
37315         }
37316         
37317         var nitems = [];
37318         this.toolbar.render(this.wrapEl, 'before');
37319         for(var i =0;i < ti.length;i++) {
37320           //  Roo.log(['add child', items[i]]);
37321             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37322         }
37323         this.toolbar.items = nitems;
37324         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37325         delete config.toolbar;
37326         
37327     }
37328     /*
37329     // xtype created footer. - not sure if will work as we normally have to render first..
37330     if (this.footer && !this.footer.el && this.footer.xtype) {
37331         if (!this.wrapEl) {
37332             this.wrapEl = this.el.wrap();
37333         }
37334     
37335         this.footer.container = this.wrapEl.createChild();
37336          
37337         this.footer = Roo.factory(this.footer, Roo);
37338         
37339     }
37340     */
37341     
37342      if(typeof config == "string"){
37343         this.title = config;
37344     }else{
37345         Roo.apply(this, config);
37346     }
37347     
37348     if(this.resizeEl){
37349         this.resizeEl = Roo.get(this.resizeEl, true);
37350     }else{
37351         this.resizeEl = this.el;
37352     }
37353     // handle view.xtype
37354     
37355  
37356     
37357     
37358     this.addEvents({
37359         /**
37360          * @event activate
37361          * Fires when this panel is activated. 
37362          * @param {Roo.ContentPanel} this
37363          */
37364         "activate" : true,
37365         /**
37366          * @event deactivate
37367          * Fires when this panel is activated. 
37368          * @param {Roo.ContentPanel} this
37369          */
37370         "deactivate" : true,
37371
37372         /**
37373          * @event resize
37374          * Fires when this panel is resized if fitToFrame is true.
37375          * @param {Roo.ContentPanel} this
37376          * @param {Number} width The width after any component adjustments
37377          * @param {Number} height The height after any component adjustments
37378          */
37379         "resize" : true,
37380         
37381          /**
37382          * @event render
37383          * Fires when this tab is created
37384          * @param {Roo.ContentPanel} this
37385          */
37386         "render" : true
37387         
37388         
37389         
37390     });
37391     
37392
37393     
37394     
37395     if(this.autoScroll){
37396         this.resizeEl.setStyle("overflow", "auto");
37397     } else {
37398         // fix randome scrolling
37399         //this.el.on('scroll', function() {
37400         //    Roo.log('fix random scolling');
37401         //    this.scrollTo('top',0); 
37402         //});
37403     }
37404     content = content || this.content;
37405     if(content){
37406         this.setContent(content);
37407     }
37408     if(config && config.url){
37409         this.setUrl(this.url, this.params, this.loadOnce);
37410     }
37411     
37412     
37413     
37414     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37415     
37416     if (this.view && typeof(this.view.xtype) != 'undefined') {
37417         this.view.el = this.el.appendChild(document.createElement("div"));
37418         this.view = Roo.factory(this.view); 
37419         this.view.render  &&  this.view.render(false, '');  
37420     }
37421     
37422     
37423     this.fireEvent('render', this);
37424 };
37425
37426 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37427     
37428     tabTip : '',
37429     
37430     setRegion : function(region){
37431         this.region = region;
37432         this.setActiveClass(region && !this.background);
37433     },
37434     
37435     
37436     setActiveClass: function(state)
37437     {
37438         if(state){
37439            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37440            this.el.setStyle('position','relative');
37441         }else{
37442            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37443            this.el.setStyle('position', 'absolute');
37444         } 
37445     },
37446     
37447     /**
37448      * Returns the toolbar for this Panel if one was configured. 
37449      * @return {Roo.Toolbar} 
37450      */
37451     getToolbar : function(){
37452         return this.toolbar;
37453     },
37454     
37455     setActiveState : function(active)
37456     {
37457         this.active = active;
37458         this.setActiveClass(active);
37459         if(!active){
37460             if(this.fireEvent("deactivate", this) === false){
37461                 return false;
37462             }
37463             return true;
37464         }
37465         this.fireEvent("activate", this);
37466         return true;
37467     },
37468     /**
37469      * Updates this panel's element
37470      * @param {String} content The new content
37471      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37472     */
37473     setContent : function(content, loadScripts){
37474         this.el.update(content, loadScripts);
37475     },
37476
37477     ignoreResize : function(w, h){
37478         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37479             return true;
37480         }else{
37481             this.lastSize = {width: w, height: h};
37482             return false;
37483         }
37484     },
37485     /**
37486      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37487      * @return {Roo.UpdateManager} The UpdateManager
37488      */
37489     getUpdateManager : function(){
37490         return this.el.getUpdateManager();
37491     },
37492      /**
37493      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37494      * @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:
37495 <pre><code>
37496 panel.load({
37497     url: "your-url.php",
37498     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37499     callback: yourFunction,
37500     scope: yourObject, //(optional scope)
37501     discardUrl: false,
37502     nocache: false,
37503     text: "Loading...",
37504     timeout: 30,
37505     scripts: false
37506 });
37507 </code></pre>
37508      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37509      * 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.
37510      * @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}
37511      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37512      * @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.
37513      * @return {Roo.ContentPanel} this
37514      */
37515     load : function(){
37516         var um = this.el.getUpdateManager();
37517         um.update.apply(um, arguments);
37518         return this;
37519     },
37520
37521
37522     /**
37523      * 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.
37524      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37525      * @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)
37526      * @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)
37527      * @return {Roo.UpdateManager} The UpdateManager
37528      */
37529     setUrl : function(url, params, loadOnce){
37530         if(this.refreshDelegate){
37531             this.removeListener("activate", this.refreshDelegate);
37532         }
37533         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37534         this.on("activate", this.refreshDelegate);
37535         return this.el.getUpdateManager();
37536     },
37537     
37538     _handleRefresh : function(url, params, loadOnce){
37539         if(!loadOnce || !this.loaded){
37540             var updater = this.el.getUpdateManager();
37541             updater.update(url, params, this._setLoaded.createDelegate(this));
37542         }
37543     },
37544     
37545     _setLoaded : function(){
37546         this.loaded = true;
37547     }, 
37548     
37549     /**
37550      * Returns this panel's id
37551      * @return {String} 
37552      */
37553     getId : function(){
37554         return this.el.id;
37555     },
37556     
37557     /** 
37558      * Returns this panel's element - used by regiosn to add.
37559      * @return {Roo.Element} 
37560      */
37561     getEl : function(){
37562         return this.wrapEl || this.el;
37563     },
37564     
37565    
37566     
37567     adjustForComponents : function(width, height)
37568     {
37569         //Roo.log('adjustForComponents ');
37570         if(this.resizeEl != this.el){
37571             width -= this.el.getFrameWidth('lr');
37572             height -= this.el.getFrameWidth('tb');
37573         }
37574         if(this.toolbar){
37575             var te = this.toolbar.getEl();
37576             te.setWidth(width);
37577             height -= te.getHeight();
37578         }
37579         if(this.footer){
37580             var te = this.footer.getEl();
37581             te.setWidth(width);
37582             height -= te.getHeight();
37583         }
37584         
37585         
37586         if(this.adjustments){
37587             width += this.adjustments[0];
37588             height += this.adjustments[1];
37589         }
37590         return {"width": width, "height": height};
37591     },
37592     
37593     setSize : function(width, height){
37594         if(this.fitToFrame && !this.ignoreResize(width, height)){
37595             if(this.fitContainer && this.resizeEl != this.el){
37596                 this.el.setSize(width, height);
37597             }
37598             var size = this.adjustForComponents(width, height);
37599             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37600             this.fireEvent('resize', this, size.width, size.height);
37601         }
37602     },
37603     
37604     /**
37605      * Returns this panel's title
37606      * @return {String} 
37607      */
37608     getTitle : function(){
37609         
37610         if (typeof(this.title) != 'object') {
37611             return this.title;
37612         }
37613         
37614         var t = '';
37615         for (var k in this.title) {
37616             if (!this.title.hasOwnProperty(k)) {
37617                 continue;
37618             }
37619             
37620             if (k.indexOf('-') >= 0) {
37621                 var s = k.split('-');
37622                 for (var i = 0; i<s.length; i++) {
37623                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37624                 }
37625             } else {
37626                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37627             }
37628         }
37629         return t;
37630     },
37631     
37632     /**
37633      * Set this panel's title
37634      * @param {String} title
37635      */
37636     setTitle : function(title){
37637         this.title = title;
37638         if(this.region){
37639             this.region.updatePanelTitle(this, title);
37640         }
37641     },
37642     
37643     /**
37644      * Returns true is this panel was configured to be closable
37645      * @return {Boolean} 
37646      */
37647     isClosable : function(){
37648         return this.closable;
37649     },
37650     
37651     beforeSlide : function(){
37652         this.el.clip();
37653         this.resizeEl.clip();
37654     },
37655     
37656     afterSlide : function(){
37657         this.el.unclip();
37658         this.resizeEl.unclip();
37659     },
37660     
37661     /**
37662      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37663      *   Will fail silently if the {@link #setUrl} method has not been called.
37664      *   This does not activate the panel, just updates its content.
37665      */
37666     refresh : function(){
37667         if(this.refreshDelegate){
37668            this.loaded = false;
37669            this.refreshDelegate();
37670         }
37671     },
37672     
37673     /**
37674      * Destroys this panel
37675      */
37676     destroy : function(){
37677         this.el.removeAllListeners();
37678         var tempEl = document.createElement("span");
37679         tempEl.appendChild(this.el.dom);
37680         tempEl.innerHTML = "";
37681         this.el.remove();
37682         this.el = null;
37683     },
37684     
37685     /**
37686      * form - if the content panel contains a form - this is a reference to it.
37687      * @type {Roo.form.Form}
37688      */
37689     form : false,
37690     /**
37691      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37692      *    This contains a reference to it.
37693      * @type {Roo.View}
37694      */
37695     view : false,
37696     
37697       /**
37698      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37699      * <pre><code>
37700
37701 layout.addxtype({
37702        xtype : 'Form',
37703        items: [ .... ]
37704    }
37705 );
37706
37707 </code></pre>
37708      * @param {Object} cfg Xtype definition of item to add.
37709      */
37710     
37711     
37712     getChildContainer: function () {
37713         return this.getEl();
37714     }
37715     
37716     
37717     /*
37718         var  ret = new Roo.factory(cfg);
37719         return ret;
37720         
37721         
37722         // add form..
37723         if (cfg.xtype.match(/^Form$/)) {
37724             
37725             var el;
37726             //if (this.footer) {
37727             //    el = this.footer.container.insertSibling(false, 'before');
37728             //} else {
37729                 el = this.el.createChild();
37730             //}
37731
37732             this.form = new  Roo.form.Form(cfg);
37733             
37734             
37735             if ( this.form.allItems.length) {
37736                 this.form.render(el.dom);
37737             }
37738             return this.form;
37739         }
37740         // should only have one of theses..
37741         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37742             // views.. should not be just added - used named prop 'view''
37743             
37744             cfg.el = this.el.appendChild(document.createElement("div"));
37745             // factory?
37746             
37747             var ret = new Roo.factory(cfg);
37748              
37749              ret.render && ret.render(false, ''); // render blank..
37750             this.view = ret;
37751             return ret;
37752         }
37753         return false;
37754     }
37755     \*/
37756 });
37757  
37758 /**
37759  * @class Roo.bootstrap.panel.Grid
37760  * @extends Roo.bootstrap.panel.Content
37761  * @constructor
37762  * Create a new GridPanel.
37763  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37764  * @param {Object} config A the config object
37765   
37766  */
37767
37768
37769
37770 Roo.bootstrap.panel.Grid = function(config)
37771 {
37772     
37773       
37774     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37775         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37776
37777     config.el = this.wrapper;
37778     //this.el = this.wrapper;
37779     
37780       if (config.container) {
37781         // ctor'ed from a Border/panel.grid
37782         
37783         
37784         this.wrapper.setStyle("overflow", "hidden");
37785         this.wrapper.addClass('roo-grid-container');
37786
37787     }
37788     
37789     
37790     if(config.toolbar){
37791         var tool_el = this.wrapper.createChild();    
37792         this.toolbar = Roo.factory(config.toolbar);
37793         var ti = [];
37794         if (config.toolbar.items) {
37795             ti = config.toolbar.items ;
37796             delete config.toolbar.items ;
37797         }
37798         
37799         var nitems = [];
37800         this.toolbar.render(tool_el);
37801         for(var i =0;i < ti.length;i++) {
37802           //  Roo.log(['add child', items[i]]);
37803             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37804         }
37805         this.toolbar.items = nitems;
37806         
37807         delete config.toolbar;
37808     }
37809     
37810     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37811     config.grid.scrollBody = true;;
37812     config.grid.monitorWindowResize = false; // turn off autosizing
37813     config.grid.autoHeight = false;
37814     config.grid.autoWidth = false;
37815     
37816     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37817     
37818     if (config.background) {
37819         // render grid on panel activation (if panel background)
37820         this.on('activate', function(gp) {
37821             if (!gp.grid.rendered) {
37822                 gp.grid.render(this.wrapper);
37823                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37824             }
37825         });
37826             
37827     } else {
37828         this.grid.render(this.wrapper);
37829         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37830
37831     }
37832     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37833     // ??? needed ??? config.el = this.wrapper;
37834     
37835     
37836     
37837   
37838     // xtype created footer. - not sure if will work as we normally have to render first..
37839     if (this.footer && !this.footer.el && this.footer.xtype) {
37840         
37841         var ctr = this.grid.getView().getFooterPanel(true);
37842         this.footer.dataSource = this.grid.dataSource;
37843         this.footer = Roo.factory(this.footer, Roo);
37844         this.footer.render(ctr);
37845         
37846     }
37847     
37848     
37849     
37850     
37851      
37852 };
37853
37854 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37855     getId : function(){
37856         return this.grid.id;
37857     },
37858     
37859     /**
37860      * Returns the grid for this panel
37861      * @return {Roo.bootstrap.Table} 
37862      */
37863     getGrid : function(){
37864         return this.grid;    
37865     },
37866     
37867     setSize : function(width, height){
37868         if(!this.ignoreResize(width, height)){
37869             var grid = this.grid;
37870             var size = this.adjustForComponents(width, height);
37871             var gridel = grid.getGridEl();
37872             gridel.setSize(size.width, size.height);
37873             /*
37874             var thd = grid.getGridEl().select('thead',true).first();
37875             var tbd = grid.getGridEl().select('tbody', true).first();
37876             if (tbd) {
37877                 tbd.setSize(width, height - thd.getHeight());
37878             }
37879             */
37880             grid.autoSize();
37881         }
37882     },
37883      
37884     
37885     
37886     beforeSlide : function(){
37887         this.grid.getView().scroller.clip();
37888     },
37889     
37890     afterSlide : function(){
37891         this.grid.getView().scroller.unclip();
37892     },
37893     
37894     destroy : function(){
37895         this.grid.destroy();
37896         delete this.grid;
37897         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37898     }
37899 });
37900
37901 /**
37902  * @class Roo.bootstrap.panel.Nest
37903  * @extends Roo.bootstrap.panel.Content
37904  * @constructor
37905  * Create a new Panel, that can contain a layout.Border.
37906  * 
37907  * 
37908  * @param {Roo.BorderLayout} layout The layout for this panel
37909  * @param {String/Object} config A string to set only the title or a config object
37910  */
37911 Roo.bootstrap.panel.Nest = function(config)
37912 {
37913     // construct with only one argument..
37914     /* FIXME - implement nicer consturctors
37915     if (layout.layout) {
37916         config = layout;
37917         layout = config.layout;
37918         delete config.layout;
37919     }
37920     if (layout.xtype && !layout.getEl) {
37921         // then layout needs constructing..
37922         layout = Roo.factory(layout, Roo);
37923     }
37924     */
37925     
37926     config.el =  config.layout.getEl();
37927     
37928     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37929     
37930     config.layout.monitorWindowResize = false; // turn off autosizing
37931     this.layout = config.layout;
37932     this.layout.getEl().addClass("roo-layout-nested-layout");
37933     this.layout.parent = this;
37934     
37935     
37936     
37937     
37938 };
37939
37940 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37941
37942     setSize : function(width, height){
37943         if(!this.ignoreResize(width, height)){
37944             var size = this.adjustForComponents(width, height);
37945             var el = this.layout.getEl();
37946             if (size.height < 1) {
37947                 el.setWidth(size.width);   
37948             } else {
37949                 el.setSize(size.width, size.height);
37950             }
37951             var touch = el.dom.offsetWidth;
37952             this.layout.layout();
37953             // ie requires a double layout on the first pass
37954             if(Roo.isIE && !this.initialized){
37955                 this.initialized = true;
37956                 this.layout.layout();
37957             }
37958         }
37959     },
37960     
37961     // activate all subpanels if not currently active..
37962     
37963     setActiveState : function(active){
37964         this.active = active;
37965         this.setActiveClass(active);
37966         
37967         if(!active){
37968             this.fireEvent("deactivate", this);
37969             return;
37970         }
37971         
37972         this.fireEvent("activate", this);
37973         // not sure if this should happen before or after..
37974         if (!this.layout) {
37975             return; // should not happen..
37976         }
37977         var reg = false;
37978         for (var r in this.layout.regions) {
37979             reg = this.layout.getRegion(r);
37980             if (reg.getActivePanel()) {
37981                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37982                 reg.setActivePanel(reg.getActivePanel());
37983                 continue;
37984             }
37985             if (!reg.panels.length) {
37986                 continue;
37987             }
37988             reg.showPanel(reg.getPanel(0));
37989         }
37990         
37991         
37992         
37993         
37994     },
37995     
37996     /**
37997      * Returns the nested BorderLayout for this panel
37998      * @return {Roo.BorderLayout} 
37999      */
38000     getLayout : function(){
38001         return this.layout;
38002     },
38003     
38004      /**
38005      * Adds a xtype elements to the layout of the nested panel
38006      * <pre><code>
38007
38008 panel.addxtype({
38009        xtype : 'ContentPanel',
38010        region: 'west',
38011        items: [ .... ]
38012    }
38013 );
38014
38015 panel.addxtype({
38016         xtype : 'NestedLayoutPanel',
38017         region: 'west',
38018         layout: {
38019            center: { },
38020            west: { }   
38021         },
38022         items : [ ... list of content panels or nested layout panels.. ]
38023    }
38024 );
38025 </code></pre>
38026      * @param {Object} cfg Xtype definition of item to add.
38027      */
38028     addxtype : function(cfg) {
38029         return this.layout.addxtype(cfg);
38030     
38031     }
38032 });/*
38033  * Based on:
38034  * Ext JS Library 1.1.1
38035  * Copyright(c) 2006-2007, Ext JS, LLC.
38036  *
38037  * Originally Released Under LGPL - original licence link has changed is not relivant.
38038  *
38039  * Fork - LGPL
38040  * <script type="text/javascript">
38041  */
38042 /**
38043  * @class Roo.TabPanel
38044  * @extends Roo.util.Observable
38045  * A lightweight tab container.
38046  * <br><br>
38047  * Usage:
38048  * <pre><code>
38049 // basic tabs 1, built from existing content
38050 var tabs = new Roo.TabPanel("tabs1");
38051 tabs.addTab("script", "View Script");
38052 tabs.addTab("markup", "View Markup");
38053 tabs.activate("script");
38054
38055 // more advanced tabs, built from javascript
38056 var jtabs = new Roo.TabPanel("jtabs");
38057 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38058
38059 // set up the UpdateManager
38060 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38061 var updater = tab2.getUpdateManager();
38062 updater.setDefaultUrl("ajax1.htm");
38063 tab2.on('activate', updater.refresh, updater, true);
38064
38065 // Use setUrl for Ajax loading
38066 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38067 tab3.setUrl("ajax2.htm", null, true);
38068
38069 // Disabled tab
38070 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38071 tab4.disable();
38072
38073 jtabs.activate("jtabs-1");
38074  * </code></pre>
38075  * @constructor
38076  * Create a new TabPanel.
38077  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38078  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38079  */
38080 Roo.bootstrap.panel.Tabs = function(config){
38081     /**
38082     * The container element for this TabPanel.
38083     * @type Roo.Element
38084     */
38085     this.el = Roo.get(config.el);
38086     delete config.el;
38087     if(config){
38088         if(typeof config == "boolean"){
38089             this.tabPosition = config ? "bottom" : "top";
38090         }else{
38091             Roo.apply(this, config);
38092         }
38093     }
38094     
38095     if(this.tabPosition == "bottom"){
38096         // if tabs are at the bottom = create the body first.
38097         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38098         this.el.addClass("roo-tabs-bottom");
38099     }
38100     // next create the tabs holders
38101     
38102     if (this.tabPosition == "west"){
38103         
38104         var reg = this.region; // fake it..
38105         while (reg) {
38106             if (!reg.mgr.parent) {
38107                 break;
38108             }
38109             reg = reg.mgr.parent.region;
38110         }
38111         Roo.log("got nest?");
38112         Roo.log(reg);
38113         if (reg.mgr.getRegion('west')) {
38114             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38115             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38116             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38117             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38118             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38119         
38120             
38121         }
38122         
38123         
38124     } else {
38125      
38126         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38127         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38128         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38129         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38130     }
38131     
38132     
38133     if(Roo.isIE){
38134         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38135     }
38136     
38137     // finally - if tabs are at the top, then create the body last..
38138     if(this.tabPosition != "bottom"){
38139         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38140          * @type Roo.Element
38141          */
38142         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38143         this.el.addClass("roo-tabs-top");
38144     }
38145     this.items = [];
38146
38147     this.bodyEl.setStyle("position", "relative");
38148
38149     this.active = null;
38150     this.activateDelegate = this.activate.createDelegate(this);
38151
38152     this.addEvents({
38153         /**
38154          * @event tabchange
38155          * Fires when the active tab changes
38156          * @param {Roo.TabPanel} this
38157          * @param {Roo.TabPanelItem} activePanel The new active tab
38158          */
38159         "tabchange": true,
38160         /**
38161          * @event beforetabchange
38162          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38163          * @param {Roo.TabPanel} this
38164          * @param {Object} e Set cancel to true on this object to cancel the tab change
38165          * @param {Roo.TabPanelItem} tab The tab being changed to
38166          */
38167         "beforetabchange" : true
38168     });
38169
38170     Roo.EventManager.onWindowResize(this.onResize, this);
38171     this.cpad = this.el.getPadding("lr");
38172     this.hiddenCount = 0;
38173
38174
38175     // toolbar on the tabbar support...
38176     if (this.toolbar) {
38177         alert("no toolbar support yet");
38178         this.toolbar  = false;
38179         /*
38180         var tcfg = this.toolbar;
38181         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38182         this.toolbar = new Roo.Toolbar(tcfg);
38183         if (Roo.isSafari) {
38184             var tbl = tcfg.container.child('table', true);
38185             tbl.setAttribute('width', '100%');
38186         }
38187         */
38188         
38189     }
38190    
38191
38192
38193     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38194 };
38195
38196 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38197     /*
38198      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38199      */
38200     tabPosition : "top",
38201     /*
38202      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38203      */
38204     currentTabWidth : 0,
38205     /*
38206      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38207      */
38208     minTabWidth : 40,
38209     /*
38210      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38211      */
38212     maxTabWidth : 250,
38213     /*
38214      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38215      */
38216     preferredTabWidth : 175,
38217     /*
38218      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38219      */
38220     resizeTabs : false,
38221     /*
38222      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38223      */
38224     monitorResize : true,
38225     /*
38226      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38227      */
38228     toolbar : false,  // set by caller..
38229     
38230     region : false, /// set by caller
38231     
38232     disableTooltips : true, // not used yet...
38233
38234     /**
38235      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38236      * @param {String} id The id of the div to use <b>or create</b>
38237      * @param {String} text The text for the tab
38238      * @param {String} content (optional) Content to put in the TabPanelItem body
38239      * @param {Boolean} closable (optional) True to create a close icon on the tab
38240      * @return {Roo.TabPanelItem} The created TabPanelItem
38241      */
38242     addTab : function(id, text, content, closable, tpl)
38243     {
38244         var item = new Roo.bootstrap.panel.TabItem({
38245             panel: this,
38246             id : id,
38247             text : text,
38248             closable : closable,
38249             tpl : tpl
38250         });
38251         this.addTabItem(item);
38252         if(content){
38253             item.setContent(content);
38254         }
38255         return item;
38256     },
38257
38258     /**
38259      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38260      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38261      * @return {Roo.TabPanelItem}
38262      */
38263     getTab : function(id){
38264         return this.items[id];
38265     },
38266
38267     /**
38268      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38269      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38270      */
38271     hideTab : function(id){
38272         var t = this.items[id];
38273         if(!t.isHidden()){
38274            t.setHidden(true);
38275            this.hiddenCount++;
38276            this.autoSizeTabs();
38277         }
38278     },
38279
38280     /**
38281      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38282      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38283      */
38284     unhideTab : function(id){
38285         var t = this.items[id];
38286         if(t.isHidden()){
38287            t.setHidden(false);
38288            this.hiddenCount--;
38289            this.autoSizeTabs();
38290         }
38291     },
38292
38293     /**
38294      * Adds an existing {@link Roo.TabPanelItem}.
38295      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38296      */
38297     addTabItem : function(item)
38298     {
38299         this.items[item.id] = item;
38300         this.items.push(item);
38301         this.autoSizeTabs();
38302       //  if(this.resizeTabs){
38303     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38304   //         this.autoSizeTabs();
38305 //        }else{
38306 //            item.autoSize();
38307        // }
38308     },
38309
38310     /**
38311      * Removes a {@link Roo.TabPanelItem}.
38312      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38313      */
38314     removeTab : function(id){
38315         var items = this.items;
38316         var tab = items[id];
38317         if(!tab) { return; }
38318         var index = items.indexOf(tab);
38319         if(this.active == tab && items.length > 1){
38320             var newTab = this.getNextAvailable(index);
38321             if(newTab) {
38322                 newTab.activate();
38323             }
38324         }
38325         this.stripEl.dom.removeChild(tab.pnode.dom);
38326         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38327             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38328         }
38329         items.splice(index, 1);
38330         delete this.items[tab.id];
38331         tab.fireEvent("close", tab);
38332         tab.purgeListeners();
38333         this.autoSizeTabs();
38334     },
38335
38336     getNextAvailable : function(start){
38337         var items = this.items;
38338         var index = start;
38339         // look for a next tab that will slide over to
38340         // replace the one being removed
38341         while(index < items.length){
38342             var item = items[++index];
38343             if(item && !item.isHidden()){
38344                 return item;
38345             }
38346         }
38347         // if one isn't found select the previous tab (on the left)
38348         index = start;
38349         while(index >= 0){
38350             var item = items[--index];
38351             if(item && !item.isHidden()){
38352                 return item;
38353             }
38354         }
38355         return null;
38356     },
38357
38358     /**
38359      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38360      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38361      */
38362     disableTab : function(id){
38363         var tab = this.items[id];
38364         if(tab && this.active != tab){
38365             tab.disable();
38366         }
38367     },
38368
38369     /**
38370      * Enables a {@link Roo.TabPanelItem} that is disabled.
38371      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38372      */
38373     enableTab : function(id){
38374         var tab = this.items[id];
38375         tab.enable();
38376     },
38377
38378     /**
38379      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38380      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38381      * @return {Roo.TabPanelItem} The TabPanelItem.
38382      */
38383     activate : function(id)
38384     {
38385         //Roo.log('activite:'  + id);
38386         
38387         var tab = this.items[id];
38388         if(!tab){
38389             return null;
38390         }
38391         if(tab == this.active || tab.disabled){
38392             return tab;
38393         }
38394         var e = {};
38395         this.fireEvent("beforetabchange", this, e, tab);
38396         if(e.cancel !== true && !tab.disabled){
38397             if(this.active){
38398                 this.active.hide();
38399             }
38400             this.active = this.items[id];
38401             this.active.show();
38402             this.fireEvent("tabchange", this, this.active);
38403         }
38404         return tab;
38405     },
38406
38407     /**
38408      * Gets the active {@link Roo.TabPanelItem}.
38409      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38410      */
38411     getActiveTab : function(){
38412         return this.active;
38413     },
38414
38415     /**
38416      * Updates the tab body element to fit the height of the container element
38417      * for overflow scrolling
38418      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38419      */
38420     syncHeight : function(targetHeight){
38421         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38422         var bm = this.bodyEl.getMargins();
38423         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38424         this.bodyEl.setHeight(newHeight);
38425         return newHeight;
38426     },
38427
38428     onResize : function(){
38429         if(this.monitorResize){
38430             this.autoSizeTabs();
38431         }
38432     },
38433
38434     /**
38435      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38436      */
38437     beginUpdate : function(){
38438         this.updating = true;
38439     },
38440
38441     /**
38442      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38443      */
38444     endUpdate : function(){
38445         this.updating = false;
38446         this.autoSizeTabs();
38447     },
38448
38449     /**
38450      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38451      */
38452     autoSizeTabs : function()
38453     {
38454         var count = this.items.length;
38455         var vcount = count - this.hiddenCount;
38456         
38457         if (vcount < 2) {
38458             this.stripEl.hide();
38459         } else {
38460             this.stripEl.show();
38461         }
38462         
38463         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38464             return;
38465         }
38466         
38467         
38468         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38469         var availWidth = Math.floor(w / vcount);
38470         var b = this.stripBody;
38471         if(b.getWidth() > w){
38472             var tabs = this.items;
38473             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38474             if(availWidth < this.minTabWidth){
38475                 /*if(!this.sleft){    // incomplete scrolling code
38476                     this.createScrollButtons();
38477                 }
38478                 this.showScroll();
38479                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38480             }
38481         }else{
38482             if(this.currentTabWidth < this.preferredTabWidth){
38483                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38484             }
38485         }
38486     },
38487
38488     /**
38489      * Returns the number of tabs in this TabPanel.
38490      * @return {Number}
38491      */
38492      getCount : function(){
38493          return this.items.length;
38494      },
38495
38496     /**
38497      * Resizes all the tabs to the passed width
38498      * @param {Number} The new width
38499      */
38500     setTabWidth : function(width){
38501         this.currentTabWidth = width;
38502         for(var i = 0, len = this.items.length; i < len; i++) {
38503                 if(!this.items[i].isHidden()) {
38504                 this.items[i].setWidth(width);
38505             }
38506         }
38507     },
38508
38509     /**
38510      * Destroys this TabPanel
38511      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38512      */
38513     destroy : function(removeEl){
38514         Roo.EventManager.removeResizeListener(this.onResize, this);
38515         for(var i = 0, len = this.items.length; i < len; i++){
38516             this.items[i].purgeListeners();
38517         }
38518         if(removeEl === true){
38519             this.el.update("");
38520             this.el.remove();
38521         }
38522     },
38523     
38524     createStrip : function(container)
38525     {
38526         var strip = document.createElement("nav");
38527         strip.className = Roo.bootstrap.version == 4 ?
38528             "navbar-light bg-light" : 
38529             "navbar navbar-default"; //"x-tabs-wrap";
38530         container.appendChild(strip);
38531         return strip;
38532     },
38533     
38534     createStripList : function(strip)
38535     {
38536         // div wrapper for retard IE
38537         // returns the "tr" element.
38538         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38539         //'<div class="x-tabs-strip-wrap">'+
38540           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38541           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38542         return strip.firstChild; //.firstChild.firstChild.firstChild;
38543     },
38544     createBody : function(container)
38545     {
38546         var body = document.createElement("div");
38547         Roo.id(body, "tab-body");
38548         //Roo.fly(body).addClass("x-tabs-body");
38549         Roo.fly(body).addClass("tab-content");
38550         container.appendChild(body);
38551         return body;
38552     },
38553     createItemBody :function(bodyEl, id){
38554         var body = Roo.getDom(id);
38555         if(!body){
38556             body = document.createElement("div");
38557             body.id = id;
38558         }
38559         //Roo.fly(body).addClass("x-tabs-item-body");
38560         Roo.fly(body).addClass("tab-pane");
38561          bodyEl.insertBefore(body, bodyEl.firstChild);
38562         return body;
38563     },
38564     /** @private */
38565     createStripElements :  function(stripEl, text, closable, tpl)
38566     {
38567         var td = document.createElement("li"); // was td..
38568         td.className = 'nav-item';
38569         
38570         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38571         
38572         
38573         stripEl.appendChild(td);
38574         /*if(closable){
38575             td.className = "x-tabs-closable";
38576             if(!this.closeTpl){
38577                 this.closeTpl = new Roo.Template(
38578                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38579                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38580                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38581                 );
38582             }
38583             var el = this.closeTpl.overwrite(td, {"text": text});
38584             var close = el.getElementsByTagName("div")[0];
38585             var inner = el.getElementsByTagName("em")[0];
38586             return {"el": el, "close": close, "inner": inner};
38587         } else {
38588         */
38589         // not sure what this is..
38590 //            if(!this.tabTpl){
38591                 //this.tabTpl = new Roo.Template(
38592                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38593                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38594                 //);
38595 //                this.tabTpl = new Roo.Template(
38596 //                   '<a href="#">' +
38597 //                   '<span unselectable="on"' +
38598 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38599 //                            ' >{text}</span></a>'
38600 //                );
38601 //                
38602 //            }
38603
38604
38605             var template = tpl || this.tabTpl || false;
38606             
38607             if(!template){
38608                 template =  new Roo.Template(
38609                         Roo.bootstrap.version == 4 ? 
38610                             (
38611                                 '<a class="nav-link" href="#" unselectable="on"' +
38612                                      (this.disableTooltips ? '' : ' title="{text}"') +
38613                                      ' >{text}</a>'
38614                             ) : (
38615                                 '<a class="nav-link" href="#">' +
38616                                 '<span unselectable="on"' +
38617                                          (this.disableTooltips ? '' : ' title="{text}"') +
38618                                     ' >{text}</span></a>'
38619                             )
38620                 );
38621             }
38622             
38623             switch (typeof(template)) {
38624                 case 'object' :
38625                     break;
38626                 case 'string' :
38627                     template = new Roo.Template(template);
38628                     break;
38629                 default :
38630                     break;
38631             }
38632             
38633             var el = template.overwrite(td, {"text": text});
38634             
38635             var inner = el.getElementsByTagName("span")[0];
38636             
38637             return {"el": el, "inner": inner};
38638             
38639     }
38640         
38641     
38642 });
38643
38644 /**
38645  * @class Roo.TabPanelItem
38646  * @extends Roo.util.Observable
38647  * Represents an individual item (tab plus body) in a TabPanel.
38648  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38649  * @param {String} id The id of this TabPanelItem
38650  * @param {String} text The text for the tab of this TabPanelItem
38651  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38652  */
38653 Roo.bootstrap.panel.TabItem = function(config){
38654     /**
38655      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38656      * @type Roo.TabPanel
38657      */
38658     this.tabPanel = config.panel;
38659     /**
38660      * The id for this TabPanelItem
38661      * @type String
38662      */
38663     this.id = config.id;
38664     /** @private */
38665     this.disabled = false;
38666     /** @private */
38667     this.text = config.text;
38668     /** @private */
38669     this.loaded = false;
38670     this.closable = config.closable;
38671
38672     /**
38673      * The body element for this TabPanelItem.
38674      * @type Roo.Element
38675      */
38676     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38677     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38678     this.bodyEl.setStyle("display", "block");
38679     this.bodyEl.setStyle("zoom", "1");
38680     //this.hideAction();
38681
38682     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38683     /** @private */
38684     this.el = Roo.get(els.el);
38685     this.inner = Roo.get(els.inner, true);
38686      this.textEl = Roo.bootstrap.version == 4 ?
38687         this.el : Roo.get(this.el.dom.firstChild, true);
38688
38689     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38690     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38691
38692     
38693 //    this.el.on("mousedown", this.onTabMouseDown, this);
38694     this.el.on("click", this.onTabClick, this);
38695     /** @private */
38696     if(config.closable){
38697         var c = Roo.get(els.close, true);
38698         c.dom.title = this.closeText;
38699         c.addClassOnOver("close-over");
38700         c.on("click", this.closeClick, this);
38701      }
38702
38703     this.addEvents({
38704          /**
38705          * @event activate
38706          * Fires when this tab becomes the active tab.
38707          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38708          * @param {Roo.TabPanelItem} this
38709          */
38710         "activate": true,
38711         /**
38712          * @event beforeclose
38713          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38714          * @param {Roo.TabPanelItem} this
38715          * @param {Object} e Set cancel to true on this object to cancel the close.
38716          */
38717         "beforeclose": true,
38718         /**
38719          * @event close
38720          * Fires when this tab is closed.
38721          * @param {Roo.TabPanelItem} this
38722          */
38723          "close": true,
38724         /**
38725          * @event deactivate
38726          * Fires when this tab is no longer the active tab.
38727          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38728          * @param {Roo.TabPanelItem} this
38729          */
38730          "deactivate" : true
38731     });
38732     this.hidden = false;
38733
38734     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38735 };
38736
38737 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38738            {
38739     purgeListeners : function(){
38740        Roo.util.Observable.prototype.purgeListeners.call(this);
38741        this.el.removeAllListeners();
38742     },
38743     /**
38744      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38745      */
38746     show : function(){
38747         this.status_node.addClass("active");
38748         this.showAction();
38749         if(Roo.isOpera){
38750             this.tabPanel.stripWrap.repaint();
38751         }
38752         this.fireEvent("activate", this.tabPanel, this);
38753     },
38754
38755     /**
38756      * Returns true if this tab is the active tab.
38757      * @return {Boolean}
38758      */
38759     isActive : function(){
38760         return this.tabPanel.getActiveTab() == this;
38761     },
38762
38763     /**
38764      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38765      */
38766     hide : function(){
38767         this.status_node.removeClass("active");
38768         this.hideAction();
38769         this.fireEvent("deactivate", this.tabPanel, this);
38770     },
38771
38772     hideAction : function(){
38773         this.bodyEl.hide();
38774         this.bodyEl.setStyle("position", "absolute");
38775         this.bodyEl.setLeft("-20000px");
38776         this.bodyEl.setTop("-20000px");
38777     },
38778
38779     showAction : function(){
38780         this.bodyEl.setStyle("position", "relative");
38781         this.bodyEl.setTop("");
38782         this.bodyEl.setLeft("");
38783         this.bodyEl.show();
38784     },
38785
38786     /**
38787      * Set the tooltip for the tab.
38788      * @param {String} tooltip The tab's tooltip
38789      */
38790     setTooltip : function(text){
38791         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38792             this.textEl.dom.qtip = text;
38793             this.textEl.dom.removeAttribute('title');
38794         }else{
38795             this.textEl.dom.title = text;
38796         }
38797     },
38798
38799     onTabClick : function(e){
38800         e.preventDefault();
38801         this.tabPanel.activate(this.id);
38802     },
38803
38804     onTabMouseDown : function(e){
38805         e.preventDefault();
38806         this.tabPanel.activate(this.id);
38807     },
38808 /*
38809     getWidth : function(){
38810         return this.inner.getWidth();
38811     },
38812
38813     setWidth : function(width){
38814         var iwidth = width - this.linode.getPadding("lr");
38815         this.inner.setWidth(iwidth);
38816         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38817         this.linode.setWidth(width);
38818     },
38819 */
38820     /**
38821      * Show or hide the tab
38822      * @param {Boolean} hidden True to hide or false to show.
38823      */
38824     setHidden : function(hidden){
38825         this.hidden = hidden;
38826         this.linode.setStyle("display", hidden ? "none" : "");
38827     },
38828
38829     /**
38830      * Returns true if this tab is "hidden"
38831      * @return {Boolean}
38832      */
38833     isHidden : function(){
38834         return this.hidden;
38835     },
38836
38837     /**
38838      * Returns the text for this tab
38839      * @return {String}
38840      */
38841     getText : function(){
38842         return this.text;
38843     },
38844     /*
38845     autoSize : function(){
38846         //this.el.beginMeasure();
38847         this.textEl.setWidth(1);
38848         /*
38849          *  #2804 [new] Tabs in Roojs
38850          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38851          */
38852         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38853         //this.el.endMeasure();
38854     //},
38855
38856     /**
38857      * Sets the text for the tab (Note: this also sets the tooltip text)
38858      * @param {String} text The tab's text and tooltip
38859      */
38860     setText : function(text){
38861         this.text = text;
38862         this.textEl.update(text);
38863         this.setTooltip(text);
38864         //if(!this.tabPanel.resizeTabs){
38865         //    this.autoSize();
38866         //}
38867     },
38868     /**
38869      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38870      */
38871     activate : function(){
38872         this.tabPanel.activate(this.id);
38873     },
38874
38875     /**
38876      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38877      */
38878     disable : function(){
38879         if(this.tabPanel.active != this){
38880             this.disabled = true;
38881             this.status_node.addClass("disabled");
38882         }
38883     },
38884
38885     /**
38886      * Enables this TabPanelItem if it was previously disabled.
38887      */
38888     enable : function(){
38889         this.disabled = false;
38890         this.status_node.removeClass("disabled");
38891     },
38892
38893     /**
38894      * Sets the content for this TabPanelItem.
38895      * @param {String} content The content
38896      * @param {Boolean} loadScripts true to look for and load scripts
38897      */
38898     setContent : function(content, loadScripts){
38899         this.bodyEl.update(content, loadScripts);
38900     },
38901
38902     /**
38903      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38904      * @return {Roo.UpdateManager} The UpdateManager
38905      */
38906     getUpdateManager : function(){
38907         return this.bodyEl.getUpdateManager();
38908     },
38909
38910     /**
38911      * Set a URL to be used to load the content for this TabPanelItem.
38912      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38913      * @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)
38914      * @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)
38915      * @return {Roo.UpdateManager} The UpdateManager
38916      */
38917     setUrl : function(url, params, loadOnce){
38918         if(this.refreshDelegate){
38919             this.un('activate', this.refreshDelegate);
38920         }
38921         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38922         this.on("activate", this.refreshDelegate);
38923         return this.bodyEl.getUpdateManager();
38924     },
38925
38926     /** @private */
38927     _handleRefresh : function(url, params, loadOnce){
38928         if(!loadOnce || !this.loaded){
38929             var updater = this.bodyEl.getUpdateManager();
38930             updater.update(url, params, this._setLoaded.createDelegate(this));
38931         }
38932     },
38933
38934     /**
38935      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38936      *   Will fail silently if the setUrl method has not been called.
38937      *   This does not activate the panel, just updates its content.
38938      */
38939     refresh : function(){
38940         if(this.refreshDelegate){
38941            this.loaded = false;
38942            this.refreshDelegate();
38943         }
38944     },
38945
38946     /** @private */
38947     _setLoaded : function(){
38948         this.loaded = true;
38949     },
38950
38951     /** @private */
38952     closeClick : function(e){
38953         var o = {};
38954         e.stopEvent();
38955         this.fireEvent("beforeclose", this, o);
38956         if(o.cancel !== true){
38957             this.tabPanel.removeTab(this.id);
38958         }
38959     },
38960     /**
38961      * The text displayed in the tooltip for the close icon.
38962      * @type String
38963      */
38964     closeText : "Close this tab"
38965 });
38966 /**
38967 *    This script refer to:
38968 *    Title: International Telephone Input
38969 *    Author: Jack O'Connor
38970 *    Code version:  v12.1.12
38971 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38972 **/
38973
38974 Roo.bootstrap.PhoneInputData = function() {
38975     var d = [
38976       [
38977         "Afghanistan (‫افغانستان‬‎)",
38978         "af",
38979         "93"
38980       ],
38981       [
38982         "Albania (Shqipëri)",
38983         "al",
38984         "355"
38985       ],
38986       [
38987         "Algeria (‫الجزائر‬‎)",
38988         "dz",
38989         "213"
38990       ],
38991       [
38992         "American Samoa",
38993         "as",
38994         "1684"
38995       ],
38996       [
38997         "Andorra",
38998         "ad",
38999         "376"
39000       ],
39001       [
39002         "Angola",
39003         "ao",
39004         "244"
39005       ],
39006       [
39007         "Anguilla",
39008         "ai",
39009         "1264"
39010       ],
39011       [
39012         "Antigua and Barbuda",
39013         "ag",
39014         "1268"
39015       ],
39016       [
39017         "Argentina",
39018         "ar",
39019         "54"
39020       ],
39021       [
39022         "Armenia (Հայաստան)",
39023         "am",
39024         "374"
39025       ],
39026       [
39027         "Aruba",
39028         "aw",
39029         "297"
39030       ],
39031       [
39032         "Australia",
39033         "au",
39034         "61",
39035         0
39036       ],
39037       [
39038         "Austria (Österreich)",
39039         "at",
39040         "43"
39041       ],
39042       [
39043         "Azerbaijan (Azərbaycan)",
39044         "az",
39045         "994"
39046       ],
39047       [
39048         "Bahamas",
39049         "bs",
39050         "1242"
39051       ],
39052       [
39053         "Bahrain (‫البحرين‬‎)",
39054         "bh",
39055         "973"
39056       ],
39057       [
39058         "Bangladesh (বাংলাদেশ)",
39059         "bd",
39060         "880"
39061       ],
39062       [
39063         "Barbados",
39064         "bb",
39065         "1246"
39066       ],
39067       [
39068         "Belarus (Беларусь)",
39069         "by",
39070         "375"
39071       ],
39072       [
39073         "Belgium (België)",
39074         "be",
39075         "32"
39076       ],
39077       [
39078         "Belize",
39079         "bz",
39080         "501"
39081       ],
39082       [
39083         "Benin (Bénin)",
39084         "bj",
39085         "229"
39086       ],
39087       [
39088         "Bermuda",
39089         "bm",
39090         "1441"
39091       ],
39092       [
39093         "Bhutan (འབྲུག)",
39094         "bt",
39095         "975"
39096       ],
39097       [
39098         "Bolivia",
39099         "bo",
39100         "591"
39101       ],
39102       [
39103         "Bosnia and Herzegovina (Босна и Херцеговина)",
39104         "ba",
39105         "387"
39106       ],
39107       [
39108         "Botswana",
39109         "bw",
39110         "267"
39111       ],
39112       [
39113         "Brazil (Brasil)",
39114         "br",
39115         "55"
39116       ],
39117       [
39118         "British Indian Ocean Territory",
39119         "io",
39120         "246"
39121       ],
39122       [
39123         "British Virgin Islands",
39124         "vg",
39125         "1284"
39126       ],
39127       [
39128         "Brunei",
39129         "bn",
39130         "673"
39131       ],
39132       [
39133         "Bulgaria (България)",
39134         "bg",
39135         "359"
39136       ],
39137       [
39138         "Burkina Faso",
39139         "bf",
39140         "226"
39141       ],
39142       [
39143         "Burundi (Uburundi)",
39144         "bi",
39145         "257"
39146       ],
39147       [
39148         "Cambodia (កម្ពុជា)",
39149         "kh",
39150         "855"
39151       ],
39152       [
39153         "Cameroon (Cameroun)",
39154         "cm",
39155         "237"
39156       ],
39157       [
39158         "Canada",
39159         "ca",
39160         "1",
39161         1,
39162         ["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"]
39163       ],
39164       [
39165         "Cape Verde (Kabu Verdi)",
39166         "cv",
39167         "238"
39168       ],
39169       [
39170         "Caribbean Netherlands",
39171         "bq",
39172         "599",
39173         1
39174       ],
39175       [
39176         "Cayman Islands",
39177         "ky",
39178         "1345"
39179       ],
39180       [
39181         "Central African Republic (République centrafricaine)",
39182         "cf",
39183         "236"
39184       ],
39185       [
39186         "Chad (Tchad)",
39187         "td",
39188         "235"
39189       ],
39190       [
39191         "Chile",
39192         "cl",
39193         "56"
39194       ],
39195       [
39196         "China (中国)",
39197         "cn",
39198         "86"
39199       ],
39200       [
39201         "Christmas Island",
39202         "cx",
39203         "61",
39204         2
39205       ],
39206       [
39207         "Cocos (Keeling) Islands",
39208         "cc",
39209         "61",
39210         1
39211       ],
39212       [
39213         "Colombia",
39214         "co",
39215         "57"
39216       ],
39217       [
39218         "Comoros (‫جزر القمر‬‎)",
39219         "km",
39220         "269"
39221       ],
39222       [
39223         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39224         "cd",
39225         "243"
39226       ],
39227       [
39228         "Congo (Republic) (Congo-Brazzaville)",
39229         "cg",
39230         "242"
39231       ],
39232       [
39233         "Cook Islands",
39234         "ck",
39235         "682"
39236       ],
39237       [
39238         "Costa Rica",
39239         "cr",
39240         "506"
39241       ],
39242       [
39243         "Côte d’Ivoire",
39244         "ci",
39245         "225"
39246       ],
39247       [
39248         "Croatia (Hrvatska)",
39249         "hr",
39250         "385"
39251       ],
39252       [
39253         "Cuba",
39254         "cu",
39255         "53"
39256       ],
39257       [
39258         "Curaçao",
39259         "cw",
39260         "599",
39261         0
39262       ],
39263       [
39264         "Cyprus (Κύπρος)",
39265         "cy",
39266         "357"
39267       ],
39268       [
39269         "Czech Republic (Česká republika)",
39270         "cz",
39271         "420"
39272       ],
39273       [
39274         "Denmark (Danmark)",
39275         "dk",
39276         "45"
39277       ],
39278       [
39279         "Djibouti",
39280         "dj",
39281         "253"
39282       ],
39283       [
39284         "Dominica",
39285         "dm",
39286         "1767"
39287       ],
39288       [
39289         "Dominican Republic (República Dominicana)",
39290         "do",
39291         "1",
39292         2,
39293         ["809", "829", "849"]
39294       ],
39295       [
39296         "Ecuador",
39297         "ec",
39298         "593"
39299       ],
39300       [
39301         "Egypt (‫مصر‬‎)",
39302         "eg",
39303         "20"
39304       ],
39305       [
39306         "El Salvador",
39307         "sv",
39308         "503"
39309       ],
39310       [
39311         "Equatorial Guinea (Guinea Ecuatorial)",
39312         "gq",
39313         "240"
39314       ],
39315       [
39316         "Eritrea",
39317         "er",
39318         "291"
39319       ],
39320       [
39321         "Estonia (Eesti)",
39322         "ee",
39323         "372"
39324       ],
39325       [
39326         "Ethiopia",
39327         "et",
39328         "251"
39329       ],
39330       [
39331         "Falkland Islands (Islas Malvinas)",
39332         "fk",
39333         "500"
39334       ],
39335       [
39336         "Faroe Islands (Føroyar)",
39337         "fo",
39338         "298"
39339       ],
39340       [
39341         "Fiji",
39342         "fj",
39343         "679"
39344       ],
39345       [
39346         "Finland (Suomi)",
39347         "fi",
39348         "358",
39349         0
39350       ],
39351       [
39352         "France",
39353         "fr",
39354         "33"
39355       ],
39356       [
39357         "French Guiana (Guyane française)",
39358         "gf",
39359         "594"
39360       ],
39361       [
39362         "French Polynesia (Polynésie française)",
39363         "pf",
39364         "689"
39365       ],
39366       [
39367         "Gabon",
39368         "ga",
39369         "241"
39370       ],
39371       [
39372         "Gambia",
39373         "gm",
39374         "220"
39375       ],
39376       [
39377         "Georgia (საქართველო)",
39378         "ge",
39379         "995"
39380       ],
39381       [
39382         "Germany (Deutschland)",
39383         "de",
39384         "49"
39385       ],
39386       [
39387         "Ghana (Gaana)",
39388         "gh",
39389         "233"
39390       ],
39391       [
39392         "Gibraltar",
39393         "gi",
39394         "350"
39395       ],
39396       [
39397         "Greece (Ελλάδα)",
39398         "gr",
39399         "30"
39400       ],
39401       [
39402         "Greenland (Kalaallit Nunaat)",
39403         "gl",
39404         "299"
39405       ],
39406       [
39407         "Grenada",
39408         "gd",
39409         "1473"
39410       ],
39411       [
39412         "Guadeloupe",
39413         "gp",
39414         "590",
39415         0
39416       ],
39417       [
39418         "Guam",
39419         "gu",
39420         "1671"
39421       ],
39422       [
39423         "Guatemala",
39424         "gt",
39425         "502"
39426       ],
39427       [
39428         "Guernsey",
39429         "gg",
39430         "44",
39431         1
39432       ],
39433       [
39434         "Guinea (Guinée)",
39435         "gn",
39436         "224"
39437       ],
39438       [
39439         "Guinea-Bissau (Guiné Bissau)",
39440         "gw",
39441         "245"
39442       ],
39443       [
39444         "Guyana",
39445         "gy",
39446         "592"
39447       ],
39448       [
39449         "Haiti",
39450         "ht",
39451         "509"
39452       ],
39453       [
39454         "Honduras",
39455         "hn",
39456         "504"
39457       ],
39458       [
39459         "Hong Kong (香港)",
39460         "hk",
39461         "852"
39462       ],
39463       [
39464         "Hungary (Magyarország)",
39465         "hu",
39466         "36"
39467       ],
39468       [
39469         "Iceland (Ísland)",
39470         "is",
39471         "354"
39472       ],
39473       [
39474         "India (भारत)",
39475         "in",
39476         "91"
39477       ],
39478       [
39479         "Indonesia",
39480         "id",
39481         "62"
39482       ],
39483       [
39484         "Iran (‫ایران‬‎)",
39485         "ir",
39486         "98"
39487       ],
39488       [
39489         "Iraq (‫العراق‬‎)",
39490         "iq",
39491         "964"
39492       ],
39493       [
39494         "Ireland",
39495         "ie",
39496         "353"
39497       ],
39498       [
39499         "Isle of Man",
39500         "im",
39501         "44",
39502         2
39503       ],
39504       [
39505         "Israel (‫ישראל‬‎)",
39506         "il",
39507         "972"
39508       ],
39509       [
39510         "Italy (Italia)",
39511         "it",
39512         "39",
39513         0
39514       ],
39515       [
39516         "Jamaica",
39517         "jm",
39518         "1876"
39519       ],
39520       [
39521         "Japan (日本)",
39522         "jp",
39523         "81"
39524       ],
39525       [
39526         "Jersey",
39527         "je",
39528         "44",
39529         3
39530       ],
39531       [
39532         "Jordan (‫الأردن‬‎)",
39533         "jo",
39534         "962"
39535       ],
39536       [
39537         "Kazakhstan (Казахстан)",
39538         "kz",
39539         "7",
39540         1
39541       ],
39542       [
39543         "Kenya",
39544         "ke",
39545         "254"
39546       ],
39547       [
39548         "Kiribati",
39549         "ki",
39550         "686"
39551       ],
39552       [
39553         "Kosovo",
39554         "xk",
39555         "383"
39556       ],
39557       [
39558         "Kuwait (‫الكويت‬‎)",
39559         "kw",
39560         "965"
39561       ],
39562       [
39563         "Kyrgyzstan (Кыргызстан)",
39564         "kg",
39565         "996"
39566       ],
39567       [
39568         "Laos (ລາວ)",
39569         "la",
39570         "856"
39571       ],
39572       [
39573         "Latvia (Latvija)",
39574         "lv",
39575         "371"
39576       ],
39577       [
39578         "Lebanon (‫لبنان‬‎)",
39579         "lb",
39580         "961"
39581       ],
39582       [
39583         "Lesotho",
39584         "ls",
39585         "266"
39586       ],
39587       [
39588         "Liberia",
39589         "lr",
39590         "231"
39591       ],
39592       [
39593         "Libya (‫ليبيا‬‎)",
39594         "ly",
39595         "218"
39596       ],
39597       [
39598         "Liechtenstein",
39599         "li",
39600         "423"
39601       ],
39602       [
39603         "Lithuania (Lietuva)",
39604         "lt",
39605         "370"
39606       ],
39607       [
39608         "Luxembourg",
39609         "lu",
39610         "352"
39611       ],
39612       [
39613         "Macau (澳門)",
39614         "mo",
39615         "853"
39616       ],
39617       [
39618         "Macedonia (FYROM) (Македонија)",
39619         "mk",
39620         "389"
39621       ],
39622       [
39623         "Madagascar (Madagasikara)",
39624         "mg",
39625         "261"
39626       ],
39627       [
39628         "Malawi",
39629         "mw",
39630         "265"
39631       ],
39632       [
39633         "Malaysia",
39634         "my",
39635         "60"
39636       ],
39637       [
39638         "Maldives",
39639         "mv",
39640         "960"
39641       ],
39642       [
39643         "Mali",
39644         "ml",
39645         "223"
39646       ],
39647       [
39648         "Malta",
39649         "mt",
39650         "356"
39651       ],
39652       [
39653         "Marshall Islands",
39654         "mh",
39655         "692"
39656       ],
39657       [
39658         "Martinique",
39659         "mq",
39660         "596"
39661       ],
39662       [
39663         "Mauritania (‫موريتانيا‬‎)",
39664         "mr",
39665         "222"
39666       ],
39667       [
39668         "Mauritius (Moris)",
39669         "mu",
39670         "230"
39671       ],
39672       [
39673         "Mayotte",
39674         "yt",
39675         "262",
39676         1
39677       ],
39678       [
39679         "Mexico (México)",
39680         "mx",
39681         "52"
39682       ],
39683       [
39684         "Micronesia",
39685         "fm",
39686         "691"
39687       ],
39688       [
39689         "Moldova (Republica Moldova)",
39690         "md",
39691         "373"
39692       ],
39693       [
39694         "Monaco",
39695         "mc",
39696         "377"
39697       ],
39698       [
39699         "Mongolia (Монгол)",
39700         "mn",
39701         "976"
39702       ],
39703       [
39704         "Montenegro (Crna Gora)",
39705         "me",
39706         "382"
39707       ],
39708       [
39709         "Montserrat",
39710         "ms",
39711         "1664"
39712       ],
39713       [
39714         "Morocco (‫المغرب‬‎)",
39715         "ma",
39716         "212",
39717         0
39718       ],
39719       [
39720         "Mozambique (Moçambique)",
39721         "mz",
39722         "258"
39723       ],
39724       [
39725         "Myanmar (Burma) (မြန်မာ)",
39726         "mm",
39727         "95"
39728       ],
39729       [
39730         "Namibia (Namibië)",
39731         "na",
39732         "264"
39733       ],
39734       [
39735         "Nauru",
39736         "nr",
39737         "674"
39738       ],
39739       [
39740         "Nepal (नेपाल)",
39741         "np",
39742         "977"
39743       ],
39744       [
39745         "Netherlands (Nederland)",
39746         "nl",
39747         "31"
39748       ],
39749       [
39750         "New Caledonia (Nouvelle-Calédonie)",
39751         "nc",
39752         "687"
39753       ],
39754       [
39755         "New Zealand",
39756         "nz",
39757         "64"
39758       ],
39759       [
39760         "Nicaragua",
39761         "ni",
39762         "505"
39763       ],
39764       [
39765         "Niger (Nijar)",
39766         "ne",
39767         "227"
39768       ],
39769       [
39770         "Nigeria",
39771         "ng",
39772         "234"
39773       ],
39774       [
39775         "Niue",
39776         "nu",
39777         "683"
39778       ],
39779       [
39780         "Norfolk Island",
39781         "nf",
39782         "672"
39783       ],
39784       [
39785         "North Korea (조선 민주주의 인민 공화국)",
39786         "kp",
39787         "850"
39788       ],
39789       [
39790         "Northern Mariana Islands",
39791         "mp",
39792         "1670"
39793       ],
39794       [
39795         "Norway (Norge)",
39796         "no",
39797         "47",
39798         0
39799       ],
39800       [
39801         "Oman (‫عُمان‬‎)",
39802         "om",
39803         "968"
39804       ],
39805       [
39806         "Pakistan (‫پاکستان‬‎)",
39807         "pk",
39808         "92"
39809       ],
39810       [
39811         "Palau",
39812         "pw",
39813         "680"
39814       ],
39815       [
39816         "Palestine (‫فلسطين‬‎)",
39817         "ps",
39818         "970"
39819       ],
39820       [
39821         "Panama (Panamá)",
39822         "pa",
39823         "507"
39824       ],
39825       [
39826         "Papua New Guinea",
39827         "pg",
39828         "675"
39829       ],
39830       [
39831         "Paraguay",
39832         "py",
39833         "595"
39834       ],
39835       [
39836         "Peru (Perú)",
39837         "pe",
39838         "51"
39839       ],
39840       [
39841         "Philippines",
39842         "ph",
39843         "63"
39844       ],
39845       [
39846         "Poland (Polska)",
39847         "pl",
39848         "48"
39849       ],
39850       [
39851         "Portugal",
39852         "pt",
39853         "351"
39854       ],
39855       [
39856         "Puerto Rico",
39857         "pr",
39858         "1",
39859         3,
39860         ["787", "939"]
39861       ],
39862       [
39863         "Qatar (‫قطر‬‎)",
39864         "qa",
39865         "974"
39866       ],
39867       [
39868         "Réunion (La Réunion)",
39869         "re",
39870         "262",
39871         0
39872       ],
39873       [
39874         "Romania (România)",
39875         "ro",
39876         "40"
39877       ],
39878       [
39879         "Russia (Россия)",
39880         "ru",
39881         "7",
39882         0
39883       ],
39884       [
39885         "Rwanda",
39886         "rw",
39887         "250"
39888       ],
39889       [
39890         "Saint Barthélemy",
39891         "bl",
39892         "590",
39893         1
39894       ],
39895       [
39896         "Saint Helena",
39897         "sh",
39898         "290"
39899       ],
39900       [
39901         "Saint Kitts and Nevis",
39902         "kn",
39903         "1869"
39904       ],
39905       [
39906         "Saint Lucia",
39907         "lc",
39908         "1758"
39909       ],
39910       [
39911         "Saint Martin (Saint-Martin (partie française))",
39912         "mf",
39913         "590",
39914         2
39915       ],
39916       [
39917         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39918         "pm",
39919         "508"
39920       ],
39921       [
39922         "Saint Vincent and the Grenadines",
39923         "vc",
39924         "1784"
39925       ],
39926       [
39927         "Samoa",
39928         "ws",
39929         "685"
39930       ],
39931       [
39932         "San Marino",
39933         "sm",
39934         "378"
39935       ],
39936       [
39937         "São Tomé and Príncipe (São Tomé e Príncipe)",
39938         "st",
39939         "239"
39940       ],
39941       [
39942         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39943         "sa",
39944         "966"
39945       ],
39946       [
39947         "Senegal (Sénégal)",
39948         "sn",
39949         "221"
39950       ],
39951       [
39952         "Serbia (Србија)",
39953         "rs",
39954         "381"
39955       ],
39956       [
39957         "Seychelles",
39958         "sc",
39959         "248"
39960       ],
39961       [
39962         "Sierra Leone",
39963         "sl",
39964         "232"
39965       ],
39966       [
39967         "Singapore",
39968         "sg",
39969         "65"
39970       ],
39971       [
39972         "Sint Maarten",
39973         "sx",
39974         "1721"
39975       ],
39976       [
39977         "Slovakia (Slovensko)",
39978         "sk",
39979         "421"
39980       ],
39981       [
39982         "Slovenia (Slovenija)",
39983         "si",
39984         "386"
39985       ],
39986       [
39987         "Solomon Islands",
39988         "sb",
39989         "677"
39990       ],
39991       [
39992         "Somalia (Soomaaliya)",
39993         "so",
39994         "252"
39995       ],
39996       [
39997         "South Africa",
39998         "za",
39999         "27"
40000       ],
40001       [
40002         "South Korea (대한민국)",
40003         "kr",
40004         "82"
40005       ],
40006       [
40007         "South Sudan (‫جنوب السودان‬‎)",
40008         "ss",
40009         "211"
40010       ],
40011       [
40012         "Spain (España)",
40013         "es",
40014         "34"
40015       ],
40016       [
40017         "Sri Lanka (ශ්‍රී ලංකාව)",
40018         "lk",
40019         "94"
40020       ],
40021       [
40022         "Sudan (‫السودان‬‎)",
40023         "sd",
40024         "249"
40025       ],
40026       [
40027         "Suriname",
40028         "sr",
40029         "597"
40030       ],
40031       [
40032         "Svalbard and Jan Mayen",
40033         "sj",
40034         "47",
40035         1
40036       ],
40037       [
40038         "Swaziland",
40039         "sz",
40040         "268"
40041       ],
40042       [
40043         "Sweden (Sverige)",
40044         "se",
40045         "46"
40046       ],
40047       [
40048         "Switzerland (Schweiz)",
40049         "ch",
40050         "41"
40051       ],
40052       [
40053         "Syria (‫سوريا‬‎)",
40054         "sy",
40055         "963"
40056       ],
40057       [
40058         "Taiwan (台灣)",
40059         "tw",
40060         "886"
40061       ],
40062       [
40063         "Tajikistan",
40064         "tj",
40065         "992"
40066       ],
40067       [
40068         "Tanzania",
40069         "tz",
40070         "255"
40071       ],
40072       [
40073         "Thailand (ไทย)",
40074         "th",
40075         "66"
40076       ],
40077       [
40078         "Timor-Leste",
40079         "tl",
40080         "670"
40081       ],
40082       [
40083         "Togo",
40084         "tg",
40085         "228"
40086       ],
40087       [
40088         "Tokelau",
40089         "tk",
40090         "690"
40091       ],
40092       [
40093         "Tonga",
40094         "to",
40095         "676"
40096       ],
40097       [
40098         "Trinidad and Tobago",
40099         "tt",
40100         "1868"
40101       ],
40102       [
40103         "Tunisia (‫تونس‬‎)",
40104         "tn",
40105         "216"
40106       ],
40107       [
40108         "Turkey (Türkiye)",
40109         "tr",
40110         "90"
40111       ],
40112       [
40113         "Turkmenistan",
40114         "tm",
40115         "993"
40116       ],
40117       [
40118         "Turks and Caicos Islands",
40119         "tc",
40120         "1649"
40121       ],
40122       [
40123         "Tuvalu",
40124         "tv",
40125         "688"
40126       ],
40127       [
40128         "U.S. Virgin Islands",
40129         "vi",
40130         "1340"
40131       ],
40132       [
40133         "Uganda",
40134         "ug",
40135         "256"
40136       ],
40137       [
40138         "Ukraine (Україна)",
40139         "ua",
40140         "380"
40141       ],
40142       [
40143         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40144         "ae",
40145         "971"
40146       ],
40147       [
40148         "United Kingdom",
40149         "gb",
40150         "44",
40151         0
40152       ],
40153       [
40154         "United States",
40155         "us",
40156         "1",
40157         0
40158       ],
40159       [
40160         "Uruguay",
40161         "uy",
40162         "598"
40163       ],
40164       [
40165         "Uzbekistan (Oʻzbekiston)",
40166         "uz",
40167         "998"
40168       ],
40169       [
40170         "Vanuatu",
40171         "vu",
40172         "678"
40173       ],
40174       [
40175         "Vatican City (Città del Vaticano)",
40176         "va",
40177         "39",
40178         1
40179       ],
40180       [
40181         "Venezuela",
40182         "ve",
40183         "58"
40184       ],
40185       [
40186         "Vietnam (Việt Nam)",
40187         "vn",
40188         "84"
40189       ],
40190       [
40191         "Wallis and Futuna (Wallis-et-Futuna)",
40192         "wf",
40193         "681"
40194       ],
40195       [
40196         "Western Sahara (‫الصحراء الغربية‬‎)",
40197         "eh",
40198         "212",
40199         1
40200       ],
40201       [
40202         "Yemen (‫اليمن‬‎)",
40203         "ye",
40204         "967"
40205       ],
40206       [
40207         "Zambia",
40208         "zm",
40209         "260"
40210       ],
40211       [
40212         "Zimbabwe",
40213         "zw",
40214         "263"
40215       ],
40216       [
40217         "Åland Islands",
40218         "ax",
40219         "358",
40220         1
40221       ]
40222   ];
40223   
40224   return d;
40225 }/**
40226 *    This script refer to:
40227 *    Title: International Telephone Input
40228 *    Author: Jack O'Connor
40229 *    Code version:  v12.1.12
40230 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40231 **/
40232
40233 /**
40234  * @class Roo.bootstrap.PhoneInput
40235  * @extends Roo.bootstrap.TriggerField
40236  * An input with International dial-code selection
40237  
40238  * @cfg {String} defaultDialCode default '+852'
40239  * @cfg {Array} preferedCountries default []
40240   
40241  * @constructor
40242  * Create a new PhoneInput.
40243  * @param {Object} config Configuration options
40244  */
40245
40246 Roo.bootstrap.PhoneInput = function(config) {
40247     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40248 };
40249
40250 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40251         
40252         listWidth: undefined,
40253         
40254         selectedClass: 'active',
40255         
40256         invalidClass : "has-warning",
40257         
40258         validClass: 'has-success',
40259         
40260         allowed: '0123456789',
40261         
40262         max_length: 15,
40263         
40264         /**
40265          * @cfg {String} defaultDialCode The default dial code when initializing the input
40266          */
40267         defaultDialCode: '+852',
40268         
40269         /**
40270          * @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
40271          */
40272         preferedCountries: false,
40273         
40274         getAutoCreate : function()
40275         {
40276             var data = Roo.bootstrap.PhoneInputData();
40277             var align = this.labelAlign || this.parentLabelAlign();
40278             var id = Roo.id();
40279             
40280             this.allCountries = [];
40281             this.dialCodeMapping = [];
40282             
40283             for (var i = 0; i < data.length; i++) {
40284               var c = data[i];
40285               this.allCountries[i] = {
40286                 name: c[0],
40287                 iso2: c[1],
40288                 dialCode: c[2],
40289                 priority: c[3] || 0,
40290                 areaCodes: c[4] || null
40291               };
40292               this.dialCodeMapping[c[2]] = {
40293                   name: c[0],
40294                   iso2: c[1],
40295                   priority: c[3] || 0,
40296                   areaCodes: c[4] || null
40297               };
40298             }
40299             
40300             var cfg = {
40301                 cls: 'form-group',
40302                 cn: []
40303             };
40304             
40305             var input =  {
40306                 tag: 'input',
40307                 id : id,
40308                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40309                 maxlength: this.max_length,
40310                 cls : 'form-control tel-input',
40311                 autocomplete: 'new-password'
40312             };
40313             
40314             var hiddenInput = {
40315                 tag: 'input',
40316                 type: 'hidden',
40317                 cls: 'hidden-tel-input'
40318             };
40319             
40320             if (this.name) {
40321                 hiddenInput.name = this.name;
40322             }
40323             
40324             if (this.disabled) {
40325                 input.disabled = true;
40326             }
40327             
40328             var flag_container = {
40329                 tag: 'div',
40330                 cls: 'flag-box',
40331                 cn: [
40332                     {
40333                         tag: 'div',
40334                         cls: 'flag'
40335                     },
40336                     {
40337                         tag: 'div',
40338                         cls: 'caret'
40339                     }
40340                 ]
40341             };
40342             
40343             var box = {
40344                 tag: 'div',
40345                 cls: this.hasFeedback ? 'has-feedback' : '',
40346                 cn: [
40347                     hiddenInput,
40348                     input,
40349                     {
40350                         tag: 'input',
40351                         cls: 'dial-code-holder',
40352                         disabled: true
40353                     }
40354                 ]
40355             };
40356             
40357             var container = {
40358                 cls: 'roo-select2-container input-group',
40359                 cn: [
40360                     flag_container,
40361                     box
40362                 ]
40363             };
40364             
40365             if (this.fieldLabel.length) {
40366                 var indicator = {
40367                     tag: 'i',
40368                     tooltip: 'This field is required'
40369                 };
40370                 
40371                 var label = {
40372                     tag: 'label',
40373                     'for':  id,
40374                     cls: 'control-label',
40375                     cn: []
40376                 };
40377                 
40378                 var label_text = {
40379                     tag: 'span',
40380                     html: this.fieldLabel
40381                 };
40382                 
40383                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40384                 label.cn = [
40385                     indicator,
40386                     label_text
40387                 ];
40388                 
40389                 if(this.indicatorpos == 'right') {
40390                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40391                     label.cn = [
40392                         label_text,
40393                         indicator
40394                     ];
40395                 }
40396                 
40397                 if(align == 'left') {
40398                     container = {
40399                         tag: 'div',
40400                         cn: [
40401                             container
40402                         ]
40403                     };
40404                     
40405                     if(this.labelWidth > 12){
40406                         label.style = "width: " + this.labelWidth + 'px';
40407                     }
40408                     if(this.labelWidth < 13 && this.labelmd == 0){
40409                         this.labelmd = this.labelWidth;
40410                     }
40411                     if(this.labellg > 0){
40412                         label.cls += ' col-lg-' + this.labellg;
40413                         input.cls += ' col-lg-' + (12 - this.labellg);
40414                     }
40415                     if(this.labelmd > 0){
40416                         label.cls += ' col-md-' + this.labelmd;
40417                         container.cls += ' col-md-' + (12 - this.labelmd);
40418                     }
40419                     if(this.labelsm > 0){
40420                         label.cls += ' col-sm-' + this.labelsm;
40421                         container.cls += ' col-sm-' + (12 - this.labelsm);
40422                     }
40423                     if(this.labelxs > 0){
40424                         label.cls += ' col-xs-' + this.labelxs;
40425                         container.cls += ' col-xs-' + (12 - this.labelxs);
40426                     }
40427                 }
40428             }
40429             
40430             cfg.cn = [
40431                 label,
40432                 container
40433             ];
40434             
40435             var settings = this;
40436             
40437             ['xs','sm','md','lg'].map(function(size){
40438                 if (settings[size]) {
40439                     cfg.cls += ' col-' + size + '-' + settings[size];
40440                 }
40441             });
40442             
40443             this.store = new Roo.data.Store({
40444                 proxy : new Roo.data.MemoryProxy({}),
40445                 reader : new Roo.data.JsonReader({
40446                     fields : [
40447                         {
40448                             'name' : 'name',
40449                             'type' : 'string'
40450                         },
40451                         {
40452                             'name' : 'iso2',
40453                             'type' : 'string'
40454                         },
40455                         {
40456                             'name' : 'dialCode',
40457                             'type' : 'string'
40458                         },
40459                         {
40460                             'name' : 'priority',
40461                             'type' : 'string'
40462                         },
40463                         {
40464                             'name' : 'areaCodes',
40465                             'type' : 'string'
40466                         }
40467                     ]
40468                 })
40469             });
40470             
40471             if(!this.preferedCountries) {
40472                 this.preferedCountries = [
40473                     'hk',
40474                     'gb',
40475                     'us'
40476                 ];
40477             }
40478             
40479             var p = this.preferedCountries.reverse();
40480             
40481             if(p) {
40482                 for (var i = 0; i < p.length; i++) {
40483                     for (var j = 0; j < this.allCountries.length; j++) {
40484                         if(this.allCountries[j].iso2 == p[i]) {
40485                             var t = this.allCountries[j];
40486                             this.allCountries.splice(j,1);
40487                             this.allCountries.unshift(t);
40488                         }
40489                     } 
40490                 }
40491             }
40492             
40493             this.store.proxy.data = {
40494                 success: true,
40495                 data: this.allCountries
40496             };
40497             
40498             return cfg;
40499         },
40500         
40501         initEvents : function()
40502         {
40503             this.createList();
40504             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40505             
40506             this.indicator = this.indicatorEl();
40507             this.flag = this.flagEl();
40508             this.dialCodeHolder = this.dialCodeHolderEl();
40509             
40510             this.trigger = this.el.select('div.flag-box',true).first();
40511             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40512             
40513             var _this = this;
40514             
40515             (function(){
40516                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40517                 _this.list.setWidth(lw);
40518             }).defer(100);
40519             
40520             this.list.on('mouseover', this.onViewOver, this);
40521             this.list.on('mousemove', this.onViewMove, this);
40522             this.inputEl().on("keyup", this.onKeyUp, this);
40523             this.inputEl().on("keypress", this.onKeyPress, this);
40524             
40525             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40526
40527             this.view = new Roo.View(this.list, this.tpl, {
40528                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40529             });
40530             
40531             this.view.on('click', this.onViewClick, this);
40532             this.setValue(this.defaultDialCode);
40533         },
40534         
40535         onTriggerClick : function(e)
40536         {
40537             Roo.log('trigger click');
40538             if(this.disabled){
40539                 return;
40540             }
40541             
40542             if(this.isExpanded()){
40543                 this.collapse();
40544                 this.hasFocus = false;
40545             }else {
40546                 this.store.load({});
40547                 this.hasFocus = true;
40548                 this.expand();
40549             }
40550         },
40551         
40552         isExpanded : function()
40553         {
40554             return this.list.isVisible();
40555         },
40556         
40557         collapse : function()
40558         {
40559             if(!this.isExpanded()){
40560                 return;
40561             }
40562             this.list.hide();
40563             Roo.get(document).un('mousedown', this.collapseIf, this);
40564             Roo.get(document).un('mousewheel', this.collapseIf, this);
40565             this.fireEvent('collapse', this);
40566             this.validate();
40567         },
40568         
40569         expand : function()
40570         {
40571             Roo.log('expand');
40572
40573             if(this.isExpanded() || !this.hasFocus){
40574                 return;
40575             }
40576             
40577             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40578             this.list.setWidth(lw);
40579             
40580             this.list.show();
40581             this.restrictHeight();
40582             
40583             Roo.get(document).on('mousedown', this.collapseIf, this);
40584             Roo.get(document).on('mousewheel', this.collapseIf, this);
40585             
40586             this.fireEvent('expand', this);
40587         },
40588         
40589         restrictHeight : function()
40590         {
40591             this.list.alignTo(this.inputEl(), this.listAlign);
40592             this.list.alignTo(this.inputEl(), this.listAlign);
40593         },
40594         
40595         onViewOver : function(e, t)
40596         {
40597             if(this.inKeyMode){
40598                 return;
40599             }
40600             var item = this.view.findItemFromChild(t);
40601             
40602             if(item){
40603                 var index = this.view.indexOf(item);
40604                 this.select(index, false);
40605             }
40606         },
40607
40608         // private
40609         onViewClick : function(view, doFocus, el, e)
40610         {
40611             var index = this.view.getSelectedIndexes()[0];
40612             
40613             var r = this.store.getAt(index);
40614             
40615             if(r){
40616                 this.onSelect(r, index);
40617             }
40618             if(doFocus !== false && !this.blockFocus){
40619                 this.inputEl().focus();
40620             }
40621         },
40622         
40623         onViewMove : function(e, t)
40624         {
40625             this.inKeyMode = false;
40626         },
40627         
40628         select : function(index, scrollIntoView)
40629         {
40630             this.selectedIndex = index;
40631             this.view.select(index);
40632             if(scrollIntoView !== false){
40633                 var el = this.view.getNode(index);
40634                 if(el){
40635                     this.list.scrollChildIntoView(el, false);
40636                 }
40637             }
40638         },
40639         
40640         createList : function()
40641         {
40642             this.list = Roo.get(document.body).createChild({
40643                 tag: 'ul',
40644                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40645                 style: 'display:none'
40646             });
40647             
40648             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40649         },
40650         
40651         collapseIf : function(e)
40652         {
40653             var in_combo  = e.within(this.el);
40654             var in_list =  e.within(this.list);
40655             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40656             
40657             if (in_combo || in_list || is_list) {
40658                 return;
40659             }
40660             this.collapse();
40661         },
40662         
40663         onSelect : function(record, index)
40664         {
40665             if(this.fireEvent('beforeselect', this, record, index) !== false){
40666                 
40667                 this.setFlagClass(record.data.iso2);
40668                 this.setDialCode(record.data.dialCode);
40669                 this.hasFocus = false;
40670                 this.collapse();
40671                 this.fireEvent('select', this, record, index);
40672             }
40673         },
40674         
40675         flagEl : function()
40676         {
40677             var flag = this.el.select('div.flag',true).first();
40678             if(!flag){
40679                 return false;
40680             }
40681             return flag;
40682         },
40683         
40684         dialCodeHolderEl : function()
40685         {
40686             var d = this.el.select('input.dial-code-holder',true).first();
40687             if(!d){
40688                 return false;
40689             }
40690             return d;
40691         },
40692         
40693         setDialCode : function(v)
40694         {
40695             this.dialCodeHolder.dom.value = '+'+v;
40696         },
40697         
40698         setFlagClass : function(n)
40699         {
40700             this.flag.dom.className = 'flag '+n;
40701         },
40702         
40703         getValue : function()
40704         {
40705             var v = this.inputEl().getValue();
40706             if(this.dialCodeHolder) {
40707                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40708             }
40709             return v;
40710         },
40711         
40712         setValue : function(v)
40713         {
40714             var d = this.getDialCode(v);
40715             
40716             //invalid dial code
40717             if(v.length == 0 || !d || d.length == 0) {
40718                 if(this.rendered){
40719                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40720                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40721                 }
40722                 return;
40723             }
40724             
40725             //valid dial code
40726             this.setFlagClass(this.dialCodeMapping[d].iso2);
40727             this.setDialCode(d);
40728             this.inputEl().dom.value = v.replace('+'+d,'');
40729             this.hiddenEl().dom.value = this.getValue();
40730             
40731             this.validate();
40732         },
40733         
40734         getDialCode : function(v)
40735         {
40736             v = v ||  '';
40737             
40738             if (v.length == 0) {
40739                 return this.dialCodeHolder.dom.value;
40740             }
40741             
40742             var dialCode = "";
40743             if (v.charAt(0) != "+") {
40744                 return false;
40745             }
40746             var numericChars = "";
40747             for (var i = 1; i < v.length; i++) {
40748               var c = v.charAt(i);
40749               if (!isNaN(c)) {
40750                 numericChars += c;
40751                 if (this.dialCodeMapping[numericChars]) {
40752                   dialCode = v.substr(1, i);
40753                 }
40754                 if (numericChars.length == 4) {
40755                   break;
40756                 }
40757               }
40758             }
40759             return dialCode;
40760         },
40761         
40762         reset : function()
40763         {
40764             this.setValue(this.defaultDialCode);
40765             this.validate();
40766         },
40767         
40768         hiddenEl : function()
40769         {
40770             return this.el.select('input.hidden-tel-input',true).first();
40771         },
40772         
40773         // after setting val
40774         onKeyUp : function(e){
40775             this.setValue(this.getValue());
40776         },
40777         
40778         onKeyPress : function(e){
40779             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40780                 e.stopEvent();
40781             }
40782         }
40783         
40784 });
40785 /**
40786  * @class Roo.bootstrap.MoneyField
40787  * @extends Roo.bootstrap.ComboBox
40788  * Bootstrap MoneyField class
40789  * 
40790  * @constructor
40791  * Create a new MoneyField.
40792  * @param {Object} config Configuration options
40793  */
40794
40795 Roo.bootstrap.MoneyField = function(config) {
40796     
40797     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40798     
40799 };
40800
40801 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40802     
40803     /**
40804      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40805      */
40806     allowDecimals : true,
40807     /**
40808      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40809      */
40810     decimalSeparator : ".",
40811     /**
40812      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40813      */
40814     decimalPrecision : 0,
40815     /**
40816      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40817      */
40818     allowNegative : true,
40819     /**
40820      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40821      */
40822     allowZero: true,
40823     /**
40824      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40825      */
40826     minValue : Number.NEGATIVE_INFINITY,
40827     /**
40828      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40829      */
40830     maxValue : Number.MAX_VALUE,
40831     /**
40832      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40833      */
40834     minText : "The minimum value for this field is {0}",
40835     /**
40836      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40837      */
40838     maxText : "The maximum value for this field is {0}",
40839     /**
40840      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40841      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40842      */
40843     nanText : "{0} is not a valid number",
40844     /**
40845      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40846      */
40847     castInt : true,
40848     /**
40849      * @cfg {String} defaults currency of the MoneyField
40850      * value should be in lkey
40851      */
40852     defaultCurrency : false,
40853     /**
40854      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40855      */
40856     thousandsDelimiter : false,
40857     /**
40858      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40859      */
40860     max_length: false,
40861     
40862     inputlg : 9,
40863     inputmd : 9,
40864     inputsm : 9,
40865     inputxs : 6,
40866     
40867     store : false,
40868     
40869     getAutoCreate : function()
40870     {
40871         var align = this.labelAlign || this.parentLabelAlign();
40872         
40873         var id = Roo.id();
40874
40875         var cfg = {
40876             cls: 'form-group',
40877             cn: []
40878         };
40879
40880         var input =  {
40881             tag: 'input',
40882             id : id,
40883             cls : 'form-control roo-money-amount-input',
40884             autocomplete: 'new-password'
40885         };
40886         
40887         var hiddenInput = {
40888             tag: 'input',
40889             type: 'hidden',
40890             id: Roo.id(),
40891             cls: 'hidden-number-input'
40892         };
40893         
40894         if(this.max_length) {
40895             input.maxlength = this.max_length; 
40896         }
40897         
40898         if (this.name) {
40899             hiddenInput.name = this.name;
40900         }
40901
40902         if (this.disabled) {
40903             input.disabled = true;
40904         }
40905
40906         var clg = 12 - this.inputlg;
40907         var cmd = 12 - this.inputmd;
40908         var csm = 12 - this.inputsm;
40909         var cxs = 12 - this.inputxs;
40910         
40911         var container = {
40912             tag : 'div',
40913             cls : 'row roo-money-field',
40914             cn : [
40915                 {
40916                     tag : 'div',
40917                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40918                     cn : [
40919                         {
40920                             tag : 'div',
40921                             cls: 'roo-select2-container input-group',
40922                             cn: [
40923                                 {
40924                                     tag : 'input',
40925                                     cls : 'form-control roo-money-currency-input',
40926                                     autocomplete: 'new-password',
40927                                     readOnly : 1,
40928                                     name : this.currencyName
40929                                 },
40930                                 {
40931                                     tag :'span',
40932                                     cls : 'input-group-addon',
40933                                     cn : [
40934                                         {
40935                                             tag: 'span',
40936                                             cls: 'caret'
40937                                         }
40938                                     ]
40939                                 }
40940                             ]
40941                         }
40942                     ]
40943                 },
40944                 {
40945                     tag : 'div',
40946                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40947                     cn : [
40948                         {
40949                             tag: 'div',
40950                             cls: this.hasFeedback ? 'has-feedback' : '',
40951                             cn: [
40952                                 input
40953                             ]
40954                         }
40955                     ]
40956                 }
40957             ]
40958             
40959         };
40960         
40961         if (this.fieldLabel.length) {
40962             var indicator = {
40963                 tag: 'i',
40964                 tooltip: 'This field is required'
40965             };
40966
40967             var label = {
40968                 tag: 'label',
40969                 'for':  id,
40970                 cls: 'control-label',
40971                 cn: []
40972             };
40973
40974             var label_text = {
40975                 tag: 'span',
40976                 html: this.fieldLabel
40977             };
40978
40979             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40980             label.cn = [
40981                 indicator,
40982                 label_text
40983             ];
40984
40985             if(this.indicatorpos == 'right') {
40986                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40987                 label.cn = [
40988                     label_text,
40989                     indicator
40990                 ];
40991             }
40992
40993             if(align == 'left') {
40994                 container = {
40995                     tag: 'div',
40996                     cn: [
40997                         container
40998                     ]
40999                 };
41000
41001                 if(this.labelWidth > 12){
41002                     label.style = "width: " + this.labelWidth + 'px';
41003                 }
41004                 if(this.labelWidth < 13 && this.labelmd == 0){
41005                     this.labelmd = this.labelWidth;
41006                 }
41007                 if(this.labellg > 0){
41008                     label.cls += ' col-lg-' + this.labellg;
41009                     input.cls += ' col-lg-' + (12 - this.labellg);
41010                 }
41011                 if(this.labelmd > 0){
41012                     label.cls += ' col-md-' + this.labelmd;
41013                     container.cls += ' col-md-' + (12 - this.labelmd);
41014                 }
41015                 if(this.labelsm > 0){
41016                     label.cls += ' col-sm-' + this.labelsm;
41017                     container.cls += ' col-sm-' + (12 - this.labelsm);
41018                 }
41019                 if(this.labelxs > 0){
41020                     label.cls += ' col-xs-' + this.labelxs;
41021                     container.cls += ' col-xs-' + (12 - this.labelxs);
41022                 }
41023             }
41024         }
41025
41026         cfg.cn = [
41027             label,
41028             container,
41029             hiddenInput
41030         ];
41031         
41032         var settings = this;
41033
41034         ['xs','sm','md','lg'].map(function(size){
41035             if (settings[size]) {
41036                 cfg.cls += ' col-' + size + '-' + settings[size];
41037             }
41038         });
41039         
41040         return cfg;
41041     },
41042     
41043     initEvents : function()
41044     {
41045         this.indicator = this.indicatorEl();
41046         
41047         this.initCurrencyEvent();
41048         
41049         this.initNumberEvent();
41050     },
41051     
41052     initCurrencyEvent : function()
41053     {
41054         if (!this.store) {
41055             throw "can not find store for combo";
41056         }
41057         
41058         this.store = Roo.factory(this.store, Roo.data);
41059         this.store.parent = this;
41060         
41061         this.createList();
41062         
41063         this.triggerEl = this.el.select('.input-group-addon', true).first();
41064         
41065         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41066         
41067         var _this = this;
41068         
41069         (function(){
41070             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41071             _this.list.setWidth(lw);
41072         }).defer(100);
41073         
41074         this.list.on('mouseover', this.onViewOver, this);
41075         this.list.on('mousemove', this.onViewMove, this);
41076         this.list.on('scroll', this.onViewScroll, this);
41077         
41078         if(!this.tpl){
41079             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41080         }
41081         
41082         this.view = new Roo.View(this.list, this.tpl, {
41083             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41084         });
41085         
41086         this.view.on('click', this.onViewClick, this);
41087         
41088         this.store.on('beforeload', this.onBeforeLoad, this);
41089         this.store.on('load', this.onLoad, this);
41090         this.store.on('loadexception', this.onLoadException, this);
41091         
41092         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41093             "up" : function(e){
41094                 this.inKeyMode = true;
41095                 this.selectPrev();
41096             },
41097
41098             "down" : function(e){
41099                 if(!this.isExpanded()){
41100                     this.onTriggerClick();
41101                 }else{
41102                     this.inKeyMode = true;
41103                     this.selectNext();
41104                 }
41105             },
41106
41107             "enter" : function(e){
41108                 this.collapse();
41109                 
41110                 if(this.fireEvent("specialkey", this, e)){
41111                     this.onViewClick(false);
41112                 }
41113                 
41114                 return true;
41115             },
41116
41117             "esc" : function(e){
41118                 this.collapse();
41119             },
41120
41121             "tab" : function(e){
41122                 this.collapse();
41123                 
41124                 if(this.fireEvent("specialkey", this, e)){
41125                     this.onViewClick(false);
41126                 }
41127                 
41128                 return true;
41129             },
41130
41131             scope : this,
41132
41133             doRelay : function(foo, bar, hname){
41134                 if(hname == 'down' || this.scope.isExpanded()){
41135                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41136                 }
41137                 return true;
41138             },
41139
41140             forceKeyDown: true
41141         });
41142         
41143         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41144         
41145     },
41146     
41147     initNumberEvent : function(e)
41148     {
41149         this.inputEl().on("keydown" , this.fireKey,  this);
41150         this.inputEl().on("focus", this.onFocus,  this);
41151         this.inputEl().on("blur", this.onBlur,  this);
41152         
41153         this.inputEl().relayEvent('keyup', this);
41154         
41155         if(this.indicator){
41156             this.indicator.addClass('invisible');
41157         }
41158  
41159         this.originalValue = this.getValue();
41160         
41161         if(this.validationEvent == 'keyup'){
41162             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41163             this.inputEl().on('keyup', this.filterValidation, this);
41164         }
41165         else if(this.validationEvent !== false){
41166             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41167         }
41168         
41169         if(this.selectOnFocus){
41170             this.on("focus", this.preFocus, this);
41171             
41172         }
41173         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41174             this.inputEl().on("keypress", this.filterKeys, this);
41175         } else {
41176             this.inputEl().relayEvent('keypress', this);
41177         }
41178         
41179         var allowed = "0123456789";
41180         
41181         if(this.allowDecimals){
41182             allowed += this.decimalSeparator;
41183         }
41184         
41185         if(this.allowNegative){
41186             allowed += "-";
41187         }
41188         
41189         if(this.thousandsDelimiter) {
41190             allowed += ",";
41191         }
41192         
41193         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41194         
41195         var keyPress = function(e){
41196             
41197             var k = e.getKey();
41198             
41199             var c = e.getCharCode();
41200             
41201             if(
41202                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41203                     allowed.indexOf(String.fromCharCode(c)) === -1
41204             ){
41205                 e.stopEvent();
41206                 return;
41207             }
41208             
41209             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41210                 return;
41211             }
41212             
41213             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41214                 e.stopEvent();
41215             }
41216         };
41217         
41218         this.inputEl().on("keypress", keyPress, this);
41219         
41220     },
41221     
41222     onTriggerClick : function(e)
41223     {   
41224         if(this.disabled){
41225             return;
41226         }
41227         
41228         this.page = 0;
41229         this.loadNext = false;
41230         
41231         if(this.isExpanded()){
41232             this.collapse();
41233             return;
41234         }
41235         
41236         this.hasFocus = true;
41237         
41238         if(this.triggerAction == 'all') {
41239             this.doQuery(this.allQuery, true);
41240             return;
41241         }
41242         
41243         this.doQuery(this.getRawValue());
41244     },
41245     
41246     getCurrency : function()
41247     {   
41248         var v = this.currencyEl().getValue();
41249         
41250         return v;
41251     },
41252     
41253     restrictHeight : function()
41254     {
41255         this.list.alignTo(this.currencyEl(), this.listAlign);
41256         this.list.alignTo(this.currencyEl(), this.listAlign);
41257     },
41258     
41259     onViewClick : function(view, doFocus, el, e)
41260     {
41261         var index = this.view.getSelectedIndexes()[0];
41262         
41263         var r = this.store.getAt(index);
41264         
41265         if(r){
41266             this.onSelect(r, index);
41267         }
41268     },
41269     
41270     onSelect : function(record, index){
41271         
41272         if(this.fireEvent('beforeselect', this, record, index) !== false){
41273         
41274             this.setFromCurrencyData(index > -1 ? record.data : false);
41275             
41276             this.collapse();
41277             
41278             this.fireEvent('select', this, record, index);
41279         }
41280     },
41281     
41282     setFromCurrencyData : function(o)
41283     {
41284         var currency = '';
41285         
41286         this.lastCurrency = o;
41287         
41288         if (this.currencyField) {
41289             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41290         } else {
41291             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41292         }
41293         
41294         this.lastSelectionText = currency;
41295         
41296         //setting default currency
41297         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41298             this.setCurrency(this.defaultCurrency);
41299             return;
41300         }
41301         
41302         this.setCurrency(currency);
41303     },
41304     
41305     setFromData : function(o)
41306     {
41307         var c = {};
41308         
41309         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41310         
41311         this.setFromCurrencyData(c);
41312         
41313         var value = '';
41314         
41315         if (this.name) {
41316             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41317         } else {
41318             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41319         }
41320         
41321         this.setValue(value);
41322         
41323     },
41324     
41325     setCurrency : function(v)
41326     {   
41327         this.currencyValue = v;
41328         
41329         if(this.rendered){
41330             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41331             this.validate();
41332         }
41333     },
41334     
41335     setValue : function(v)
41336     {
41337         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41338         
41339         this.value = v;
41340         
41341         if(this.rendered){
41342             
41343             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41344             
41345             this.inputEl().dom.value = (v == '') ? '' :
41346                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41347             
41348             if(!this.allowZero && v === '0') {
41349                 this.hiddenEl().dom.value = '';
41350                 this.inputEl().dom.value = '';
41351             }
41352             
41353             this.validate();
41354         }
41355     },
41356     
41357     getRawValue : function()
41358     {
41359         var v = this.inputEl().getValue();
41360         
41361         return v;
41362     },
41363     
41364     getValue : function()
41365     {
41366         return this.fixPrecision(this.parseValue(this.getRawValue()));
41367     },
41368     
41369     parseValue : function(value)
41370     {
41371         if(this.thousandsDelimiter) {
41372             value += "";
41373             r = new RegExp(",", "g");
41374             value = value.replace(r, "");
41375         }
41376         
41377         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41378         return isNaN(value) ? '' : value;
41379         
41380     },
41381     
41382     fixPrecision : function(value)
41383     {
41384         if(this.thousandsDelimiter) {
41385             value += "";
41386             r = new RegExp(",", "g");
41387             value = value.replace(r, "");
41388         }
41389         
41390         var nan = isNaN(value);
41391         
41392         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41393             return nan ? '' : value;
41394         }
41395         return parseFloat(value).toFixed(this.decimalPrecision);
41396     },
41397     
41398     decimalPrecisionFcn : function(v)
41399     {
41400         return Math.floor(v);
41401     },
41402     
41403     validateValue : function(value)
41404     {
41405         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41406             return false;
41407         }
41408         
41409         var num = this.parseValue(value);
41410         
41411         if(isNaN(num)){
41412             this.markInvalid(String.format(this.nanText, value));
41413             return false;
41414         }
41415         
41416         if(num < this.minValue){
41417             this.markInvalid(String.format(this.minText, this.minValue));
41418             return false;
41419         }
41420         
41421         if(num > this.maxValue){
41422             this.markInvalid(String.format(this.maxText, this.maxValue));
41423             return false;
41424         }
41425         
41426         return true;
41427     },
41428     
41429     validate : function()
41430     {
41431         if(this.disabled || this.allowBlank){
41432             this.markValid();
41433             return true;
41434         }
41435         
41436         var currency = this.getCurrency();
41437         
41438         if(this.validateValue(this.getRawValue()) && currency.length){
41439             this.markValid();
41440             return true;
41441         }
41442         
41443         this.markInvalid();
41444         return false;
41445     },
41446     
41447     getName: function()
41448     {
41449         return this.name;
41450     },
41451     
41452     beforeBlur : function()
41453     {
41454         if(!this.castInt){
41455             return;
41456         }
41457         
41458         var v = this.parseValue(this.getRawValue());
41459         
41460         if(v || v == 0){
41461             this.setValue(v);
41462         }
41463     },
41464     
41465     onBlur : function()
41466     {
41467         this.beforeBlur();
41468         
41469         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41470             //this.el.removeClass(this.focusClass);
41471         }
41472         
41473         this.hasFocus = false;
41474         
41475         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41476             this.validate();
41477         }
41478         
41479         var v = this.getValue();
41480         
41481         if(String(v) !== String(this.startValue)){
41482             this.fireEvent('change', this, v, this.startValue);
41483         }
41484         
41485         this.fireEvent("blur", this);
41486     },
41487     
41488     inputEl : function()
41489     {
41490         return this.el.select('.roo-money-amount-input', true).first();
41491     },
41492     
41493     currencyEl : function()
41494     {
41495         return this.el.select('.roo-money-currency-input', true).first();
41496     },
41497     
41498     hiddenEl : function()
41499     {
41500         return this.el.select('input.hidden-number-input',true).first();
41501     }
41502     
41503 });/**
41504 *    This script refer to:
41505 *    Title: Signature Pad
41506 *    Author: szimek
41507 *    Availability: https://github.com/szimek/signature_pad
41508 **/
41509
41510 /**
41511  * @class Roo.bootstrap.BezierSignature
41512  * @extends Roo.bootstrap.Component
41513  * Bootstrap BezierSignature class
41514  * 
41515  * @constructor
41516  * Create a new BezierSignature
41517  * @param {Object} config The config object
41518  */
41519
41520 Roo.bootstrap.BezierSignature = function(config){
41521     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
41522     this.addEvents({
41523         "resize" : true
41524     });
41525 };
41526
41527 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,  {
41528     
41529     curve_data: [],
41530     
41531     is_empty: true,
41532     
41533     mouse_btn_down: true,
41534     
41535     /**
41536      * @cfg(int) canvas height
41537      */
41538     canvas_height: '200px',
41539     
41540     /**
41541      * @cfg(float or function) Radius of a single dot.
41542      */ 
41543     dot_size: false,
41544     
41545     /**
41546      * @cfg(float) Minimum width of a line. Defaults to 0.5.
41547      */
41548     min_width: 0.5,
41549     
41550     /**
41551      * @cfg(float) Maximum width of a line. Defaults to 2.5.
41552      */
41553     max_width: 2.5,
41554     
41555     /**
41556      * @cfg(integer) Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
41557      */
41558     throttle: 16,
41559     
41560     /**
41561      * @cfg(integer) Add the next point only if the previous one is farther than x pixels. Defaults to 5.
41562      */
41563     min_distance: 5,
41564     
41565     /**
41566      * @cfg(string) Color used to clear the background. Can be any color format accepted by context.fillStyle. Defaults to "rgba(0,0,0,0)" (transparent black). Use a non-transparent color e.g. "rgb(255,255,255)" (opaque white) if you'd like to save signatures as JPEG images.
41567      */
41568     bg_color: 'rgba(0, 0, 0, 0)',
41569     
41570     /**
41571      * @cfg(string) Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
41572      */
41573     dot_color: 'black',
41574     
41575     /**
41576      * @cfg(float) Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
41577      */
41578     velocity_filter_weight: 0.7,
41579     
41580     /**
41581      * @cfg(function) Callback when stroke begin.
41582      */
41583     onBegin: false,
41584     
41585     /**
41586      * @cfg(function) Callback when stroke end.
41587      */
41588     onEnd: false,
41589     
41590     getAutoCreate : function()
41591     {
41592         var cls = 'roo-signature column';
41593         
41594         if(this.cls){
41595             cls += ' ' + this.cls;
41596         }
41597         
41598         var col_sizes = [
41599             'lg',
41600             'md',
41601             'sm',
41602             'xs'
41603         ];
41604         
41605         for(var i = 0; i < col_sizes.length; i++) {
41606             if(this[col_sizes[i]]) {
41607                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
41608             }
41609         }
41610         
41611         var cfg = {
41612             tag: 'div',
41613             cls: cls,
41614             cn: [
41615                 {
41616                     tag: 'div',
41617                     cls: 'roo-signature-body',
41618                     cn: [
41619                         {
41620                             tag: 'canvas',
41621                             cls: 'roo-signature-body-canvas',
41622                             height: this.canvas_height,
41623                             width: this.canvas_width
41624                         }
41625                     ]
41626                 }
41627             ]
41628         };
41629         
41630         return cfg;
41631     },
41632     
41633     initEvents: function() 
41634     {
41635         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
41636         
41637         var canvas = this.canvasEl();
41638         
41639         // mouse && touch event swapping...
41640         canvas.dom.style.touchAction = 'none';
41641         canvas.dom.style.msTouchAction = 'none';
41642         
41643         this.mouse_btn_down = false;
41644         canvas.on('mousedown', this._handleMouseDown, this);
41645         canvas.on('mousemove', this._handleMouseMove, this);
41646         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
41647         
41648         if (window.PointerEvent) {
41649             canvas.on('pointerdown', this._handleMouseDown, this);
41650             canvas.on('pointermove', this._handleMouseMove, this);
41651             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
41652         }
41653         
41654         if ('ontouchstart' in window) {
41655             canvas.on('touchstart', this._handleTouchStart, this);
41656             canvas.on('touchmove', this._handleTouchMove, this);
41657             canvas.on('touchend', this._handleTouchEnd, this);
41658         }
41659         
41660         Roo.EventManager.onWindowResize(this.resize, this, true);
41661         
41662         this.clear();
41663         
41664         this.resize();
41665     },
41666     
41667     resize: function(){
41668         
41669         var canvas = this.canvasEl().dom;
41670         var ctx = this.canvasElCtx();
41671         var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
41672         
41673         // setting canvas width will clean img data
41674         canvas.width = 0;
41675         
41676         var style = window.getComputedStyle ? 
41677             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
41678             
41679         var padding_left = parseInt(style.paddingLeft) || 0;
41680         var padding_right = parseInt(style.paddingRight) || 0;
41681         
41682         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
41683         
41684         ctx.putImageData(img_data, 0, 0);
41685     },
41686     
41687     _handleMouseDown: function(e)
41688     {
41689         if (e.browserEvent.which === 1) {
41690             this.mouse_btn_down = true;
41691             this.strokeBegin(e);
41692         }
41693     },
41694     
41695     _handleMouseMove: function (e)
41696     {
41697         if (this.mouse_btn_down) {
41698             this.strokeMoveUpdate(e);
41699         }
41700     },
41701     
41702     _handleMouseUp: function (e)
41703     {
41704         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
41705             this.mouse_btn_down = false;
41706             this.strokeEnd(e);
41707         }
41708     },
41709     
41710     _handleTouchStart: function (e) {
41711         
41712         e.preventDefault();
41713         if (e.browserEvent.targetTouches.length === 1) {
41714             // var touch = e.browserEvent.changedTouches[0];
41715             // this.strokeBegin(touch);
41716             
41717              this.strokeBegin(e); // assume e catching the correct xy...
41718         }
41719     },
41720     
41721     _handleTouchMove: function (e) {
41722         e.preventDefault();
41723         // var touch = event.targetTouches[0];
41724         // _this._strokeMoveUpdate(touch);
41725         this.strokeMoveUpdate(e);
41726     },
41727     
41728     _handleTouchEnd: function (e) {
41729         var wasCanvasTouched = e.target === this.canvasEl().dom;
41730         if (wasCanvasTouched) {
41731             e.preventDefault();
41732             // var touch = event.changedTouches[0];
41733             // _this._strokeEnd(touch);
41734             this.strokeEnd(e);
41735         }
41736     },
41737     
41738     reset: function () {
41739         this._lastPoints = [];
41740         this._lastVelocity = 0;
41741         this._lastWidth = (this.min_width + this.max_width) / 2;
41742         this.canvasElCtx().fillStyle = this.dot_color;
41743     },
41744     
41745     strokeMoveUpdate: function(e)
41746     {
41747         this.strokeUpdate(e);
41748         
41749         if (this.throttle) {
41750             this.throttle(this.strokeUpdate, this.throttle);
41751         }
41752         else {
41753             this.strokeUpdate(e);
41754         }
41755     },
41756     
41757     strokeBegin: function(e)
41758     {
41759         var newPointGroup = {
41760             color: this.dot_color,
41761             points: []
41762         };
41763         
41764         if (typeof this.onBegin === 'function') {
41765             this.onBegin(e);
41766         }
41767         
41768         this.curve_data.push(newPointGroup);
41769         this.reset();
41770         this.strokeUpdate(e);
41771     },
41772     
41773     strokeUpdate: function(e)
41774     {
41775         var rect = this.canvasEl().dom.getBoundingClientRect();
41776         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
41777         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
41778         var lastPoints = lastPointGroup.points;
41779         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
41780         var isLastPointTooClose = lastPoint
41781             ? point.distanceTo(lastPoint) <= this.min_distance
41782             : false;
41783         var color = lastPointGroup.color;
41784         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
41785             var curve = this.addPoint(point);
41786             if (!lastPoint) {
41787                 this.drawDot({color: color, point: point});
41788             }
41789             else if (curve) {
41790                 this.drawCurve({color: color, curve: curve});
41791             }
41792             lastPoints.push({
41793                 time: point.time,
41794                 x: point.x,
41795                 y: point.y
41796             });
41797         }
41798     },
41799     
41800     strokeEnd: function(e)
41801     {
41802         this.strokeUpdate(e);
41803         if (typeof this.onEnd === 'function') {
41804             this.onEnd(e);
41805         }
41806     },
41807     
41808     addPoint:  function (point) {
41809         var _lastPoints = this._lastPoints;
41810         _lastPoints.push(point);
41811         if (_lastPoints.length > 2) {
41812             if (_lastPoints.length === 3) {
41813                 _lastPoints.unshift(_lastPoints[0]);
41814             }
41815             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
41816             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
41817             _lastPoints.shift();
41818             return curve;
41819         }
41820         return null;
41821     },
41822     
41823     calculateCurveWidths: function (startPoint, endPoint) {
41824         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
41825             (1 - this.velocity_filter_weight) * this._lastVelocity;
41826
41827         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
41828         var widths = {
41829             end: newWidth,
41830             start: this._lastWidth
41831         };
41832         
41833         this._lastVelocity = velocity;
41834         this._lastWidth = newWidth;
41835         return widths;
41836     },
41837     
41838     drawDot: function (_a) {
41839         var color = _a.color, point = _a.point;
41840         var ctx = this.canvasElCtx();
41841         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
41842         ctx.beginPath();
41843         this.drawCurveSegment(point.x, point.y, width);
41844         ctx.closePath();
41845         ctx.fillStyle = color;
41846         ctx.fill();
41847     },
41848     
41849     drawCurve: function (_a) {
41850         var color = _a.color, curve = _a.curve;
41851         var ctx = this.canvasElCtx();
41852         var widthDelta = curve.endWidth - curve.startWidth;
41853         var drawSteps = Math.floor(curve.length()) * 2;
41854         ctx.beginPath();
41855         ctx.fillStyle = color;
41856         for (var i = 0; i < drawSteps; i += 1) {
41857         var t = i / drawSteps;
41858         var tt = t * t;
41859         var ttt = tt * t;
41860         var u = 1 - t;
41861         var uu = u * u;
41862         var uuu = uu * u;
41863         var x = uuu * curve.startPoint.x;
41864         x += 3 * uu * t * curve.control1.x;
41865         x += 3 * u * tt * curve.control2.x;
41866         x += ttt * curve.endPoint.x;
41867         var y = uuu * curve.startPoint.y;
41868         y += 3 * uu * t * curve.control1.y;
41869         y += 3 * u * tt * curve.control2.y;
41870         y += ttt * curve.endPoint.y;
41871         var width = curve.startWidth + ttt * widthDelta;
41872         this.drawCurveSegment(x, y, width);
41873         }
41874         ctx.closePath();
41875         ctx.fill();
41876     },
41877     
41878     drawCurveSegment: function (x, y, width) {
41879         var ctx = this.canvasElCtx();
41880         ctx.moveTo(x, y);
41881         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
41882         this.is_empty = false;
41883     },
41884     
41885     clear: function()
41886     {
41887         var ctx = this.canvasElCtx();
41888         var canvas = this.canvasEl().dom;
41889         ctx.fillStyle = this.bg_color;
41890         ctx.clearRect(0, 0, canvas.width, canvas.height);
41891         ctx.fillRect(0, 0, canvas.width, canvas.height);
41892         this.curve_data = [];
41893         this.reset();
41894         this.is_empty = true;
41895     },
41896     
41897     canvasEl: function()
41898     {
41899         return this.el.select('canvas',true).first();
41900     },
41901     
41902     canvasElCtx: function()
41903     {
41904         return this.el.select('canvas',true).first().dom.getContext('2d');
41905     },
41906     
41907     getImage: function(type)
41908     {
41909         if(this.is_empty) {
41910             return false;
41911         }
41912         
41913         // encryption ?
41914         return this.canvasEl().dom.toDataURL('image/'+type, 1);
41915     },
41916     
41917     drawFromImage: function(img_src)
41918     {
41919         var img = new Image();
41920         
41921         img.onload = function(){
41922             this.canvasElCtx().drawImage(img, 0, 0);
41923         }.bind(this);
41924         
41925         img.src = img_src;
41926         
41927         this.is_empty = false;
41928     },
41929     
41930     // Bezier Point Constructor
41931     Point: (function () {
41932         function Point(x, y, time) {
41933             this.x = x;
41934             this.y = y;
41935             this.time = time || Date.now();
41936         }
41937         Point.prototype.distanceTo = function (start) {
41938             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
41939         };
41940         Point.prototype.equals = function (other) {
41941             return this.x === other.x && this.y === other.y && this.time === other.time;
41942         };
41943         Point.prototype.velocityFrom = function (start) {
41944             return this.time !== start.time
41945             ? this.distanceTo(start) / (this.time - start.time)
41946             : 0;
41947         };
41948         return Point;
41949     }()),
41950     
41951     
41952     // Bezier Constructor
41953     Bezier: (function () {
41954         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
41955             this.startPoint = startPoint;
41956             this.control2 = control2;
41957             this.control1 = control1;
41958             this.endPoint = endPoint;
41959             this.startWidth = startWidth;
41960             this.endWidth = endWidth;
41961         }
41962         Bezier.fromPoints = function (points, widths, scope) {
41963             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
41964             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
41965             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
41966         };
41967         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
41968             var dx1 = s1.x - s2.x;
41969             var dy1 = s1.y - s2.y;
41970             var dx2 = s2.x - s3.x;
41971             var dy2 = s2.y - s3.y;
41972             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
41973             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
41974             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
41975             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
41976             var dxm = m1.x - m2.x;
41977             var dym = m1.y - m2.y;
41978             var k = l2 / (l1 + l2);
41979             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
41980             var tx = s2.x - cm.x;
41981             var ty = s2.y - cm.y;
41982             return {
41983                 c1: new scope.Point(m1.x + tx, m1.y + ty),
41984                 c2: new scope.Point(m2.x + tx, m2.y + ty)
41985             };
41986         };
41987         Bezier.prototype.length = function () {
41988             var steps = 10;
41989             var length = 0;
41990             var px;
41991             var py;
41992             for (var i = 0; i <= steps; i += 1) {
41993                 var t = i / steps;
41994                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
41995                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
41996                 if (i > 0) {
41997                     var xdiff = cx - px;
41998                     var ydiff = cy - py;
41999                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42000                 }
42001                 px = cx;
42002                 py = cy;
42003             }
42004             return length;
42005         };
42006         Bezier.prototype.point = function (t, start, c1, c2, end) {
42007             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42008             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42009             + (3.0 * c2 * (1.0 - t) * t * t)
42010             + (end * t * t * t);
42011         };
42012         return Bezier;
42013     }()),
42014     
42015     throttle: function(fn, wait) {
42016       if (wait === void 0) { wait = 250; }
42017       var previous = 0;
42018       var timeout = null;
42019       var result;
42020       var storedContext;
42021       var storedArgs;
42022       var later = function () {
42023           previous = Date.now();
42024           timeout = null;
42025           result = fn.apply(storedContext, storedArgs);
42026           if (!timeout) {
42027               storedContext = null;
42028               storedArgs = [];
42029           }
42030       };
42031       return function wrapper() {
42032           var args = [];
42033           for (var _i = 0; _i < arguments.length; _i++) {
42034               args[_i] = arguments[_i];
42035           }
42036           var now = Date.now();
42037           var remaining = wait - (now - previous);
42038           storedContext = this;
42039           storedArgs = args;
42040           if (remaining <= 0 || remaining > wait) {
42041               if (timeout) {
42042                   clearTimeout(timeout);
42043                   timeout = null;
42044               }
42045               previous = now;
42046               result = fn.apply(storedContext, storedArgs);
42047               if (!timeout) {
42048                   storedContext = null;
42049                   storedArgs = [];
42050               }
42051           }
42052           else if (!timeout) {
42053               timeout = window.setTimeout(later, remaining);
42054           }
42055           return result;
42056       };
42057   }
42058   
42059 });
42060
42061  
42062
42063